diff options
Diffstat (limited to 'src/widgets')
122 files changed, 87382 insertions, 0 deletions
diff --git a/src/widgets/qaction.cpp b/src/widgets/qaction.cpp new file mode 100644 index 000000000..025609b2d --- /dev/null +++ b/src/widgets/qaction.cpp @@ -0,0 +1,2144 @@ +/**************************************************************************** +** +** Implementation of TQAction class +** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQAction qaction.h + \brief The TQAction 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 TQAction 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 TQAction 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 TQPixmap( 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 TQToolBar + \printline + \skipto fileSaveAction->addTo + \printline + \skipto new TQPopupMenu + \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 TQActionPrivate +{ +public: + TQActionPrivate(TQAction *act); + ~TQActionPrivate(); + TQIconSet *iconset; + TQString text; + TQString menutext; + TQString tooltip; + TQString statustip; + TQString whatsthis; +#ifndef QT_NO_ACCEL + TQKeySequence key; + TQAccel* 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 + TQToolTipGroup tipGroup; +#endif + TQActionGroupPrivate* d_group; + TQAction *action; + + struct MenuItem { + MenuItem():popup(0),id(0){} + TQPopupMenu* 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) {} + TQComboBox *combo; + int id; + }; + TQPtrList<MenuItem> menuitems; + TQPtrList<TQToolButton> toolbuttons; + TQPtrList<ComboItem> comboitems; + + enum Update { Icons = 1, Visibility = 2, State = 4, EverythingElse = 8 }; + void update( uint upd = EverythingElse ); + + TQString menuText() const; + TQString toolTip() const; + TQString statusTip() const; +}; + +TQActionPrivate::TQActionPrivate(TQAction *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 +} + +TQActionPrivate::~TQActionPrivate() +{ + TQPtrListIterator<TQToolButton> ittb( toolbuttons ); + TQToolButton *tb; + + while ( ( tb = ittb.current() ) ) { + ++ittb; + delete tb; + } + + TQPtrListIterator<TQActionPrivate::MenuItem> itmi( menuitems); + TQActionPrivate::MenuItem* mi; + while ( ( mi = itmi.current() ) ) { + ++itmi; + TQPopupMenu* menu = mi->popup; + if ( menu->findItem( mi->id ) ) + menu->removeItem( mi->id ); + } + + TQPtrListIterator<TQActionPrivate::ComboItem> itci(comboitems); + TQActionPrivate::ComboItem* ci; + while ( ( ci = itci.current() ) ) { + ++itci; + TQComboBox* combo = ci->combo; + combo->clear(); + TQActionGroup *group = ::qt_cast<TQActionGroup*>(action->parent()); + TQObjectList *siblings = group ? group->queryList("TQAction") : 0; + if (siblings) { + TQObjectListIt it(*siblings); + while (it.current()) { + TQAction *sib = ::qt_cast<TQAction*>(it.current()); + ++it; + sib->removeFrom(combo); + } + it = TQObjectListIt(*siblings); + while (it.current()) { + TQAction *sib = ::qt_cast<TQAction*>(it.current()); + ++it; + if (sib == action) + continue; + sib->addTo(combo); + } + } + delete siblings; + } + +#ifndef QT_NO_ACCEL + delete accel; +#endif + delete iconset; +} + +class TQActionGroupPrivate +{ +public: + uint exclusive: 1; + uint dropdown: 1; + TQPtrList<TQAction> actions; + TQAction* selected; + TQAction* separatorAction; + + struct MenuItem { + MenuItem():popup(0),id(0){} + TQPopupMenu* popup; + int id; + }; + + TQPtrList<TQComboBox> comboboxes; + TQPtrList<TQToolButton> menubuttons; + TQPtrList<MenuItem> menuitems; + TQPtrList<TQPopupMenu> popupmenus; + + void update( const TQActionGroup * ); +}; + +void TQActionPrivate::update( uint upd ) +{ + for ( TQPtrListIterator<MenuItem> it( menuitems); it.current(); ++it ) { + MenuItem* mi = it.current(); + TQString t = menuText(); +#ifndef QT_NO_ACCEL + if ( key ) + t += '\t' + TQAccel::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, TQIconSet(), 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 ( TQPtrListIterator<TQToolButton> it2(toolbuttons); it2.current(); ++it2 ) { + TQToolButton* 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( TQIconSet() ); + } + if ( upd & EverythingElse ) { + btn->setToggleButton( toggleaction ); + if ( !text.isEmpty() ) + btn->setTextLabel( text, FALSE ); +#ifndef QT_NO_TOOLTIP + TQToolTip::remove( btn ); + TQToolTip::add( btn, toolTip(), &tipGroup, statusTip() ); +#endif +#ifndef QT_NO_WHATSTHIS + TQWhatsThis::remove( btn ); + if ( !whatsthis.isEmpty() ) + TQWhatsThis::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 ( TQPtrListIterator<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 ); + } +} + +TQString TQActionPrivate::menuText() const +{ + if ( menutext.isNull() ) { + TQString t(text); + t.replace('&', "&&"); + return t; + } + return menutext; +} + +TQString TQActionPrivate::toolTip() const +{ + if ( tooltip.isNull() ) { +#ifndef QT_NO_ACCEL + if ( accel ) + return text + " (" + TQAccel::keyToString( accel->key( accelid )) + ")"; +#endif + return text; + } + return tooltip; +} + +TQString TQActionPrivate::statusTip() const +{ + if ( statustip.isNull() ) + return toolTip(); + return statustip; +} + +/* + internal: guesses a descriptive text from a menu text + */ +static TQString qt_stripMenuText( TQString s ) +{ + s.remove( TQString::fromLatin1("...") ); + s.remove( TQChar('&' ) ); + return s.stripWhiteSpace(); +} + +/*! + Constructs an action called \a name with parent \a parent. + + If \a parent is a TQActionGroup, 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. +*/ +TQAction::TQAction( TQObject* parent, const char* name ) + : TQObject( parent, name ) +{ + d = new TQActionPrivate(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 TQActionGroup, 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. +*/ +TQAction::TQAction( TQObject* parent, const char* name, bool toggle ) + : TQObject( parent, name ) +{ + d = new TQActionPrivate(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 TQActionGroup, 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. +*/ +TQAction::TQAction( const TQIconSet& icon, const TQString& menuText, TQKeySequence accel, + TQObject* parent, const char* name ) + : TQObject( parent, name ) +{ + d = new TQActionPrivate(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 TQActionGroup, 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. +*/ +TQAction::TQAction( const TQString& menuText, TQKeySequence accel, + TQObject* parent, const char* name ) + : TQObject( parent, name ) +{ + d = new TQActionPrivate(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 TQActionGroup, 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(). +*/ +TQAction::TQAction( const TQString& text, const TQIconSet& icon, const TQString& menuText, TQKeySequence accel, TQObject* parent, const char* name, bool toggle ) + : TQObject( parent, name ) +{ + d = new TQActionPrivate(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 TQActionGroup. + + 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(). +*/ +TQAction::TQAction( const TQString& text, const TQString& menuText, TQKeySequence accel, TQObject* parent, const char* name, bool toggle ) + : TQObject( parent, name ) +{ + d = new TQActionPrivate(this); + d->toggleaction = toggle; + d->text = text; + d->menutext = menuText; + setAccel( accel ); + init(); +} +#endif + +/*! + \internal +*/ +void TQAction::init() +{ + if ( ::qt_cast<TQActionGroup*>(parent()) ) + ((TQActionGroup*) parent())->add( this ); // insert into action group +} + +/*! + Destroys the object and frees allocated resources. +*/ + +TQAction::~TQAction() +{ + delete d; +} + +/*! + \property TQAction::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 (TQIconSet::isNull() is passed into this function, + the icon of the action is cleared. + + (See the action/toggleaction/toggleaction.cpp example.) + +*/ +void TQAction::setIconSet( const TQIconSet& icon ) +{ + register TQIconSet *i = d->iconset; + if ( !icon.isNull() ) + d->iconset = new TQIconSet( icon ); + else + d->iconset = 0; + delete i; + d->update( TQActionPrivate::Icons ); +} + +TQIconSet TQAction::iconSet() const +{ + if ( d->iconset ) + return *d->iconset; + return TQIconSet(); +} + +/*! + \property TQAction::text + \brief the action's descriptive text + + If \l TQMainWindow::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 TQAction::setText( const TQString& text ) +{ + d->text = text; + d->update(); +} + +TQString TQAction::text() const +{ + return d->text; +} + + +/*! + \property TQAction::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 TQAction::setMenuText( const TQString& text ) +{ + if ( d->menutext == text ) + return; + + d->menutext = text; + d->update(); +} + +TQString TQAction::menuText() const +{ + return d->menuText(); +} + +/*! + \property TQAction::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 TQAction::setToolTip( const TQString& tip ) +{ + if ( d->tooltip == tip ) + return; + + d->tooltip = tip; + d->update(); +} + +TQString TQAction::toolTip() const +{ + return d->toolTip(); +} + +/*! + \property TQAction::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 TQActionGroup! +//#### For consistency reasons even action groups should show +//#### status tips (as they already do with tool tips) +//#### Please change TQActionGroup class doc appropriately after +//#### reimplementation. +void TQAction::setStatusTip( const TQString& tip ) +{ + if ( d->statustip == tip ) + return; + + d->statustip = tip; + d->update(); +} + +TQString TQAction::statusTip() const +{ + return d->statusTip(); +} + +/*! + \property TQAction::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 + TQStyleSheet for the list of supported tags). There is no default + "What's This" text. + + \sa TQWhatsThis +*/ +void TQAction::setWhatsThis( const TQString& whatsThis ) +{ + if ( d->whatsthis == whatsThis ) + return; + d->whatsthis = whatsThis; + d->update(); +} + +TQString TQAction::whatsThis() const +{ + return d->whatsthis; +} + + +#ifndef QT_NO_ACCEL +/*! + \property TQAction::accel + \brief the action's accelerator key + + The keycodes can be found in \l TQt::Key and \l TQt::Modifier. There + is no default accelerator key. +*/ +//#### Please reimp for TQActionGroup! +//#### For consistency reasons even TQActionGroups should respond to +//#### their accelerators and e.g. open the relevant submenu. +//#### Please change appropriate TQActionGroup class doc after +//#### reimplementation. +void TQAction::setAccel( const TQKeySequence& key ) +{ + if ( d->key == key ) + return; + + d->key = key; + delete d->accel; + d->accel = 0; + + if ( !(int)key ) { + d->update(); + return; + } + + TQObject* p = parent(); + while ( p && !p->isWidgetType() ) { + p = p->parent(); + } + if ( p ) { + d->accel = new TQAccel( (TQWidget*)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( "TQAction::setAccel() (%s) retquires widget in parent chain", name() ); +#endif + d->update(); +} + + +TQKeySequence TQAction::accel() const +{ + return d->key; +} +#endif + + +/*! + \property TQAction::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 + TQActionGroup with the \l TQActionGroup::exclusive property set to + TRUE. +*/ +void TQAction::setToggleAction( bool enable ) +{ + if ( enable == (bool)d->toggleaction ) + return; + + if ( !enable ) + d->on = FALSE; + + d->toggleaction = enable; + d->update(); +} + +bool TQAction::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 TQAction::activate() +{ + if ( isToggleAction() ) { +#if defined(QT_CHECK_STATE) + qWarning( "TQAction::%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 TQAction::toggle() +{ + if ( !isToggleAction() ) { +#if defined(QT_CHECK_STATE) + qWarning( "TQAction::%s() (%s) Only toggle actions " + "can be switched", "toggle", name() ); +#endif + return; + } + setOn( !isOn() ); +} + +/*! + \property TQAction::on + \brief whether a toggle action is on + + This property is always on (TRUE) for command actions and + \l{TQActionGroup}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 TQAction::setOn( bool enable ) +{ + if ( !isToggleAction() ) { +#if defined(QT_CHECK_STATE) + if ( enable ) + qWarning( "TQAction::%s() (%s) Only toggle actions " + "can be switched", "setOn", name() ); +#endif + return; + } + if ( enable == (bool)d->on ) + return; + d->on = enable; + d->update( TQActionPrivate::State ); + emit toggled( enable ); +} + +bool TQAction::isOn() const +{ + return d->on; +} + +/*! + \property TQAction::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 TQAction::whatsThis property is set. +*/ +void TQAction::setEnabled( bool enable ) +{ + d->forceDisabled = !enable; + + if ( (bool)d->enabled == enable ) + return; + + d->enabled = enable; + d->update( TQActionPrivate::State ); +} + +bool TQAction::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 TQAction::setDisabled( bool disable ) +{ + setEnabled( !disable ); +} + +/*! + \property TQAction::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 TQAction::setVisible( bool visible ) +{ + d->forceInvisible = !visible; + + if ( (bool)d->visible == visible ) + return; + d->visible = visible; + d->update( TQActionPrivate::Visibility ); +#if (QT_VERSION-0 >= 0x040000) +#error "TQAction::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( (TQActionGroup*) this ); +} + +/* + Returns TRUE if the action is visible (e.g. in menus and + toolbars); otherwise returns FALSE. +*/ +bool TQAction::isVisible() const +{ + return d->visible; +} + +/*! \internal +*/ +void TQAction::internalActivation() +{ + if ( isToggleAction() ) + setOn( !isOn() ); + emit activated(); +} + +/*! \internal +*/ +void TQAction::toolButtonToggled( bool on ) +{ + if ( !isToggleAction() ) + return; + setOn( on ); +} + +/*! + Adds this action to widget \a w. + + Currently actions may be added to TQToolBar and TQPopupMenu 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 TQToolBar or TQPopupMenu the + action will not be added and FALSE will be returned.) + + \sa removeFrom() +*/ +bool TQAction::addTo( TQWidget* w ) +{ +#ifndef QT_NO_TOOLBAR + if ( ::qt_cast<TQToolBar*>(w) ) { + if ( !qstrcmp( name(), "qt_separator_action" ) ) { + ((TQToolBar*)w)->addSeparator(); + } else { + TQCString bname = name() + TQCString( "_action_button" ); + TQToolButton* btn = new TQToolButton( (TQToolBar*) w, bname ); + addedTo( btn, w ); + btn->setToggleButton( d->toggleaction ); + d->toolbuttons.append( btn ); + if ( d->iconset ) + btn->setIconSet( *d->iconset ); + d->update( TQActionPrivate::State | TQActionPrivate::Visibility | TQActionPrivate::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 TQString&)), this, SLOT(showStatusText(const TQString&)) ); + connect( &(d->tipGroup), SIGNAL(removeTip()), this, SLOT(clearStatusText()) ); +#endif + } + } else +#endif + if ( ::qt_cast<TQPopupMenu*>(w) ) { + TQActionPrivate::MenuItem* mi = new TQActionPrivate::MenuItem; + mi->popup = (TQPopupMenu*) w; + TQIconSet* diconset = d->iconset; + if ( !qstrcmp( name(), "qt_separator_action" ) ) + mi->id = ((TQPopupMenu*)w)->insertSeparator(); + else if ( diconset ) + mi->id = mi->popup->insertItem( *diconset, TQString::fromLatin1("") ); + else + mi->id = mi->popup->insertItem( TQString::fromLatin1("") ); + addedTo( mi->popup->indexOf( mi->id ), mi->popup ); + mi->popup->connectItem( mi->id, this, SLOT(internalActivation()) ); + d->menuitems.append( mi ); + d->update( TQActionPrivate::State | TQActionPrivate::Visibility | TQActionPrivate::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 TQActionGroup::addTo + } else if ( ::qt_cast<TQComboBox*>(w) ) { + TQActionPrivate::ComboItem *ci = new TQActionPrivate::ComboItem; + ci->combo = (TQComboBox*)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( TQActionPrivate::State | TQActionPrivate::EverythingElse ); + } else { + qWarning( "TQAction::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 TQAction::addedTo( TQWidget *actionWidget, TQWidget *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 TQAction::addedTo( int index, TQPopupMenu *menu ) +{ + Q_UNUSED( index ); + Q_UNUSED( menu ); +} + +/*! + Sets the status message to \a text +*/ +void TQAction::showStatusText( const TQString& text ) +{ +#ifndef QT_NO_STATUSBAR + // find out whether we are clearing the status bar by the popup that actually set the text + static TQPopupMenu *lastmenu = 0; + TQObject *s = (TQObject*)sender(); + if ( s ) { + TQPopupMenu *menu = (TQPopupMenu*)s->qt_cast( "TQPopupMenu" ); + if ( menu && !!text ) + lastmenu = menu; + else if ( menu && text.isEmpty() ) { + if ( lastmenu && menu != lastmenu ) + return; + lastmenu = 0; + } + } + + TQObject* par = parent(); + TQObject* lpar = 0; + TQStatusBar *bar = 0; + while ( par && !bar ) { + lpar = par; + bar = (TQStatusBar*)par->child( 0, "TQStatusBar", FALSE ); + par = par->parent(); + } + if ( !bar && lpar ) { + TQObjectList *l = lpar->queryList( "TQStatusBar" ); + if ( !l ) + return; + // #### hopefully the last one is the one of the mainwindow... + bar = (TQStatusBar*)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 TQAction::menuStatusText( int id ) +{ + static int lastId = 0; + TQString text; + TQPtrListIterator<TQActionPrivate::MenuItem> it( d->menuitems); + TQActionPrivate::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 TQAction::clearStatusText() +{ + if (!statusTip().isEmpty()) + showStatusText( TQString::null ); +} + +/*! + Removes the action from widget \a w. + + Returns TRUE if the action was removed successfully; otherwise + returns FALSE. + + \sa addTo() +*/ +bool TQAction::removeFrom( TQWidget* w ) +{ +#ifndef QT_NO_TOOLBAR + if ( ::qt_cast<TQToolBar*>(w) ) { + TQPtrListIterator<TQToolButton> it( d->toolbuttons); + TQToolButton* 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<TQPopupMenu*>(w) ) { + TQPtrListIterator<TQActionPrivate::MenuItem> it( d->menuitems); + TQActionPrivate::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<TQComboBox*>(w) ) { + TQPtrListIterator<TQActionPrivate::ComboItem> it( d->comboitems ); + TQActionPrivate::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( "TQAction::removeFrom(), unknown object" ); + return FALSE; + } + return TRUE; +} + +/*! + \internal +*/ +void TQAction::objectDestroyed() +{ + const TQObject* obj = sender(); + TQPtrListIterator<TQActionPrivate::MenuItem> it( d->menuitems ); + TQActionPrivate::MenuItem* mi; + while ( ( mi = it.current() ) ) { + ++it; + if ( mi->popup == obj ) + d->menuitems.removeRef( mi ); + } + TQActionPrivate::ComboItem *ci; + TQPtrListIterator<TQActionPrivate::ComboItem> it2( d->comboitems ); + while ( ( ci = it2.current() ) ) { + ++it2; + if ( ci->combo == obj ) + d->comboitems.removeRef( ci ); + } + d->toolbuttons.removeRef( (TQToolButton*) obj ); +} + +/*! + \fn void TQAction::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 TQAction::toggled(bool on) + + This signal is emitted when a toggle action changes state; command + actions and \l{TQActionGroup}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 TQMainWindow * window + \printline TQMainWindow * window + \skipto labelonoffaction + \printline labelonoffaction + \skipto connect + \printuntil setUsesTextLabel + + \sa activated() setToggleAction() setOn() +*/ + +void TQActionGroupPrivate::update( const TQActionGroup* that ) +{ + for ( TQPtrListIterator<TQAction> 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 ( TQPtrListIterator<TQComboBox> cb( comboboxes ); cb.current(); ++cb ) { + TQComboBox *combobox = cb.current(); + combobox->setEnabled( that->isEnabled() ); + combobox->setShown( that->isVisible() ); + +#ifndef QT_NO_TOOLTIP + TQToolTip::remove( combobox ); + if ( !!that->toolTip() ) + TQToolTip::add( combobox, that->toolTip() ); +#endif +#ifndef QT_NO_WHATSTHIS + TQWhatsThis::remove( combobox ); + if ( !!that->whatsThis() ) + TQWhatsThis::add( combobox, that->whatsThis() ); +#endif + + } + for ( TQPtrListIterator<TQToolButton> mb( menubuttons ); mb.current(); ++mb ) { + TQToolButton *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 + TQToolTip::remove( mb.current() ); + if ( !!that->toolTip() ) + TQToolTip::add( button, that->toolTip() ); +#endif +#ifndef QT_NO_WHATSTHIS + TQWhatsThis::remove( button ); + if ( !!that->whatsThis() ) + TQWhatsThis::add( button, that->whatsThis() ); +#endif + } + for ( TQPtrListIterator<TQActionGroupPrivate::MenuItem> pu( menuitems ); pu.current(); ++pu ) { + TQWidget* parent = pu.current()->popup->parentWidget(); + if ( ::qt_cast<TQPopupMenu*>(parent) ) { + TQPopupMenu* ppopup = (TQPopupMenu*)parent; + ppopup->setItemEnabled( pu.current()->id, that->isEnabled() ); + ppopup->setItemVisible( pu.current()->id, that->isVisible() ); + } else { + pu.current()->popup->setEnabled( that->isEnabled() ); + } + } + for ( TQPtrListIterator<TQPopupMenu> pm( popupmenus ); pm.current(); ++pm ) { + TQPopupMenu *popup = pm.current(); + TQPopupMenu *parent = ::qt_cast<TQPopupMenu*>(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 TQActionGroup qaction.h + \brief The TQActionGroup 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 TQActionGroup + \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 TQActionGroup 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. +*/ +TQActionGroup::TQActionGroup( TQObject* parent, const char* name ) + : TQAction( parent, name ) +{ + d = new TQActionGroupPrivate; + d->exclusive = TRUE; + d->dropdown = FALSE; + d->selected = 0; + d->separatorAction = 0; + TQAction::d->d_group = d; + + connect( this, SIGNAL(selected(TQAction*)), SLOT(internalToggle(TQAction*)) ); +} + +/*! + 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 +*/ +TQActionGroup::TQActionGroup( TQObject* parent, const char* name, bool exclusive ) + : TQAction( parent, name ) +{ + d = new TQActionGroupPrivate; + d->exclusive = exclusive; + d->dropdown = FALSE; + d->selected = 0; + d->separatorAction = 0; + TQAction::d->d_group = d; + + connect( this, SIGNAL(selected(TQAction*)), SLOT(internalToggle(TQAction*)) ); +} + +/*! + Destroys the object and frees allocated resources. +*/ + +TQActionGroup::~TQActionGroup() +{ + TQPtrListIterator<TQActionGroupPrivate::MenuItem> mit( d->menuitems ); + while ( mit.current() ) { + TQActionGroupPrivate::MenuItem *mi = mit.current(); + ++mit; + if ( mi->popup ) + mi->popup->disconnect( SIGNAL(destroyed()), this, SLOT(objectDestroyed()) ); + } + + TQPtrListIterator<TQComboBox> cbit( d->comboboxes ); + while ( cbit.current() ) { + TQComboBox *cb = cbit.current(); + ++cbit; + cb->disconnect( SIGNAL(destroyed()), this, SLOT(objectDestroyed()) ); + } + TQPtrListIterator<TQToolButton> mbit( d->menubuttons ); + while ( mbit.current() ) { + TQToolButton *mb = mbit.current(); + ++mbit; + mb->disconnect( SIGNAL(destroyed()), this, SLOT(objectDestroyed()) ); + } + TQPtrListIterator<TQPopupMenu> pmit( d->popupmenus ); + while ( pmit.current() ) { + TQPopupMenu *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 TQActionGroup::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 TQAction::toggleAction +*/ +void TQActionGroup::setExclusive( bool enable ) +{ + d->exclusive = enable; +} + +bool TQActionGroup::isExclusive() const +{ + return d->exclusive; +} + +/*! + \property TQActionGroup::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 TQAction::text and \l + TQAction::iconSet properties shown. Non-exclusive groups are + represented by a tool button showing their \l TQAction::iconSet and + -- depending on \l TQMainWindow::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 TQAction::setVisible()\endlink, + \link TQAction::setEnabled()\endlink, and + \link TQAction::setDisabled()\endlink functions to have no effect. + + This property's default is FALSE. + +*/ +void TQActionGroup::setUsesDropDown( bool enable ) +{ + d->dropdown = enable; +} + +bool TQActionGroup::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 TQActionGroup::add( TQAction* 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 ( TQPtrListIterator<TQComboBox> cb( d->comboboxes ); cb.current(); ++cb ) { + action->addTo( cb.current() ); + } + for ( TQPtrListIterator<TQToolButton> mb( d->menubuttons ); mb.current(); ++mb ) { + TQPopupMenu* popup = mb.current()->popup(); + if ( !popup ) + continue; + action->addTo( popup ); + } + for ( TQPtrListIterator<TQActionGroupPrivate::MenuItem> mi( d->menuitems ); mi.current(); ++mi ) { + TQPopupMenu* popup = mi.current()->popup; + if ( !popup ) + continue; + action->addTo( popup ); + } +} + +/*! + Adds a separator to the group. +*/ +void TQActionGroup::addSeparator() +{ + if ( !d->separatorAction ) + d->separatorAction = new TQAction( 0, "qt_separator_action" ); + d->actions.append( d->separatorAction ); +} + + +/*! \fn void TQActionGroup::insert( TQAction* 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 TQActionGroup::addTo( TQWidget* w ) +{ +#ifndef QT_NO_TOOLBAR + if ( ::qt_cast<TQToolBar*>(w) ) { + if ( d->dropdown ) { + if ( !d->exclusive ) { + TQPtrListIterator<TQAction> it( d->actions); + if ( !it.current() ) + return TRUE; + + TQAction *defAction = it.current(); + + TQToolButton* btn = new TQToolButton( (TQToolBar*) 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() ) + TQToolTip::add( btn, toolTip() ); + else if ( !!defAction->toolTip() ) + TQToolTip::add( btn, defAction->toolTip() ); +#endif +#ifndef QT_NO_WHATSTHIS + if ( !!whatsThis() ) + TQWhatsThis::add( btn, whatsThis() ); + else if ( !!defAction->whatsThis() ) + TQWhatsThis::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() ) ); + + TQPopupMenu *menu = new TQPopupMenu( btn, "qt_actiongroup_menu" ); + btn->setPopupDelay( 0 ); + btn->setPopup( menu ); + + while( it.current() ) { + it.current()->addTo( menu ); + ++it; + } + d->update( this ); + return TRUE; + } else { + TQComboBox *box = new TQComboBox( FALSE, w, "qt_actiongroup_combo" ); + addedTo( box, w ); + connect( box, SIGNAL(destroyed()), SLOT(objectDestroyed()) ); + d->comboboxes.append( box ); +#ifndef QT_NO_TOOLTIP + if ( !!toolTip() ) + TQToolTip::add( box, toolTip() ); +#endif +#ifndef QT_NO_WHATSTHIS + if ( !!whatsThis() ) + TQWhatsThis::add( box, whatsThis() ); +#endif + + int onIndex = 0; + bool foundOn = FALSE; + for ( TQPtrListIterator<TQAction> it( d->actions); it.current(); ++it ) { + TQAction *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<TQPopupMenu*>(w) ) { + TQPopupMenu *popup; + if ( d->dropdown ) { + TQPopupMenu *menu = (TQPopupMenu*)w; + popup = new TQPopupMenu( 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 ); + + TQActionGroupPrivate::MenuItem *item = new TQActionGroupPrivate::MenuItem; + item->id = id; + item->popup = popup; + d->menuitems.append( item ); + } else { + popup = (TQPopupMenu*)w; + } + for ( TQPtrListIterator<TQAction> it( d->actions); it.current(); ++it ) { + // #### do an addedTo( index, popup, action), need to find out index + it.current()->addTo( popup ); + } + return TRUE; + } + + for ( TQPtrListIterator<TQAction> 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 TQActionGroup::removeFrom( TQWidget* w ) +{ + for ( TQPtrListIterator<TQAction> it( d->actions); it.current(); ++it ) { + it.current()->removeFrom( w ); + } + +#ifndef QT_NO_TOOLBAR + if ( ::qt_cast<TQToolBar*>(w) ) { + TQPtrListIterator<TQComboBox> cb( d->comboboxes ); + while( cb.current() ) { + TQComboBox *box = cb.current(); + ++cb; + if ( box->parentWidget() == w ) + delete box; + } + TQPtrListIterator<TQToolButton> mb( d->menubuttons ); + while( mb.current() ) { + TQToolButton *btn = mb.current(); + ++mb; + if ( btn->parentWidget() == w ) + delete btn; + } + } else +#endif + if ( ::qt_cast<TQPopupMenu*>(w) ) { + TQPtrListIterator<TQActionGroupPrivate::MenuItem> pu( d->menuitems ); + while ( pu.current() ) { + TQActionGroupPrivate::MenuItem *mi = pu.current(); + ++pu; + if ( d->dropdown && mi->popup ) + ( (TQPopupMenu*)w )->removeItem( mi->id ); + delete mi->popup; + } + } + + return TRUE; +} + +/*! \internal +*/ +void TQActionGroup::childToggled( bool b ) +{ + if ( !isExclusive() ) + return; + TQAction* s = (TQAction*) sender(); + if ( b ) { + if ( s != d->selected ) { + d->selected = s; + for ( TQPtrListIterator<TQAction> 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 TQActionGroup::childDestroyed() +{ + d->actions.removeRef( (TQAction*) sender() ); + if ( d->selected == sender() ) + d->selected = 0; +} + +/*! \reimp +*/ +void TQActionGroup::setEnabled( bool enable ) +{ + if ( enable == isEnabled() ) + return; + + TQAction::setEnabled( enable ); + d->update( this ); +} + +/*! \reimp +*/ +void TQActionGroup::setToggleAction( bool toggle ) +{ + for ( TQPtrListIterator<TQAction> it( d->actions); it.current(); ++it ) + it.current()->setToggleAction( toggle ); + + TQAction::setToggleAction( TRUE ); + d->update( this ); +} + +/*! \reimp +*/ +void TQActionGroup::setOn( bool on ) +{ + for ( TQPtrListIterator<TQAction> it( d->actions); it.current(); ++it ) { + TQAction *act = it.current(); + if ( act->isToggleAction() ) + act->setOn( on ); + } + + TQAction::setOn( on ); + d->update( this ); +} + +/*! \reimp +*/ +void TQActionGroup::setIconSet( const TQIconSet& icon ) +{ + TQAction::setIconSet( icon ); + d->update( this ); +} + +/*! \reimp +*/ +void TQActionGroup::setText( const TQString& txt ) +{ + if ( txt == text() ) + return; + + TQAction::setText( txt ); + d->update( this ); +} + +/*! \reimp +*/ +void TQActionGroup::setMenuText( const TQString& text ) +{ + if ( text == menuText() ) + return; + + TQAction::setMenuText( text ); + d->update( this ); +} + +/*! \reimp +*/ +void TQActionGroup::setToolTip( const TQString& text ) +{ + if ( text == toolTip() ) + return; + for ( TQPtrListIterator<TQAction> it( d->actions); it.current(); ++it ) { + if ( it.current()->toolTip().isNull() ) + it.current()->setToolTip( text ); + } + TQAction::setToolTip( text ); + d->update( this ); +} + +/*! \reimp +*/ +void TQActionGroup::setWhatsThis( const TQString& text ) +{ + if ( text == whatsThis() ) + return; + for ( TQPtrListIterator<TQAction> it( d->actions); it.current(); ++it ) { + if ( it.current()->whatsThis().isNull() ) + it.current()->setWhatsThis( text ); + } + TQAction::setWhatsThis( text ); + d->update( this ); +} + +/*! \reimp +*/ +void TQActionGroup::childEvent( TQChildEvent *e ) +{ + if ( !e->removed() ) + return; + + TQAction *action = ::qt_cast<TQAction*>(e->child()); + if ( !action ) + return; + + for ( TQPtrListIterator<TQComboBox> 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 ( TQPtrListIterator<TQToolButton> mb( d->menubuttons ); mb.current(); ++mb ) { + TQPopupMenu* popup = mb.current()->popup(); + if ( !popup ) + continue; + action->removeFrom( popup ); + } + for ( TQPtrListIterator<TQActionGroupPrivate::MenuItem> mi( d->menuitems ); mi.current(); ++mi ) { + TQPopupMenu* popup = mi.current()->popup; + if ( !popup ) + continue; + action->removeFrom( popup ); + } +} + +/*! + \fn void TQActionGroup::selected( TQAction* ) + + 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() TQAction::toggled() +*/ + +/*! \internal +*/ +void TQActionGroup::internalComboBoxActivated( int index ) +{ + TQAction *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 ( TQPtrListIterator<TQAction> 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 ((TQActionGroup*)a)->activated(); + } else if ( !a->isToggleAction() ) { + emit activated(); + emit ((TQActionGroup*)a)->activated(); + } + a->clearStatusText(); + } +} + +/*! \internal +*/ +void TQActionGroup::internalComboBoxHighlighted( int index ) +{ + TQAction *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 TQActionGroup::internalToggle( TQAction *a ) +{ + int index = d->actions.find( a ); + if ( index == -1 ) + return; + + int lastItem = index; + for ( int i = 0; i < lastItem; i++ ) { + TQAction *action = d->actions.at( i ); + if ( !qstrcmp( action->name(), "qt_separator_action" ) ) + index--; + } + + for ( TQPtrListIterator<TQComboBox> it( d->comboboxes); it.current(); ++it ) + it.current()->setCurrentItem( index ); +} + +/*! \internal +*/ +void TQActionGroup::objectDestroyed() +{ + const TQObject* obj = sender(); + d->menubuttons.removeRef( (TQToolButton*)obj ); + for ( TQPtrListIterator<TQActionGroupPrivate::MenuItem> mi( d->menuitems ); mi.current(); ++mi ) { + if ( mi.current()->popup == obj ) { + d->menuitems.removeRef( mi.current() ); + break; + } + } + d->popupmenus.removeRef( (TQPopupMenu*)obj ); + d->comboboxes.removeRef( (TQComboBox*)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 TQActionGroup::addedTo( TQWidget *actionWidget, TQWidget *container, TQAction *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 TQActionGroup::addedTo( int index, TQPopupMenu *menu, TQAction *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 TQActionGroup::addedTo( TQWidget *actionWidget, TQWidget *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 TQActionGroup::addedTo( int index, TQPopupMenu *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 000000000..b8c04b13a --- /dev/null +++ b/src/widgets/qaction.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Definition of TQAction class +** +** Created : 000000 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQACTION_H +#define TQACTION_H + +#ifndef QT_H +#include "qobject.h" +#include "qiconset.h" +#include "qstring.h" +#include "qkeysequence.h" +#endif // QT_H + +#ifndef QT_NO_ACTION + +class TQActionPrivate; +class TQActionGroupPrivate; +class TQStatusBar; +class TQPopupMenu; +class TQToolTipGroup; + +class Q_EXPORT TQAction : public TQObject +{ + 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( TQIconSet iconSet READ iconSet WRITE setIconSet ) + Q_PROPERTY( TQString text READ text WRITE setText ) + Q_PROPERTY( TQString menuText READ menuText WRITE setMenuText ) + Q_PROPERTY( TQString toolTip READ toolTip WRITE setToolTip ) + Q_PROPERTY( TQString statusTip READ statusTip WRITE setStatusTip ) + Q_PROPERTY( TQString whatsThis READ whatsThis WRITE setWhatsThis ) +#ifndef QT_NO_ACCEL + Q_PROPERTY( TQKeySequence accel READ accel WRITE setAccel ) +#endif + Q_PROPERTY( bool visible READ isVisible WRITE setVisible ) + +public: + TQAction( TQObject* parent, const char* name = 0 ); +#ifndef QT_NO_ACCEL + TQAction( const TQString& menuText, TQKeySequence accel, + TQObject* parent, const char* name = 0 ); + TQAction( const TQIconSet& icon, const TQString& menuText, TQKeySequence accel, + TQObject* parent, const char* name = 0 ); + + TQAction( const TQString& text, const TQIconSet& icon, const TQString& menuText, TQKeySequence accel, + TQObject* parent, const char* name = 0, bool toggle = FALSE ); // obsolete + TQAction( const TQString& text, const TQString& menuText, TQKeySequence accel, TQObject* parent, + const char* name = 0, bool toggle = FALSE ); // obsolete +#endif + TQAction( TQObject* parent, const char* name , bool toggle ); // obsolete + ~TQAction(); + + virtual void setIconSet( const TQIconSet& ); + TQIconSet iconSet() const; + virtual void setText( const TQString& ); + TQString text() const; + virtual void setMenuText( const TQString& ); + TQString menuText() const; + virtual void setToolTip( const TQString& ); + TQString toolTip() const; + virtual void setStatusTip( const TQString& ); + TQString statusTip() const; + virtual void setWhatsThis( const TQString& ); + TQString whatsThis() const; +#ifndef QT_NO_ACCEL + virtual void setAccel( const TQKeySequence& key ); + TQKeySequence accel() const; +#endif + virtual void setToggleAction( bool ); + + bool isToggleAction() const; + bool isOn() const; + bool isEnabled() const; + bool isVisible() const; + virtual bool addTo( TQWidget* ); + virtual bool removeFrom( TQWidget* ); + +protected: + virtual void addedTo( TQWidget *actionWidget, TQWidget *container ); + virtual void addedTo( int index, TQPopupMenu *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 TQString& ); + void clearStatusText(); + +private: + void init(); + + friend class TQActionGroup; + friend class TQActionGroupPrivate; + TQActionPrivate* d; + +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQAction( const TQAction & ); + TQAction &operator=( const TQAction & ); +#endif +}; + +class Q_EXPORT TQActionGroup : public TQAction +{ + Q_OBJECT + Q_PROPERTY( bool exclusive READ isExclusive WRITE setExclusive ) + Q_PROPERTY( bool usesDropDown READ usesDropDown WRITE setUsesDropDown ) + +public: + TQActionGroup( TQObject* parent, const char* name = 0 ); + TQActionGroup( TQObject* parent, const char* name , bool exclusive ); // obsolete + ~TQActionGroup(); + void setExclusive( bool ); + bool isExclusive() const; + void add( TQAction* a); + void addSeparator(); + bool addTo( TQWidget* ); + bool removeFrom( TQWidget* ); + void setEnabled( bool ); + void setToggleAction( bool toggle ); + void setOn( bool on ); + + void setUsesDropDown( bool enable ); + bool usesDropDown() const; + + void setIconSet( const TQIconSet& ); + void setText( const TQString& ); + void setMenuText( const TQString& ); + void setToolTip( const TQString& ); + void setWhatsThis( const TQString& ); + +protected: + void childEvent( TQChildEvent* ); + virtual void addedTo( TQWidget *actionWidget, TQWidget *container, TQAction *a ); + virtual void addedTo( int index, TQPopupMenu *menu, TQAction *a ); + virtual void addedTo( TQWidget *actionWidget, TQWidget *container ); + virtual void addedTo( int index, TQPopupMenu *menu ); + +signals: + void selected( TQAction* ); + +private slots: + void childToggled( bool ); + void childDestroyed(); + void internalComboBoxActivated( int ); + void internalComboBoxHighlighted( int ); + void internalToggle( TQAction* ); + void objectDestroyed(); + +private: + TQActionGroupPrivate* d; + +#ifndef QT_NO_COMPAT +public: + void insert( TQAction* a ) { add( a ); } +#endif + +private: +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQActionGroup( const TQActionGroup & ); + TQActionGroup &operator=( const TQActionGroup & ); +#endif +}; + +#endif + +#endif diff --git a/src/widgets/qbutton.cpp b/src/widgets/qbutton.cpp new file mode 100644 index 000000000..3cacc089d --- /dev/null +++ b/src/widgets/qbutton.cpp @@ -0,0 +1,1008 @@ +/**************************************************************************** +** +** Implementation of TQButton widget class +** +** Created : 940206 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#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 TQButtonData +{ +public: + TQButtonData() { +#ifndef QT_NO_BUTTONGROUP + group = 0; +#endif +#ifndef QT_NO_ACCEL + a = 0; +#endif + } +#ifndef QT_NO_BUTTONGROUP + TQButtonGroup *group; +#endif + TQTimer timer; +#ifndef QT_NO_ACCEL + TQAccel *a; +#endif +}; + + +void TQButton::ensureData() +{ + if ( !d ) { + d = new TQButtonData; + 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 TQButtonGroup, this function + returns 0. + + \sa TQButtonGroup +*/ + +TQButtonGroup *TQButton::group() const +{ +#ifndef QT_NO_BUTTONGROUP + return d ? d->group : 0; +#else + return 0; +#endif +} + + +void TQButton::setGroup( TQButtonGroup* g ) +{ +#ifndef QT_NO_BUTTONGROUP + ensureData(); + d->group = g; +#endif +} + + +TQTimer *TQButton::timer() +{ + ensureData(); + return &d->timer; +} + + +/*! + \class TQButton qbutton.h + \brief The TQButton 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 TQPushButton.</b> + + The TQButton class implements an \e abstract button, and lets + subclasses specify how to reply to user actions and how to draw + the button. + + TQButton provides both push and toggle buttons. The TQRadioButton + and TQCheckBox classes provide only toggle buttons; TQPushButton and + TQToolButton 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. + + TQButton 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 + TQPushButton::setDefault() and TQPushButton::setAutoDefault(). + + TQButton 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, + TQButton 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 + TQPushButton *p = new TQPushButton( "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( TQPixmap("print.png") ); + p->setAccel( ALT+Key_F7 ); + \endcode + + All of the buttons provided by TQt (\l TQPushButton, \l TQToolButton, + \l TQCheckBox and \l TQRadioButton) can display both text and + pixmaps. + + To subclass TQButton, 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, TQButton::paintEvent() sets up a pixmap that + the drawButton() function draws in. You should not reimplement + paintEvent() for a subclass of TQButton unless you want to take + over all drawing. + + \sa TQButtonGroup +*/ + + +/*! + \enum TQButton::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 TQButton::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 TQButton::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 TQButton::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 TQButton::autoResize + \brief whether autoResize is enabled + \obsolete + + If autoResize is enabled then the button will resize itself + whenever the contents are changed. +*/ + +/*! + \property TQButton::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 TQButton::exclusiveToggle + \brief whether the button is an exclusive toggle + + If this property is TRUE and the button is in a TQButtonGroup, the + button can only be toggled off by another one being toggled on. + The default is FALSE. +*/ + +/*! + \property TQButton::on + \brief whether the button is toggled + + This property should only be set for toggle buttons. +*/ + +/*! + \fn void TQButton::setOn( bool on ) + + Sets the state of this button to On if \a on is TRUE; otherwise to + Off. + + \sa toggleState +*/ + +/*! + \property TQButton::pixmap + \brief the pixmap shown on the button + + If the pixmap is monochrome (i.e. it is a TQBitmap or its \link + TQPixmap::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 TQButton::text + \brief the text shown on the button + + This property will return a TQString::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 TQButton::toggleButton + \brief whether the button is a toggle button + + The default value is FALSE. +*/ + +/*! + \fn TQButton::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 TQButton::toggleState + \brief the state of the toggle button + + If this property is changed then it does not cause the button + to be repainted. +*/ + +/*! + \property TQButton::toggleType + \brief the type of toggle on the button + + The default toggle type is \c SingleShot. + + \sa TQButton::ToggleType +*/ + +/*! + Constructs a standard button called \a name with parent \a parent, + using the widget flags \a f. + + If \a parent is a TQButtonGroup, this constructor calls + TQButtonGroup::insert(). +*/ + +TQButton::TQButton( TQWidget *parent, const char *name, WFlags f ) + : TQWidget( 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<TQButtonGroup*>(parent) ) { + setGroup((TQButtonGroup*)parent); + group()->insert( this ); // insert into button group + } +#endif + setFocusPolicy( TabFocus ); +} + +/*! + Destroys the button. + */ +TQButton::~TQButton() +{ +#ifndef QT_NO_BUTTONGROUP + if ( group() ) + group()->remove( this ); +#endif + delete bpixmap; + delete d; +} + + +/*! + \fn void TQButton::pressed() + + This signal is emitted when the button is pressed down. + + \sa released(), clicked() +*/ + +/*! + \fn void TQButton::released() + + This signal is emitted when the button is released. + + \sa pressed(), clicked(), toggled() +*/ + +/*! + \fn void TQButton::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 TQButtonGroup::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 TQButton::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 TQButton::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 TQCheckBox::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() TQButton::ToggleState +*/ + +void TQButton::setText( const TQString &text ) +{ + if ( btext == text ) + return; + btext = text; +#ifndef QT_NO_ACCEL + setAccel( TQAccel::shortcutKey( text ) ); +#endif + + if ( bpixmap ) { + delete bpixmap; + bpixmap = 0; + } + + if ( autoresize ) + adjustSize(); + + update(); + updateGeometry(); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::NameChanged ); +#endif +} + +void TQButton::setPixmap( const TQPixmap &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 TQPixmap( pixmap ); + Q_CHECK_PTR( bpixmap ); + } + if ( bpixmap->depth() == 1 && !bpixmap->mask() ) + bpixmap->setMask( *((TQBitmap *)bpixmap) ); + if ( !btext.isNull() ) { + btext = TQString::null; +#ifndef QT_NO_ACCEL + setAccel( TQKeySequence() ); +#endif + } + if ( autoresize && newSize ) + adjustSize(); + if ( autoMask() ) + updateMask(); + update(); + if ( newSize ) + updateGeometry(); +} + + +#ifndef QT_NO_ACCEL +TQKeySequence TQButton::accel() const +{ + if ( d && d->a ) + return d->a->key( 0 ); + return TQKeySequence(); +} + +void TQButton::setAccel( const TQKeySequence& key ) +{ + if ( d && d->a ) + d->a->clear(); + if ( key.isEmpty() ) + return; + ensureData(); + if ( !d->a ) { + d->a = new TQAccel( 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 TQButton::setAutoResize( bool enable ) +{ + if ( (bool)autoresize != enable ) { + autoresize = enable; + if ( autoresize ) + adjustSize(); // calls resize which repaints + } +} + +#endif + +void TQButton::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 TQButton::animateClick() +{ + if ( !isEnabled() || animation ) + return; + animation = TRUE; + buttonDown = TRUE; + repaint( FALSE ); + emit pressed(); + TQTimer::singleShot( 100, this, SLOT(animateTimeout()) ); +} + +void TQButton::emulateClick() +{ + if ( !isEnabled() || animation ) + return; + animation = TRUE; + buttonDown = TRUE; + emit pressed(); + animateTimeout(); +} + +void TQButton::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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::StateChanged ); +#endif + } +} + +/*! + Sets the toggle state of the button to \a s. \a s can be \c Off, \c + NoChange or \c On. +*/ + +void TQButton::setState( ToggleState s ) +{ + if ( !toggleTyp ) { +#if defined(QT_CHECK_STATE) + qWarning( "TQButton::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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::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 TQButton::hitButton( const TQPoint &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 "TQButton. Make pure virtual" +#endif +void TQButton::drawButton( TQPainter * ) +{ + 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 TQButton::drawButtonLabel( TQPainter * ) +{ + return; +} + +/*! \reimp */ +void TQButton::keyPressEvent( TQKeyEvent *e ) +{ + switch ( e->key() ) { + case Key_Enter: + case Key_Return: + { +#ifndef QT_NO_PUSHBUTTON + TQPushButton *pb = (TQPushButton*)qt_cast( "TQPushButton" ); + 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<TQPushButton*>(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 + { + TQFocusEvent::setReason(TQFocusEvent::Backtab); + focusNextPrevChild( FALSE ); + TQFocusEvent::resetReason(); + } + break; + case Key_Right: + case Key_Down: +#ifndef QT_NO_BUTTONGROUP + if ( group() ) { + group()->moveFocus( e->key() ); + } else +#endif + { + TQFocusEvent::setReason(TQFocusEvent::Tab); + focusNextPrevChild( TRUE ); + TQFocusEvent::resetReason(); + } + break; + case Key_Escape: + if ( buttonDown ) { + buttonDown = FALSE; + update(); + break; + } + // fall through + default: + e->ignore(); + } +} + +/*! \reimp */ +void TQButton::keyReleaseEvent( TQKeyEvent * e) +{ + switch ( e->key() ) { + case Key_Space: + if ( buttonDown && !e->isAutoRepeat() ) { + buttonDown = FALSE; + nextState(); + emit released(); + emit clicked(); + } + break; + default: + e->ignore(); + } +} + +/*! \reimp */ +void TQButton::mousePressEvent( TQMouseEvent *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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::StateChanged ); +#endif + TQGuardedPtr<TQTimer> t = timer(); + emit pressed(); + if ( t && repeat ) + t->start( AUTO_REPEAT_DELAY, TRUE ); + } +} + +/*! \reimp */ +void TQButton::mouseReleaseEvent( TQMouseEvent *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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::StateChanged ); +#endif + emit released(); + emit clicked(); + } else { + repaint( FALSE ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::StateChanged ); +#endif + if (oldButtonDown) + emit released(); + } +} + +/*! \reimp */ +void TQButton::mouseMoveEvent( TQMouseEvent *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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::StateChanged ); +#endif + emit pressed(); + } + } else { // mouse move outside button + if ( buttonDown ) { + buttonDown = FALSE; + repaint( FALSE ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::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 TQButton::paintEvent( TQPaintEvent *) +{ + TQSharedDoubleBuffer buffer( this ); + drawButton( buffer.painter() ); +} + +/*! \reimp */ +void TQButton::focusInEvent( TQFocusEvent * e) +{ + TQWidget::focusInEvent( e ); +} + +/*! \reimp */ +void TQButton::focusOutEvent( TQFocusEvent * e ) +{ + buttonDown = FALSE; + TQWidget::focusOutEvent( e ); +} + +/*! + Internal slot used for auto repeat. +*/ +void TQButton::autoRepeatTimeout() +{ + if ( mlbDown && isEnabled() && autoRepeat() ) { + TQGuardedPtr<TQTimer> 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 TQButton::animateTimeout() +{ + if ( !animation ) + return; + animation = FALSE; + buttonDown = FALSE; + nextState(); + emit released(); + emit clicked(); +} + + +void TQButton::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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::StateChanged ); +#endif + if ( was != (stat != Off) ) + emit toggled( stat != Off ); + emit stateChanged( stat ); + } +} + +/*! \reimp */ +void TQButton::enabledChange( bool e ) +{ + if ( !isEnabled() ) + setDown( FALSE ); + TQWidget::enabledChange( e ); +} + + +/*! + Toggles the state of a toggle button. + + \sa isOn(), setOn(), toggled(), isToggleButton() +*/ +void TQButton::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 TQButton::setToggleType( ToggleType type ) +{ + toggleTyp = type; + if ( type != Tristate && stat == NoChange ) + setState( On ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + else + TQAccessible::updateAccessibility( this, 0, TQAccessible::StateChanged ); +#endif +} + +bool TQButton::isExclusiveToggle() const +{ +#ifndef QT_NO_BUTTONGROUP + return group() && ( group()->isExclusive() || + group()->isRadioButtonExclusive() && + ::qt_cast<TQRadioButton*>(this) ); +#else + return FALSE; +#endif +} + +#endif diff --git a/src/widgets/qbutton.h b/src/widgets/qbutton.h new file mode 100644 index 000000000..18048683b --- /dev/null +++ b/src/widgets/qbutton.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Definition of TQButton widget class +** +** Created : 940206 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQBUTTON_H +#define TQBUTTON_H + +#ifndef QT_H +#include "qwidget.h" +#include "qkeysequence.h" +#endif // QT_H + +#ifndef QT_NO_BUTTON + + +class TQButtonGroup; +class TQToolBar; +class TQButtonData; + +class Q_EXPORT TQButton : public TQWidget +{ + Q_OBJECT + Q_ENUMS( ToggleType ToggleState ) + Q_PROPERTY( TQString text READ text WRITE setText ) + Q_PROPERTY( TQPixmap pixmap READ pixmap WRITE setPixmap ) + Q_PROPERTY( TQKeySequence 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: + TQButton( TQWidget* parent=0, const char* name=0, WFlags f=0 ); + ~TQButton(); + + TQString text() const; + virtual void setText( const TQString &); + const TQPixmap *pixmap() const; + virtual void setPixmap( const TQPixmap & ); + +#ifndef QT_NO_ACCEL + TQKeySequence accel() const; + virtual void setAccel( const TQKeySequence& ); +#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; + + TQButtonGroup *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 TQPoint &pos ) const; + virtual void drawButton( TQPainter * ); + virtual void drawButtonLabel( TQPainter * ); + + void keyPressEvent( TQKeyEvent *); + void keyReleaseEvent( TQKeyEvent *); + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void paintEvent( TQPaintEvent * ); + void focusInEvent( TQFocusEvent * ); + void focusOutEvent( TQFocusEvent * ); + + void enabledChange( bool ); + +private slots: + void animateTimeout(); + void autoRepeatTimeout(); + void emulateClick(); + +private: + TQString btext; + TQPixmap *bpixmap; + uint toggleTyp : 2; + uint buttonDown : 1; + uint stat : 2; + uint mlbDown : 1; + uint autoresize : 1; + uint animation : 1; + uint repeat : 1; + TQButtonData *d; + + friend class TQButtonGroup; + friend class TQToolBar; + void ensureData(); + virtual void setGroup( TQButtonGroup* ); + TQTimer *timer(); + void nextState(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQButton( const TQButton & ); + TQButton &operator=( const TQButton & ); +#endif +}; + + +inline TQString TQButton::text() const +{ + return btext; +} + +inline const TQPixmap *TQButton::pixmap() const +{ + return bpixmap; +} + +inline bool TQButton::isToggleButton() const +{ + return ToggleType(toggleTyp) != SingleShot; +} + +inline bool TQButton::isDown() const +{ + return buttonDown; +} + +inline bool TQButton::isOn() const +{ + return ToggleState(stat) != Off; +} + +#ifndef QT_NO_COMPAT +inline bool TQButton::autoResize() const +{ + return autoresize; +} +#endif + +inline bool TQButton::autoRepeat() const +{ + return repeat; +} + +inline TQButton::ToggleState TQButton::state() const +{ + return ToggleState(stat); +} + +inline void TQButton::setToggleButton( bool b ) +{ + setToggleType( b ? Toggle : SingleShot ); +} + +inline void TQButton::setOn( bool y ) +{ + setState( y ? On : Off ); +} + +inline TQButton::ToggleType TQButton::toggleType() const +{ + return ToggleType(toggleTyp); +} + + +#endif // QT_NO_BUTTON + +#endif // TQBUTTON_H diff --git a/src/widgets/qbuttongroup.cpp b/src/widgets/qbuttongroup.cpp new file mode 100644 index 000000000..7e89b1dad --- /dev/null +++ b/src/widgets/qbuttongroup.cpp @@ -0,0 +1,682 @@ +/**************************************************************************** +** +** Implementation of TQButtonGroup class +** +** Created : 950130 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qbuttongroup.h" +#ifndef QT_NO_BUTTONGROUP +#include "qbutton.h" +#include "qptrlist.h" +#include "qapplication.h" +#include "qradiobutton.h" + + + +/*! + \class TQButtonGroup qbuttongroup.h + \brief The TQButtonGroup widget organizes TQButton 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 TQButtonGroup can display a frame + and a title because it inherits TQGroupBox. + \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 TQPushButton, TQCheckBox, TQRadioButton +*/ + +/*! + \property TQButtonGroup::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 TQButtonGroup::radioButtonExclusive + \brief whether the radio buttons in the group are exclusive + + If this property is TRUE (the default), the \link TQRadioButton + radiobuttons\endlink in the group are treated exclusively. +*/ + +struct TQButtonItem +{ + TQButton *button; + int id; +}; + + +class TQButtonList: public TQPtrList<TQButtonItem> +{ +public: + TQButtonList() {} + ~TQButtonList() {} +}; + + +typedef TQPtrListIterator<TQButtonItem> TQButtonListIt; + + +/*! + Constructs a button group with no title. + + The \a parent and \a name arguments are passed to the TQWidget + constructor. +*/ + +TQButtonGroup::TQButtonGroup( TQWidget *parent, const char *name ) + : TQGroupBox( parent, name ) +{ + init(); +} + +/*! + Constructs a button group with the title \a title. + + The \a parent and \a name arguments are passed to the TQWidget + constructor. +*/ + +TQButtonGroup::TQButtonGroup( const TQString &title, TQWidget *parent, + const char *name ) + : TQGroupBox( 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 TQWidget + constructor. +*/ + +TQButtonGroup::TQButtonGroup( int strips, Orientation orientation, + TQWidget *parent, const char *name ) + : TQGroupBox( 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 TQWidget + constructor. +*/ + +TQButtonGroup::TQButtonGroup( int strips, Orientation orientation, + const TQString &title, TQWidget *parent, + const char *name ) + : TQGroupBox( strips, orientation, title, parent, name ) +{ + init(); +} + +/*! + Initializes the button group. +*/ + +void TQButtonGroup::init() +{ + buttons = new TQButtonList; + Q_CHECK_PTR( buttons ); + buttons->setAutoDelete( TRUE ); + excl_grp = FALSE; + radio_excl = TRUE; +} + +/*! \reimp */ + +TQButtonGroup::~TQButtonGroup() +{ + TQButtonList * tmp = buttons; + TQButtonItem *bi = tmp->first(); + buttons = 0; + while( bi ) { + bi->button->setGroup(0); + bi = tmp->next(); + } + delete tmp; +} + +bool TQButtonGroup::isExclusive() const +{ + return excl_grp; +} + +void TQButtonGroup::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 TQButtonGroup::insert( TQButton *button, int id ) +{ + if ( button->group() ) + button->group()->remove( button ); + + static int seq_no = -2; + TQButtonItem *bi = new TQButtonItem; + 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 TQButtonGroup::count() const +{ + return buttons->count(); +} + +/*! + Removes the \a button from the button group. + + \sa insert() +*/ + +void TQButtonGroup::remove( TQButton *button ) +{ + if ( !buttons ) + return; + + TQButtonListIt it( *buttons ); + TQButtonItem *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. +*/ + +TQButton *TQButtonGroup::find( int id ) const +{ + // introduce a TQButtonListIt if calling anything + for ( TQButtonItem *i=buttons->first(); i; i=buttons->next() ) + if ( i->id == id ) + return i->button; + return 0; +} + + +/*! + \fn void TQButtonGroup::pressed( int id ) + + This signal is emitted when a button in the group is \link + TQButton::pressed() pressed\endlink. The \a id argument is the + button's identifier. + + \sa insert() +*/ + +/*! + \fn void TQButtonGroup::released( int id ) + + This signal is emitted when a button in the group is \link + TQButton::released() released\endlink. The \a id argument is the + button's identifier. + + \sa insert() +*/ + +/*! + \fn void TQButtonGroup::clicked( int id ) + + This signal is emitted when a button in the group is \link + TQButton::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 + TQButton::pressed() signal. +*/ + +void TQButtonGroup::buttonPressed() +{ + // introduce a TQButtonListIt if calling anything + int id = -1; + TQButton *bt = (TQButton *)sender(); // object that sent the signal + for ( register TQButtonItem *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 + TQButton::released() signal. +*/ + +void TQButtonGroup::buttonReleased() +{ + // introduce a TQButtonListIt if calling anything + int id = -1; + TQButton *bt = (TQButton *)sender(); // object that sent the signal + for ( register TQButtonItem *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 + TQButton::clicked() signal. +*/ + +void TQButtonGroup::buttonClicked() +{ + // introduce a TQButtonListIt if calling anything + int id = -1; + TQButton *bt = ::qt_cast<TQButton*>(sender()); // object that sent the signal +#if defined(QT_CHECK_NULL) + Q_ASSERT( bt ); +#endif + for ( register TQButtonItem *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 + TQButton::toggled() signal. +*/ + +void TQButtonGroup::buttonToggled( bool on ) +{ + // introduce a TQButtonListIt if calling anything + if ( !on || !excl_grp && !radio_excl ) + return; + TQButton *bt = ::qt_cast<TQButton*>(sender()); // object that sent the signal +#if defined(QT_CHECK_NULL) + Q_ASSERT( bt ); + Q_ASSERT( bt->isToggleButton() ); +#endif + + if ( !excl_grp && !::qt_cast<TQRadioButton*>(bt) ) + return; + TQButtonItem * i = buttons->first(); + bool hasTabFocus = FALSE; + + while( i != 0 && hasTabFocus == FALSE ) { + if ( ( excl_grp || ::qt_cast<TQRadioButton*>(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<TQRadioButton*>(i->button) ) ) + i->button->setOn( FALSE ); + if ( ( excl_grp || ::qt_cast<TQRadioButton*>(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 TQButtonGroup::setButton( int id ) +{ + TQButton * b = find( id ); + if ( b ) + b->setOn( TRUE ); +} + +void TQButtonGroup::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 TQButtonGroup::moveFocus( int key ) +{ + TQWidget * f = qApp->focusWidget(); + + TQButtonItem * i; + i = buttons->first(); + while( i && i->button != f ) + i = buttons->next(); + + if ( !i || !i->button ) + return; + + TQWidget * candidate = 0; + int bestScore = -1; + + TQPoint goal( f->mapToGlobal( f->geometry().center() ) ); + + i = buttons->first(); + while( i && i->button ) { + if ( i->button != f && + i->button->isEnabled() ) { + TQPoint 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 ( TQABS( p.x() - goal.x() ) < TQABS( 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 ( TQABS( p.x() - goal.x() ) < TQABS( 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 ( TQABS( p.y() - goal.y() ) < TQABS( 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 ( TQABS( p.y() - goal.y() ) < TQABS( 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(); + } + + TQButton *buttoncand = ::qt_cast<TQButton*>(candidate); + if ( buttoncand && ::qt_cast<TQButton*>(f) && + ((TQButton*)f)->isOn() && + buttoncand->isToggleButton() && + ( isExclusive() || ( ::qt_cast<TQRadioButton*>(f) && + ::qt_cast<TQRadioButton*>(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) + TQFocusEvent::setReason(TQFocusEvent::Backtab); + else + TQFocusEvent::setReason(TQFocusEvent::Tab); + candidate->setFocus(); + TQFocusEvent::resetReason(); + } +} + + +/*! + Returns the selected toggle button if exactly one is selected; + otherwise returns 0. + + \sa selectedId() +*/ + +TQButton * TQButtonGroup::selected() const +{ + if ( !buttons ) + return 0; + TQButtonListIt it( *buttons ); + TQButtonItem *i; + TQButton *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 TQButtonGroup::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 TQButtonGroup::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 TQButtonGroup::id( TQButton * button ) const +{ + TQButtonItem *i = buttons->first(); + while ( i && i->button != button ) + i = buttons->next(); + return i ? i->id : -1; +} + + +/*! + \reimp +*/ +bool TQButtonGroup::event( TQEvent * e ) +{ + if ( e->type() == TQEvent::ChildInserted ) { + TQChildEvent * ce = (TQChildEvent *) e; + if ( radio_excl && ::qt_cast<TQRadioButton*>(ce->child()) ) { + TQButton * button = (TQButton *) ce->child(); + if ( button->isToggleButton() && !button->isOn() && + selected() && (selected()->focusPolicy() & TabFocus) != 0 ) + button->setFocusPolicy( (FocusPolicy)(button->focusPolicy() & + ~TabFocus) ); + } + } + return TQGroupBox::event( e ); +} +#endif diff --git a/src/widgets/qbuttongroup.h b/src/widgets/qbuttongroup.h new file mode 100644 index 000000000..fa0b389e9 --- /dev/null +++ b/src/widgets/qbuttongroup.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Definition of TQButtonGroup class +** +** Created : 950130 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQBUTTONGROUP_H +#define TQBUTTONGROUP_H + +#ifndef QT_H +#include "qgroupbox.h" +#endif // QT_H + +#ifndef QT_NO_BUTTONGROUP + + +class TQButton; +class TQButtonList; + + +class Q_EXPORT TQButtonGroup : public TQGroupBox +{ + 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: + TQButtonGroup( TQWidget* parent=0, const char* name=0 ); + TQButtonGroup( const TQString &title, + TQWidget* parent=0, const char* name=0 ); + TQButtonGroup( int columns, Orientation o, + TQWidget* parent=0, const char* name=0 ); + TQButtonGroup( int columns, Orientation o, const TQString &title, + TQWidget* parent=0, const char* name=0 ); + ~TQButtonGroup(); + + bool isExclusive() const; + bool isRadioButtonExclusive() const { return radio_excl; } + virtual void setExclusive( bool ); + virtual void setRadioButtonExclusive( bool ); + +public: + int insert( TQButton *, int id=-1 ); + void remove( TQButton * ); + TQButton *find( int id ) const; + int id( TQButton * ) const; + int count() const; + + virtual void setButton( int id ); + + virtual void moveFocus( int ); + + TQButton *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( TQEvent * e ); + +private: + void init(); + bool excl_grp; + bool radio_excl; + TQButtonList *buttons; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQButtonGroup( const TQButtonGroup & ); + TQButtonGroup &operator=( const TQButtonGroup & ); +#endif +}; + + +#endif // QT_NO_BUTTONGROUP + +#endif // TQBUTTONGROUP_H diff --git a/src/widgets/qcheckbox.cpp b/src/widgets/qcheckbox.cpp new file mode 100644 index 000000000..91c8ed424 --- /dev/null +++ b/src/widgets/qcheckbox.cpp @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** Implementation of TQCheckBox class +** +** Created : 940222 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQCheckBox qcheckbox.h + \brief The TQCheckBox widget provides a checkbox with a text label. + + \ingroup basic + \mainclass + + TQCheckBox and TQRadioButton 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 TQButtonGroup 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, TQCheckBox + 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 TQPushButton, 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 TQButton TQRadioButton + \link guibooks.html#fowler Fowler: Check Box \endlink +*/ + +/*! + \property TQCheckBox::checked + \brief whether the checkbox is checked + + The default is unchecked, i.e. FALSE. +*/ + +/*! + \property TQCheckBox::autoMask + \brief whether the checkbox is automatically masked + + \sa TQWidget::setAutoMask() +*/ + +/*! + \property TQCheckBox::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 TQWidget + constructor. +*/ + +TQCheckBox::TQCheckBox( TQWidget *parent, const char *name ) + : TQButton( parent, name, WNoAutoErase | WMouseNoMask ) +{ + setToggleButton( TRUE ); + setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ) ); +} + +/*! + Constructs a checkbox with text \a text. + + The \a parent and \a name arguments are sent to the TQWidget + constructor. +*/ + +TQCheckBox::TQCheckBox( const TQString &text, TQWidget *parent, const char *name ) + : TQButton( parent, name, WNoAutoErase | WMouseNoMask ) +{ + setText( text ); + setToggleButton( TRUE ); + setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ) ); +} + +/*! + Sets the checkbox to the "no change" state. + + \sa setTristate() +*/ +void TQCheckBox::setNoChange() +{ + setTristate(TRUE); + setState( NoChange ); +} + +void TQCheckBox::setTristate(bool y) +{ + setToggleType( y ? Tristate : Toggle ); +} + +bool TQCheckBox::isTristate() const +{ + return toggleType() == Tristate; +} + + +/*!\reimp +*/ +TQSize TQCheckBox::sizeHint() const +{ + // NB: TQRadioButton::sizeHint() is similar + constPolish(); + + TQPainter p(this); + TQSize sz = style().itemRect(&p, TQRect(0, 0, 1, 1), ShowPrefix, FALSE, + pixmap(), text()).size(); + + return (style().sizeFromContents(TQStyle::CT_CheckBox, this, sz). + expandedTo(TQApplication::globalStrut())); +} + + +/*!\reimp +*/ + +void TQCheckBox::drawButton( TQPainter *paint ) +{ + TQPainter *p = paint; + TQRect irect = TQStyle::visualRect( style().subRect(TQStyle::SR_CheckBoxIndicator, this), this ); + const TQColorGroup &cg = colorGroup(); + +#if !defined( QT_NO_TEXTSTREAM ) && !defined( Q_WS_MACX ) +# define SAVE_CHECKBOX_PIXMAPS +#endif +#if defined(SAVE_CHECKBOX_PIXMAPS) + TQString 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; + TQTextOStream os(&pmkey); + os << "$qt_check_" << style().className() << "_" + << palette().serialNumber() << "_" << irect.width() << "x" << irect.height() << "_" << kf; + TQPixmap *pm = TQPixmapCache::find( pmkey ); + if ( pm ) { // pixmap exists + p->drawPixmap( irect.topLeft(), *pm ); + drawButtonLabel( p ); + return; + } + bool use_pm = TRUE; + TQPainter pmpaint; + int wx = 0, wy = 0; + if ( use_pm ) { + pm = new TQPixmap( irect.size() ); // create new pixmap + Q_CHECK_PTR( pm ); + pm->fill( cg.background() ); + TQPainter::redirect(this, pm); + pmpaint.begin(this); + p = &pmpaint; // draw in pixmap + wx = irect.x(); // save x,y coords + wy = irect.y(); + irect.moveTopLeft(TQPoint(0, 0)); + p->setBackgroundColor( cg.background() ); + } +#endif + + TQStyle::SFlags flags = TQStyle::Style_Default; + if ( isEnabled() ) + flags |= TQStyle::Style_Enabled; + if ( hasFocus() ) + flags |= TQStyle::Style_HasFocus; + if ( isDown() ) + flags |= TQStyle::Style_Down; + if ( hasMouse() ) + flags |= TQStyle::Style_MouseOver; + if ( state() == TQButton::On ) + flags |= TQStyle::Style_On; + else if ( state() == TQButton::Off ) + flags |= TQStyle::Style_Off; + else if ( state() == TQButton::NoChange ) + flags |= TQStyle::Style_NoChange; + + style().drawControl(TQStyle::CE_CheckBox, p, this, irect, cg, flags); + +#if defined(SAVE_CHECKBOX_PIXMAPS) + if ( use_pm ) { + pmpaint.end(); + TQPainter::redirect( this, 0 ); + if ( backgroundPixmap() || backgroundMode() == X11ParentRelative ) { + TQBitmap bm( pm->size() ); + bm.fill( color0 ); + pmpaint.begin( &bm ); + style().drawControlMask(TQStyle::CE_CheckBox, &pmpaint, this, irect); + pmpaint.end(); + pm->setMask( bm ); + } + p = paint; // draw in default device + p->drawPixmap( wx, wy, *pm ); + if (!TQPixmapCache::insert(pmkey, pm) ) // save in cache + delete pm; + } +#endif + + drawButtonLabel( paint ); +} + + +/*!\reimp +*/ +void TQCheckBox::drawButtonLabel( TQPainter *p ) +{ + TQRect r = + TQStyle::visualRect( style().subRect(TQStyle::SR_CheckBoxContents, this), this ); + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + if (isDown()) + flags |= TQStyle::Style_Down; + if (state() == TQButton::On) + flags |= TQStyle::Style_On; + else if (state() == TQButton::Off) + flags |= TQStyle::Style_Off; + else if (state() == TQButton::NoChange) + flags |= TQStyle::Style_NoChange; + + style().drawControl(TQStyle::CE_CheckBoxLabel, p, this, r, colorGroup(), flags); +} + +/*! + \reimp +*/ +void TQCheckBox::resizeEvent( TQResizeEvent *e ) +{ + TQButton::resizeEvent(e); + if ( isVisible() ) { + TQPainter p(this); + TQSize isz = style().itemRect(&p, TQRect(0, 0, 1, 1), ShowPrefix, FALSE, + pixmap(), text()).size(); + TQSize wsz = (style().sizeFromContents(TQStyle::CT_CheckBox, this, isz). + expandedTo(TQApplication::globalStrut())); + + update(wsz.width(), isz.width(), 0, wsz.height()); + } + if (autoMask()) + updateMask(); +} + +/*! + \reimp +*/ +void TQCheckBox::updateMask() +{ + TQRect irect = + TQStyle::visualRect( style().subRect(TQStyle::SR_CheckBoxIndicator, this), this ); + + TQBitmap bm(width(), height()); + bm.fill(color0); + + TQPainter p( &bm, this ); + style().drawControlMask(TQStyle::CE_CheckBox, &p, this, irect); + if ( ! text().isNull() || ( pixmap() && ! pixmap()->isNull() ) ) { + TQRect crect = + TQStyle::visualRect( style().subRect( TQStyle::SR_CheckBoxContents, + this ), this ); + TQRect frect = + TQStyle::visualRect( style().subRect( TQStyle::SR_CheckBoxFocusRect, + this ), this ); + TQRect label(crect.unite(frect)); + p.fillRect(label, color1); + } + p.end(); + + setMask(bm); +} + +/*!\reimp*/ +bool TQCheckBox::hitButton( const TQPoint &pos ) const +{ + TQRect r = TQStyle::visualRect( style().subRect( TQStyle::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 000000000..dfb5217b4 --- /dev/null +++ b/src/widgets/qcheckbox.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Definition of TQCheckBox class +** +** Created : 940222 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQCHECKBOX_H +#define TQCHECKBOX_H + +#ifndef QT_H +#include "qbutton.h" +#endif // QT_H + +#ifndef QT_NO_CHECKBOX + +class Q_EXPORT TQCheckBox : public TQButton +{ + 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: + TQCheckBox( TQWidget *parent, const char* name=0 ); + TQCheckBox( const TQString &text, TQWidget *parent, const char* name=0 ); + + bool isChecked() const; + + void setNoChange(); + + void setTristate(bool y=TRUE); + bool isTristate() const; + + TQSize sizeHint() const; + +public slots: + void setChecked( bool check ); + +protected: + void resizeEvent( TQResizeEvent* ); + void drawButton( TQPainter * ); + void drawButtonLabel( TQPainter * ); + void updateMask(); + bool hitButton( const TQPoint &pos ) const; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQCheckBox( const TQCheckBox & ); + TQCheckBox &operator=( const TQCheckBox & ); +#endif +}; + + +inline bool TQCheckBox::isChecked() const +{ return isOn(); } + +inline void TQCheckBox::setChecked( bool check ) +{ setOn( check ); } + + +#endif // QT_NO_CHECKBOX + +#endif // TQCHECKBOX_H diff --git a/src/widgets/qcombobox.cpp b/src/widgets/qcombobox.cpp new file mode 100644 index 000000000..8923192af --- /dev/null +++ b/src/widgets/qcombobox.cpp @@ -0,0 +1,2334 @@ +/********************************************************************** +** +** Implementation of TQComboBox widget class +** +** Created : 940426 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQComboBox qcombobox.h + \brief The TQComboBox 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. + + TQComboBox 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. TQComboBox provides both. + + TQComboBox provides two different constructors. The simplest + constructor creates an "old-style" combobox in Motif (or Aqua) + style: + \code + TQComboBox *c = new TQComboBox( 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 + TQComboBox *c1 = new TQComboBox( FALSE, this, "read-only combobox" ); + TQComboBox *c2 = new TQComboBox( 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 + TQString 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 TQValidator; 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, TQComboBox will use a TQListBox or a + TQPopupMenu to display the list of items. See setListBox() for + more information. + + \sa TQLineEdit TQListBox TQSpinBox TQRadioButton TQButtonGroup + \link guibooks.html#fowler GUI Design Handbook: Combo Box,\endlink + \link guibooks.html#fowler GUI Design Handbook: Drop-Down List Box.\endlink +*/ + + +/*! + \enum TQComboBox::Policy + + This enum specifies what the TQComboBox 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 TQComboBox::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 TQComboBox::activated( const TQString &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 TQComboBox::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 TQComboBox::highlighted( const TQString &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 TQComboBox::textChanged( const TQString &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 TQComboBox::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 TQComboBox::autoMask + \brief whether the combobox is automatically masked + + \sa TQWidget::setAutoMask() +*/ + +/*! \property TQComboBox::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 TQComboBox::count + \brief the number of items in the combobox +*/ + +/*! + \property TQComboBox::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 TQComboBox::currentText + \brief the text of the combobox's current item +*/ + +/*! + \property TQComboBox::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 TQComboBox::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 TQComboBox::insertionPolicy + \brief the position of the items inserted by the user + + The default insertion policy is \c AtBottom. See \l Policy. +*/ + +/*! + \property TQComboBox::maxCount + \brief the maximum number of items allowed in the combobox +*/ + +/*! + \property TQComboBox::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 TQComboBoxPopup : public TQPopupMenu +{ +public: + TQComboBoxPopup( TQWidget *parent=0, const char *name=0 ) + : TQPopupMenu( parent, name ) + { + } + + int itemHeight( int index ) + { + return TQPopupMenu::itemHeight( index ); + } +}; + +static inline TQString escapedComboString(const TQString &str) +{ + TQString stringToReturn = str; + return stringToReturn.replace('&', "&&"); +} + +class TQComboBoxPopupItem : public TQCustomMenuItem +{ + TQListBoxItem *li; + TQSize sc; // Size cache optimization +public: + TQComboBoxPopupItem(TQListBoxItem *i) : TQCustomMenuItem(), li(i), sc(0, 0) { } + virtual bool fullSpan() const { return TRUE; } + virtual void paint( TQPainter*, const TQColorGroup&, bool, bool, int, int, int, int); + virtual TQSize sizeHint() { if (sc.isNull()) sc = TQSize(li->width(li->listBox()), TQMAX(25, li->height(li->listBox()))); return sc; } +}; +void TQComboBoxPopupItem::paint( TQPainter* p, const TQColorGroup&, 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 TQComboBoxData +{ +public: + TQComboBoxData( TQComboBox *cb ): ed( 0 ), usingLBox( FALSE ), pop( 0 ), lBox( 0 ), combo( cb ) + { + duplicatesEnabled = TRUE; + cb->setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ) ); + } + + inline bool usingListBox() { return usingLBox; } + inline TQListBox * listBox() { return lBox; } + inline TQComboBoxPopup * popup() { return pop; } + void updateLinedGeometry(); + void setListBox( TQListBox *l ); + void setPopupMenu( TQComboBoxPopup * pm, bool isPopup=TRUE ); + + int current; + int maxCount; + int sizeLimit; + TQComboBox::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; + + TQLineEdit * ed; // /bin/ed rules! + TQTimer *completionTimer; + + TQSize sizeHint; + +private: + bool usingLBox; + TQComboBoxPopup *pop; + TQListBox *lBox; + TQComboBox *combo; + +}; + +void TQComboBoxData::updateLinedGeometry() +{ + if ( !ed || !combo ) + return; + TQRect r = TQStyle::visualRect( combo->style().querySubControlMetrics(TQStyle::CC_ComboBox, combo, + TQStyle::SC_ComboBoxEditField), combo ); + + const TQPixmap *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 TQComboBoxData::setListBox( TQListBox *l ) +{ + lBox = l; + usingLBox = TRUE; + l->setMouseTracking( TRUE ); +#ifdef Q_WS_X11 + l->x11SetWindowType( TQWidget::X11WindowTypeCombo ); + l->x11SetWindowTransient( combo->topLevelWidget()); +#endif +} + +void TQComboBoxData::setPopupMenu( TQComboBoxPopup * pm, bool isPopup ) +{ + pop = pm; + if(isPopup) + usingLBox = FALSE; +#ifdef Q_WS_X11 + if( pm ) { + pm->x11SetWindowType( TQWidget::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( "TQComboBox::%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( "TQComboBox::%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 TQComboBox, then + the pixmap() function will always return 0. To workaround this, + use the other constructor. + +*/ + + + +TQComboBox::TQComboBox( TQWidget *parent, const char *name ) + : TQWidget( parent, name, WNoAutoErase ) +{ + d = new TQComboBoxData( this ); + if ( style().styleHint(TQStyle::SH_ComboBox_Popup, this) || + style().styleHint(TQStyle::SH_GUIStyle) == TQt::MotifStyle ) { + d->setPopupMenu( new TQComboBoxPopup( 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 TQTimer( 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 TQWidget + constructor. +*/ + + +TQComboBox::TQComboBox( bool rw, TQWidget *parent, const char *name ) + : TQWidget( parent, name, WNoAutoErase ) +{ + d = new TQComboBoxData( this ); + setUpListBox(); + + if(d->popup() && style().styleHint(TQStyle::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 TQTimer( this ); + + setFocusPolicy( StrongFocus ); + + d->ed = 0; + if ( rw ) + setUpLineEdit(); + setBackgroundMode( PaletteButton, PaletteBase ); +} + + + +/*! + Destroys the combobox. +*/ + +TQComboBox::~TQComboBox() +{ + delete d; +} + +void TQComboBox::setDuplicatesEnabled( bool enable ) +{ + d->duplicatesEnabled = enable; +} + +bool TQComboBox::duplicatesEnabled() const +{ + return d->duplicatesEnabled; +} + +int TQComboBox::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 TQComboBox::insertStrList( const TQStrList &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 TQComboBox::insertStrList( const TQStrList *list, int index ) +{ + if ( !list ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( list != 0 ); +#endif + return; + } + TQStrListIterator it( *list ); + const char* tmp; + if ( index < 0 ) + index = count(); + while ( (tmp=it.current()) ) { + ++it; + if ( d->usingListBox() ) + d->listBox()->insertItem( TQString::fromLatin1(tmp), index ); + else + d->popup()->insertItem( escapedComboString(TQString::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 TQComboBox::insertStringList( const TQStringList &list, int index ) +{ + TQStringList::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 TQComboBox::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( TQString::fromLatin1(strings[i]), index ); + else + d->popup()->insertItem( escapedComboString(TQString::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 TQComboBox::insertItem( const TQString &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 TQComboBox::insertItem( const TQPixmap &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 TQComboBox::insertItem( const TQPixmap &pixmap, const TQString& 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 TQComboBox::removeItem( int index ) +{ + int cnt = count(); + if ( !checkIndex( "removeItem", name(), cnt, index ) ) + return; + if ( d->usingListBox() ) { + if ( style().styleHint(TQStyle::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 ) { + TQString s = TQString::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 TQComboBox::clear() +{ + if ( d->usingListBox() ) { + if ( style().styleHint(TQStyle::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(TQStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(d->current, FALSE); + d->current = 0; + if ( d->ed ) { + d->ed->setText( TQString::fromLatin1("") ); + d->updateLinedGeometry(); + } + currentChanged(); +} + + +TQString TQComboBox::currentText() const +{ + if ( d->ed ) + return d->ed->text(); + else if ( d->current < count() ) + return text( currentItem() ); + else + return TQString::null; +} + +void TQComboBox::setCurrentText( const TQString& 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 TQString::null if + the item is not a string. + + \sa currentText() +*/ + +TQString TQComboBox::text( int index ) const +{ + if ( !checkIndex( "text", name(), count(), index ) ) + return TQString::null; + if ( d->usingListBox() ) { + return d->listBox()->text( index ); + } else { + TQString 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 TQPixmap *TQComboBox::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 TQComboBox::changeItem( const TQString &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 TQComboBox::changeItem( const TQPixmap &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 TQComboBox::changeItem( const TQPixmap &im, const TQString &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 TQComboBox::currentItem() const +{ + return d->current; +} + +void TQComboBox::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(TQStyle::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 TQComboBox::autoResize() const +{ + return d->autoresize; +} + +void TQComboBox::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(). +*/ +TQSize TQComboBox::sizeHint() const +{ + if ( isVisible() && d->sizeHint.isValid() ) + return d->sizeHint; + + constPolish(); + int i, w; + TQFontMetrics fm = fontMetrics(); + + int maxW = count() ? 18 : 7 * fm.width(TQChar('x')) + 18; + int maxH = TQMAX( 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(TQStyle::CT_ComboBox, this, + TQSize(maxW, maxH)). + expandedTo(TQApplication::globalStrut())); + + return d->sizeHint; +} + + +/*! + \internal + Receives activated signals from an internal popup list and emits + the activated() signal. +*/ + +void TQComboBox::internalActivate( int index ) +{ + if ( d->current != index ) { + if ( !d->usingListBox() || listBox()->item( index )->isSelectable() ) { + if(d->popup() && style().styleHint(TQStyle::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; + + TQString 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 TQComboBox::internalHighlight( int index ) +{ + emit highlighted( index ); + TQString 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 TQComboBox::internalClickTimeout() +{ + d->shortClick = FALSE; +} + +/*! + Sets the palette for both the combobox button and the combobox + popup list to \a palette. +*/ + +void TQComboBox::setPalette( const TQPalette &palette ) +{ + TQWidget::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 TQComboBox::setFont( const TQFont &font ) +{ + d->sizeHint = TQSize(); // invalidate size hint + TQWidget::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 TQComboBox::resizeEvent( TQResizeEvent * e ) +{ + if ( d->ed ) + d->updateLinedGeometry(); + if ( d->listBox() ) + d->listBox()->resize( width(), d->listBox()->height() ); + TQWidget::resizeEvent( e ); +} + +/*!\reimp +*/ + +void TQComboBox::paintEvent( TQPaintEvent * ) +{ + TQPainter p( this ); + const TQColorGroup & g = colorGroup(); + p.setPen(g.text()); + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + + if ( width() < 5 || height() < 5 ) { + qDrawShadePanel( &p, rect(), g, FALSE, 2, + &g.brush( TQColorGroup::Button ) ); + return; + } + + bool reverse = TQApplication::reverseLayout(); + if ( !d->usingListBox() && + style().styleHint(TQStyle::SH_GUIStyle) == TQt::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(TQStyle::PM_DefaultFrameWidth, this), + &g.brush( TQColorGroup::Button ) ); + qDrawShadePanel( &p, xPos, (height() - buttonH)/2, + buttonW, buttonH, g, FALSE, + style().pixelMetric(TQStyle::PM_DefaultFrameWidth, this) ); + TQRect clip( x0, 2, w - 2 - 4 - 5, height() - 4 ); + TQString str = d->popup()->text( this->d->current ); + if ( !str.isNull() ) { + p.drawText( clip, AlignCenter | SingleLine, str ); + } + + TQPixmap *pix = d->popup()->pixmap( this->d->current ); + TQIconSet *iconSet = d->popup()->iconSet( this->d->current ); + if (pix || iconSet) { + TQPixmap 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( TQStyle::CC_ComboBox, &p, this, rect(), g, + flags, (uint)TQStyle::SC_All, + (d->arrowDown ? + TQStyle::SC_ComboBoxArrow : + TQStyle::SC_None )); + + TQRect re = style().querySubControlMetrics( TQStyle::CC_ComboBox, this, + TQStyle::SC_ComboBoxEditField ); + re = TQStyle::visualRect(re, this); + p.setClipRect( re ); + + TQString str = d->popup()->text( this->d->current ); + TQPixmap *pix = d->popup()->pixmap( this->d->current ); + if ( !str.isNull() ) { + p.save(); + p.setFont(font()); + TQFontMetrics 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( TQColorGroup::Base ) ); + p.drawPixmap( re.x() + 2, re.y() + + ( re.height() - pix->height() ) / 2, *pix ); + } + } else { + style().drawComplexControl( TQStyle::CC_ComboBox, &p, this, rect(), g, + flags, (uint)TQStyle::SC_All, + (d->arrowDown ? + TQStyle::SC_ComboBoxArrow : + TQStyle::SC_None )); + + TQRect re = style().querySubControlMetrics( TQStyle::CC_ComboBox, this, + TQStyle::SC_ComboBoxEditField ); + re = TQStyle::visualRect(re, this); + p.setClipRect( re ); + + if ( !d->ed ) { + TQListBoxItem * 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 ) ) { + TQListBoxItem * item = d->listBox()->item( d->current ); + const TQPixmap *pix = item->pixmap(); + if ( pix ) { + p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(), + colorGroup().brush( TQColorGroup::Base ) ); + p.drawPixmap( re.x() + 2, re.y() + + ( re.height() - pix->height() ) / 2, *pix ); + } + } + p.setClipping( FALSE ); + } +} + + +/*!\reimp +*/ + +void TQComboBox::mousePressEvent( TQMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + if ( d->discardNextMousePress ) { + d->discardNextMousePress = FALSE; + return; + } + TQRect arrowRect = style().querySubControlMetrics( TQStyle::CC_ComboBox, this, + TQStyle::SC_ComboBoxArrow); + arrowRect = TQStyle::visualRect(arrowRect, this); + + // Correction for motif style, where arrow is smaller + // and thus has a rect that doesn't fit the button. + arrowRect.setHeight( TQMAX( 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(); + } + TQTimer::singleShot( 200, this, SLOT(internalClickTimeout())); + d->shortClick = TRUE; + } +} + +/*!\reimp +*/ + +void TQComboBox::mouseMoveEvent( TQMouseEvent * ) +{ +} + +/*!\reimp +*/ + +void TQComboBox::mouseReleaseEvent( TQMouseEvent * ) +{ +} + +/*!\reimp +*/ + +void TQComboBox::mouseDoubleClickEvent( TQMouseEvent *e ) +{ + mousePressEvent( e ); +} + + +/*!\reimp +*/ + +void TQComboBox::keyPressEvent( TQKeyEvent *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(); + TQString 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 TQComboBox::focusInEvent( TQFocusEvent * e ) +{ + TQWidget::focusInEvent( e ); + d->completeNow = FALSE; + d->completeAt = 0; +} + +/*!\reimp +*/ + +void TQComboBox::focusOutEvent( TQFocusEvent * e ) +{ + TQWidget::focusOutEvent( e ); + d->completeNow = FALSE; + d->completeAt = 0; +} + +/*!\reimp +*/ +#ifndef QT_NO_WHEELEVENT +void TQComboBox::wheelEvent( TQWheelEvent *e ) +{ + if ( d->poppedUp ) { + if ( d->usingListBox() ) { + TQApplication::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( TQListBox *l, int sl ) +{ + if ( l->count() > 0 ) + return TQMIN( 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 TQComboBox::popup() +{ + if ( !count() || d->poppedUp ) + return; + + if( !d->usingListBox() || style().styleHint(TQStyle::SH_ComboBox_Popup, this) ) { + if(d->usingListBox()) { + if(!d->popup()) { + TQComboBoxPopup *p = new TQComboBoxPopup( 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++) { + TQListBoxItem *item = d->listBox()->item(i); + if(item->rtti() == TQListBoxText::RTTI) { + d->popup()->insertItem(escapedComboString(item->text()), i, i); + } else if(item->rtti() == TQListBoxPixmap::RTTI) { + if(item->pixmap()) + d->popup()->insertItem(TQIconSet(*item->pixmap()), escapedComboString(item->text()), i, i); + else + d->popup()->insertItem(escapedComboString(item->text()), i, i); + } else { + d->popup()->insertItem(new TQComboBoxPopupItem(item), i, i); + } + } + } + d->popup()->installEventFilter( this ); + if(d->popup() && style().styleHint(TQStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(this->d->current, TRUE); + d->popup()->popup( mapToGlobal( TQPoint(0,0) ), this->d->current ); + update(); + } else { + // Send all listbox events to eventFilter(): + TQListBox* 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; + TQRect screen = TQApplication::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 + TQPoint pos = mapToGlobal( TQPoint(0,height()) ); + // ## Similar code is in TQPopupMenu + 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(); + + TQRect rect = + style().querySubControlMetrics( TQStyle::CC_ComboBox, this, + TQStyle::SC_ComboBoxListBoxPopup, + TQStyleOption( 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 ); + TQListBoxItem* 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(TQScrollView::Auto); + +#ifndef QT_NO_EFFECTS + if ( TQApplication::isEffectEnabled( UI_AnimateCombo ) ) { + if ( lb->y() < mapToGlobal(TQPoint(0,0)).y() ) + qScrollEffect( lb, TQEffects::UpScroll ); + else + qScrollEffect( lb ); + } else +#endif + lb->show(); + } + d->poppedUp = TRUE; +} + + +/*! + \reimp +*/ +void TQComboBox::updateMask() +{ + TQBitmap bm( size() ); + bm.fill( color0 ); + + { + TQPainter p( &bm, this ); + style().drawComplexControlMask(TQStyle::CC_ComboBox, &p, this, rect()); + } + + setMask( bm ); +} + +/*! + \internal + Pops down (removes) the combobox popup list box. +*/ +void TQComboBox::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 TQComboBox::reIndex() +{ + if ( !d->usingListBox() ) { + int cnt = count(); + while ( cnt-- ) + d->popup()->setId( cnt, cnt ); + } +} + +/*! + \internal + Repaints the combobox. +*/ + +void TQComboBox::currentChanged() +{ + if ( d->autoresize ) + adjustSize(); + update(); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::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 TQComboBox::eventFilter( TQObject *object, TQEvent *event ) +{ + if ( !event ) + return TRUE; + else if ( object == d->ed ) { + if ( event->type() == TQEvent::KeyPress ) { + bool isAccepted = ( (TQKeyEvent*)event )->isAccepted(); + keyPressEvent( (TQKeyEvent *)event ); + if ( ((TQKeyEvent *)event)->isAccepted() ) { + d->completeNow = FALSE; + return TRUE; + } else if ( ((TQKeyEvent *)event)->key() != Key_End ) { + d->completeNow = TRUE; + d->completeAt = d->ed->cursorPosition(); + } + if ( isAccepted ) + ( (TQKeyEvent*)event )->accept(); + else + ( (TQKeyEvent*)event )->ignore(); + } else if ( event->type() == TQEvent::KeyRelease ) { + keyReleaseEvent( (TQKeyEvent *)event ); + return ((TQKeyEvent *)event)->isAccepted(); + } else if ( event->type() == TQEvent::FocusIn ) { + focusInEvent( (TQFocusEvent *)event ); + } else if ( event->type() == TQEvent::FocusOut ) { + focusOutEvent( (TQFocusEvent *)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() ) { + TQString ct( d->ed->text() ); + int i = completionIndex( ct, currentItem() ); + if ( i > -1 ) { + TQString 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() )) { + TQMouseEvent *e = (TQMouseEvent*)event; + switch( event->type() ) { + case TQEvent::MouseMove: + if ( !d->mouseWasInsidePopup ) { + TQPoint pos = e->pos(); + if ( d->listBox()->rect().contains( pos ) ) + d->mouseWasInsidePopup = TRUE; + // Check if arrow button should toggle + if ( d->arrowPressed ) { + TQPoint comboPos; + comboPos = mapFromGlobal( d->listBox()->mapToGlobal(pos) ); + TQRect arrowRect = + style().querySubControlMetrics( TQStyle::CC_ComboBox, this, + TQStyle::SC_ComboBoxArrow); + arrowRect = TQStyle::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(TQStyle::SH_ComboBox_ListMouseTracking, this)) { + TQWidget *mouseW = TQApplication::widgetAt( e->globalPos(), TRUE ); + if ( mouseW == d->listBox()->viewport() ) { //### + TQMouseEvent m( TQEvent::MouseMove, e->pos(), e->globalPos(), + LeftButton, LeftButton ); + TQApplication::sendEvent( object, &m ); //### Evil + return TRUE; + } + } + + break; + case TQEvent::MouseButtonRelease: + if ( d->listBox()->rect().contains( e->pos() ) ) { + TQMouseEvent tmp( TQEvent::MouseButtonDblClick, + e->pos(), e->button(), e->state() ) ; + // will hide popup + TQApplication::sendEvent( object, &tmp ); + return TRUE; + } else { + if ( d->mouseWasInsidePopup ) { + popDownListBox(); + } else { + d->arrowPressed = FALSE; + if ( d->arrowDown ) { + d->arrowDown = FALSE; + repaint( FALSE ); + } + } + } + break; + case TQEvent::MouseButtonDblClick: + case TQEvent::MouseButtonPress: + if ( !d->listBox()->rect().contains( e->pos() ) ) { + TQPoint globalPos = d->listBox()->mapToGlobal(e->pos()); + if ( TQApplication::widgetAt( globalPos, TRUE ) == this ) { + d->discardNextMousePress = TRUE; + // avoid popping up again + } + popDownListBox(); + return TRUE; + } + break; + case TQEvent::KeyPress: + switch( ((TQKeyEvent *)event)->key() ) { + case Key_Up: + case Key_Down: + if ( !(((TQKeyEvent *)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 TQDialog's enter handling + return FALSE; + default: + break; + } + break; + case TQEvent::Hide: + popDownListBox(); + break; + default: + break; + } + } else if ( (!d->usingListBox() || style().styleHint(TQStyle::SH_ComboBox_Popup, this)) && + object == d->popup() ) { + TQMouseEvent *e = (TQMouseEvent*)event; + switch ( event->type() ) { + case TQEvent::MouseButtonRelease: + if ( d->shortClick ) { + TQMouseEvent tmp( TQEvent::MouseMove, + e->pos(), e->button(), e->state() ) ; + // highlight item, but don't pop down: + TQApplication::sendEvent( object, &tmp ); + return TRUE; + } + break; + case TQEvent::MouseButtonDblClick: + case TQEvent::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 TQEvent::Hide: + d->poppedUp = FALSE; + break; + default: + break; + } + } + return TQWidget::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 TQComboBox::completionIndex( const TQString & prefix, + int startingAt = 0 ) const +{ + int start = startingAt; + if ( start < 0 || start >= count() ) + start = 0; + if ( start >= count() ) + return -1; + TQString match = prefix.lower(); + if ( match.length() < 1 ) + return start; + + TQString 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 TQComboBox::sizeLimit() const +{ + return d ? d->sizeLimit : INT_MAX; +} + +void TQComboBox::setSizeLimit( int lines ) +{ + d->sizeLimit = lines; +} + + +int TQComboBox::maxCount() const +{ + return d ? d->maxCount : INT_MAX; +} + +void TQComboBox::setMaxCount( int count ) +{ + int l = this->count(); + while( --l > count ) + removeItem( l ); + d->maxCount = count; +} + +TQComboBox::Policy TQComboBox::insertionPolicy() const +{ + return d->p; +} + +void TQComboBox::setInsertionPolicy( Policy policy ) +{ + d->p = policy; +} + + + +/*! + Internal slot to keep the line editor up to date. +*/ + +void TQComboBox::returnPressed() +{ + TQString 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 TQComboBox::setEnabled( bool enable ) +{ + if ( !enable ) { + if ( d->usingListBox() ) { + popDownListBox(); + } else { + d->popup()->removeEventFilter( this ); + d->popup()->close(); + d->poppedUp = FALSE; + } + } + TQWidget::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() TQValidator +*/ + +void TQComboBox::setValidator( const TQValidator * 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() TQValidator +*/ + +const TQValidator * TQComboBox::validator() const +{ + return d && d->ed ? d->ed->validator() : 0; +} + + +/*! + This slot is equivalent to setValidator( 0 ). +*/ + +void TQComboBox::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 TQComboBox assumes that newListBox->text(n) returns + non-null for 0 \<= n \< newListbox->count(). This assumption is + necessary because of the line edit in TQComboBox. +*/ + +void TQComboBox::setListBox( TQListBox * newListBox ) +{ + clear(); + + if ( d->usingListBox() ) { + delete d->listBox(); + } else { + delete d->popup(); + d->setPopupMenu(0, FALSE); + } + + newListBox->reparent( this, WType_Popup, TQPoint(0,0), FALSE ); + d->setListBox( newListBox ); + d->listBox()->setFont( font() ); + d->listBox()->setPalette( palette() ); + d->listBox()->setVScrollBarMode(TQScrollView::AlwaysOff); + d->listBox()->setHScrollBarMode(TQScrollView::AlwaysOff); + d->listBox()->setFrameStyle( TQFrame::Box | TQFrame::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. + (TQComboBox can use TQPopupMenu instead of TQListBox.) Provided to + match setListBox(). + + \sa setListBox() +*/ + +TQListBox * TQComboBox::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. +*/ +TQLineEdit* TQComboBox::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 TQComboBox::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 TQComboBox::setEditText( const TQString &newText ) +{ + if ( d && d->ed ) { + d->updateLinedGeometry(); + d->ed->setText( newText ); + } +} + +void TQComboBox::setAutoCompletion( bool enable ) +{ + d->useCompletion = enable; + d->completeNow = FALSE; +} + + +bool TQComboBox::autoCompletion() const +{ + return d->useCompletion; +} + +/*!\reimp + */ +void TQComboBox::styleChange( TQStyle& s ) +{ + d->sizeHint = TQSize(); // invalidate size hint... + if ( d->ed ) + d->updateLinedGeometry(); + TQWidget::styleChange( s ); +} + +bool TQComboBox::editable() const +{ + return d->ed != 0; +} + +void TQComboBox::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 TQComboBox::setUpListBox() +{ + d->setListBox( new TQListBox( this, "in-combo", WType_Popup ) ); + d->listBox()->setFont( font() ); + d->listBox()->setPalette( palette() ); + d->listBox()->setVScrollBarMode( TQListBox::AlwaysOff ); + d->listBox()->setHScrollBarMode( TQListBox::AlwaysOff ); + d->listBox()->setFrameStyle( TQFrame::Box | TQFrame::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 TQComboBox::setUpLineEdit() +{ + if ( !d->ed ) + setLineEdit( new TQLineEdit( this, "combo edit" ) ); +} + +/*! + Sets the line edit to use \a edit instead of the current line edit. +*/ + +void TQComboBox::setLineEdit( TQLineEdit *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, TQPoint(0,0), FALSE ); + + connect (edit, SIGNAL( textChanged(const TQString&) ), + this, SIGNAL( textChanged(const TQString&) ) ); + 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 TQComboBox::hide() +{ + TQWidget::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 000000000..349958240 --- /dev/null +++ b/src/widgets/qcombobox.h @@ -0,0 +1,206 @@ +/********************************************************************** +** +** Definition of TQComboBox class +** +** Created : 950426 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQCOMBOBOX_H +#define TQCOMBOBOX_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_COMBOBOX + + +class TQStrList; +class TQStringList; +class TQLineEdit; +class TQValidator; +class TQListBox; +class TQComboBoxData; +class TQWheelEvent; + +class Q_EXPORT TQComboBox : public TQWidget +{ + Q_OBJECT + Q_ENUMS( Policy ) + Q_PROPERTY( bool editable READ editable WRITE setEditable ) + Q_PROPERTY( int count READ count ) + Q_PROPERTY( TQString 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: + TQComboBox( TQWidget* parent=0, const char* name=0 ); + TQComboBox( bool rw, TQWidget* parent=0, const char* name=0 ); + ~TQComboBox(); + + int count() const; + + void insertStringList( const TQStringList &, int index=-1 ); + void insertStrList( const TQStrList &, int index=-1 ); + void insertStrList( const TQStrList *, int index=-1 ); + void insertStrList( const char **, int numStrings=-1, int index=-1); + + void insertItem( const TQString &text, int index=-1 ); + void insertItem( const TQPixmap &pixmap, int index=-1 ); + void insertItem( const TQPixmap &pixmap, const TQString &text, int index=-1 ); + + void removeItem( int index ); + + int currentItem() const; + virtual void setCurrentItem( int index ); + + TQString currentText() const; + virtual void setCurrentText( const TQString& ); + + TQString text( int index ) const; + const TQPixmap *pixmap( int index ) const; + + void changeItem( const TQString &text, int index ); + void changeItem( const TQPixmap &pixmap, int index ); + void changeItem( const TQPixmap &pixmap, const TQString &text, int index ); + + bool autoResize() const; + virtual void setAutoResize( bool ); + TQSize sizeHint() const; + + void setPalette( const TQPalette & ); + void setFont( const TQFont & ); + 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 TQValidator * ); + const TQValidator * validator() const; + + virtual void setListBox( TQListBox * ); + TQListBox * listBox() const; + + virtual void setLineEdit( TQLineEdit *edit ); + TQLineEdit* lineEdit() const; + + virtual void setAutoCompletion( bool ); + bool autoCompletion() const; + + bool eventFilter( TQObject *object, TQEvent *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 TQString &); + +signals: + void activated( int index ); + void highlighted( int index ); + void activated( const TQString &); + void highlighted( const TQString &); + void textChanged( const TQString &); + +private slots: + void internalActivate( int ); + void internalHighlight( int ); + void internalClickTimeout(); + void returnPressed(); + +protected: + void paintEvent( TQPaintEvent * ); + void resizeEvent( TQResizeEvent * ); + void mousePressEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseDoubleClickEvent( TQMouseEvent * ); + void keyPressEvent( TQKeyEvent *e ); + void focusInEvent( TQFocusEvent *e ); + void focusOutEvent( TQFocusEvent *e ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( TQWheelEvent *e ); +#endif + void styleChange( TQStyle& ); + + void updateMask(); + +private: + void setUpListBox(); + void setUpLineEdit(); + void popDownListBox(); + void reIndex(); + void currentChanged(); + int completionIndex( const TQString &, int ) const; + + TQComboBoxData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQComboBox( const TQComboBox & ); + TQComboBox &operator=( const TQComboBox & ); +#endif +}; + + +#endif // QT_NO_COMBOBOX + +#endif // TQCOMBOBOX_H diff --git a/src/widgets/qdatetimeedit.cpp b/src/widgets/qdatetimeedit.cpp new file mode 100644 index 000000000..300db0ee4 --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQDATETIMEEDIT_HIDDEN_CHAR '0' + +class Q_EXPORT TQNumberSection +{ +public: + TQNumberSection( 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( TQNumberSection ) +private: + int selstart :12; + int selend :12; + int act :7; + bool sep :1; +}; + +static TQString *lDateSep = 0; +static TQString *lTimeSep = 0; +static bool lAMPM = FALSE; +static TQString *lAM = 0; +static TQString *lPM = 0; +static TQDateEdit::Order lOrder = TQDateEdit::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 TQString(); + lTimeSep = new TQString(); + +#if defined(Q_WS_WIN) + QT_WA( { + TCHAR data[10]; + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SDATE, data, 10 ); + *lDateSep = TQString::fromUcs2( (ushort*)data ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_STIME, data, 10 ); + *lTimeSep = TQString::fromUcs2( (ushort*)data ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_ITIME, data, 10 ); + lAMPM = TQString::fromUcs2( (ushort*)data ).toInt()==0; + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_S1159, data, 10 ); + TQString am = TQString::fromUcs2( (ushort*)data ); + if ( !am.isEmpty() ) + lAM = new TQString( am ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_S2359, data, 10 ); + TQString pm = TQString::fromUcs2( (ushort*)data ); + if ( !pm.isEmpty() ) + lPM = new TQString( pm ); + } , { + char data[10]; + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_SDATE, (char*)&data, 10 ); + *lDateSep = TQString::fromLocal8Bit( data ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_STIME, (char*)&data, 10 ); + *lTimeSep = TQString::fromLocal8Bit( data ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_ITIME, (char*)&data, 10 ); + lAMPM = TQString::fromLocal8Bit( data ).toInt()==0; + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_S1159, (char*)&data, 10 ); + TQString am = TQString::fromLocal8Bit( data ); + if ( !am.isEmpty() ) + lAM = new TQString( am ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_S2359, (char*)&data, 10 ); + TQString pm = TQString::fromLocal8Bit( data ); + if ( !pm.isEmpty() ) + lPM = new TQString( pm ); + } ); +#else + *lDateSep = "-"; + *lTimeSep = ":"; +#endif + TQString d = TQDate( 1999, 11, 22 ).toString( TQt::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 = TQDateEdit::DMY; + } else if ( mpos < dpos && dpos < ypos ) { + lOrder = TQDateEdit::MDY; + } else if ( ypos < mpos && mpos < dpos ) { + lOrder = TQDateEdit::YMD; + } else if ( ypos < dpos && dpos < mpos ) { + lOrder = TQDateEdit::YDM; + } else { + // cannot determine the dateformat - use the default + return; + } + + // this code needs to change if new formats are added + +#ifndef Q_WS_WIN + TQString sep = d.mid( TQMIN( dpos, mpos ) + 2, TQABS( dpos - mpos ) - 2 ); + if ( d.contains( sep ) == 2 ) { + *lDateSep = sep; + } +#endif + } + +#ifndef Q_WS_WIN + TQString t = TQTime( 11, 22, 33 ).toString( TQt::LocalDate ); + dpos = t.find( "11" ); + mpos = t.find( "22" ); + ypos = t.find( "33" ); + // We only allow hhmmss + if ( dpos > -1 && dpos < mpos && mpos < ypos ) { + TQString sep = t.mid( dpos + 2, mpos - dpos - 2 ); + if ( sep == t.mid( mpos + 2, ypos - mpos - 2 ) ) { + *lTimeSep = sep; + } + } +#endif +} + +static TQDateEdit::Order localOrder() { + if ( !lDateSep ) { + readLocaleSettings(); + } + return lOrder; +} + +static TQString localDateSep() { + if ( !lDateSep ) { + readLocaleSettings(); + } + return *lDateSep; +} + +static TQString localTimeSep() { + if ( !lTimeSep ) { + readLocaleSettings(); + } + return *lTimeSep; +} + +class TQDateTimeEditorPrivate +{ +public: + TQDateTimeEditorPrivate() + : frm( TRUE ), + parag( new TQTextParagraph( 0, 0, 0, FALSE ) ), + focusSec(0) + { + parag->formatter()->setWrapEnabled( FALSE ); + cursor = new TQTextCursor( 0 ); + cursor->setParagraph( parag ); + offset = 0; + sep = localDateSep(); + refcount++; + } + ~TQDateTimeEditorPrivate() + { + delete parag; + delete cursor; + if ( !--refcount ) + cleanup(); + } + + void appendSection( const TQNumberSection& 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 TQString& s ) { sep = s; } + TQString separator() const { return sep; } + + void setFrame( bool f ) { frm = f; } + bool frame() const { return frm; } + + int focusSection() const { return focusSec; } + int section( const TQPoint& p ) + { + cursor->place( p + TQPoint( 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; + } + TQNumberSection 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 TQString& txt, bool focus, TQPainter& p, + const TQColorGroup& cg, const TQRect& rect, TQStyle& style ) + { + int fw = 0; + if ( frm ) + fw = style.pixelMetric(TQStyle::PM_DefaultFrameWidth); + + parag->truncate( 0 ); + parag->append( txt ); + if ( !focus ) + parag->removeSelection( TQTextDocument::Standard ); + else { + applyFocusSelection(); + } + + /* color all TQDATETIMEEDIT_HIDDEN_CHAR chars to background color */ + TQTextFormat *fb = parag->formatCollection()->format( p.font(), + cg.base() ); + TQTextFormat *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) == TQDATETIMEEDIT_HIDDEN_CHAR ) + parag->setFormat( i, 1, fb ); + else + parag->setFormat( i, 1, nf ); + } + fb->removeRef(); + nf->removeRef(); + + TQRect 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 TQSize& 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( TQTextDocument::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; + TQTextParagraph *parag; + TQTextCursor *cursor; + TQSize sz; + int focusSec; + TQValueList< TQNumberSection > sections; + TQString sep; + int offset; +}; + +class TQDateTimeEditor : public TQWidget +{ + Q_OBJECT +public: + TQDateTimeEditor( TQDateTimeEditBase * widget, TQWidget *parent, + const char * name=0 ); + ~TQDateTimeEditor(); + + void setControlWidget( TQDateTimeEditBase * widget ); + TQDateTimeEditBase * controlWidget() const; + + void setSeparator( const TQString& s ); + TQString separator() const; + + int focusSection() const; + bool setFocusSection( int s ); + void appendSection( const TQNumberSection& sec ); + void clearSections(); + void setSectionSelection( int sec, int selstart, int selend ); + bool eventFilter( TQObject *o, TQEvent *e ); + int sectionAt( const TQPoint &p ); + int mapSection( int sec ); + +protected: + void init(); + bool event( TQEvent *e ); + void resizeEvent( TQResizeEvent * ); + void paintEvent( TQPaintEvent * ); + void mousePressEvent( TQMouseEvent *e ); + +private: + TQDateTimeEditBase* cw; + TQDateTimeEditorPrivate* d; +}; + +class TQDateTimeSpinWidget : public TQSpinWidget +{ +public: + TQDateTimeSpinWidget( TQWidget *parent, const char *name ) + : TQSpinWidget( parent, name ) + { + } + + void enabledChange(bool notenabled) + { + TQDateEdit *de = ::qt_cast<TQDateEdit*>(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( TQWheelEvent *e ) + { + TQDateTimeEditor *editor = (TQDateTimeEditor*)editWidget()->qt_cast( "TQDateTimeEditor" ); + Q_ASSERT( editor ); + if ( !editor ) + return; + + int section = editor->sectionAt( e->pos() ); + editor->setFocusSection( section ); + + if ( section == -1 ) + return; + TQSpinWidget::wheelEvent( e ); + } +#endif +}; + +/*! + Constructs an empty datetime editor with parent \a parent and + called \a name. +*/ +TQDateTimeEditor::TQDateTimeEditor( TQDateTimeEditBase * widget, TQWidget *parent, + const char * name ) + : TQWidget( parent, name, WNoAutoErase ) +{ + d = new TQDateTimeEditorPrivate(); + cw = widget; + init(); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQDateTimeEditor::~TQDateTimeEditor() +{ + delete d; +} + +/*! \internal + +*/ + +void TQDateTimeEditor::init() +{ + setBackgroundMode( PaletteBase ); + setFocusSection( -1 ); + installEventFilter( this ); + setFocusPolicy( WheelFocus ); +} + + +/*! \reimp + +*/ + +bool TQDateTimeEditor::event( TQEvent *e ) +{ + if ( e->type() == TQEvent::FocusIn || e->type() == TQEvent::FocusOut ) { + if ( e->type() == TQEvent::FocusOut ) + qApp->sendEvent( cw, e ); + update( rect() ); + } else if ( e->type() == TQEvent::AccelOverride ) { + TQKeyEvent* ke = (TQKeyEvent*) 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 TQWidget::event( e ); +} + +/*! \reimp + +*/ + +void TQDateTimeEditor::resizeEvent( TQResizeEvent *e ) +{ + d->resize( e->size() ); + TQWidget::resizeEvent( e ); +} + + +/*! \reimp + +*/ + +void TQDateTimeEditor::paintEvent( TQPaintEvent * ) +{ + TQString 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 += " "; + } + } + + TQSharedDoubleBuffer buffer( this ); + const TQBrush &bg = + colorGroup().brush( isEnabled() ? TQColorGroup::Base : TQColorGroup::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 TQDateTimeEditor::sectionAt( const TQPoint &p ) +{ + return d->section( p ); +} + +int TQDateTimeEditor::mapSection( int sec ) +{ + return d->mapSection( sec ); +} + + +/*! \reimp + +*/ + +void TQDateTimeEditor::mousePressEvent( TQMouseEvent *e ) +{ + TQPoint p( e->pos().x(), 0 ); + int sec = sectionAt( p ); + if ( sec != -1 ) { + cw->setFocusSection( sec ); + repaint( rect(), FALSE ); + } +} + +/*! \reimp + +*/ +bool TQDateTimeEditor::eventFilter( TQObject *o, TQEvent *e ) +{ + if ( o == this ) { + if ( e->type() == TQEvent::KeyPress ) { + TQKeyEvent *ke = (TQKeyEvent*)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<TQDateEdit*>(cw) ) + ((TQDateEdit*)cw)->removeFirstNumber( d->focusSection() ); + else if ( ::qt_cast<TQTimeEdit*>(cw) ) + ((TQTimeEdit*)cw)->removeFirstNumber( d->focusSection() ); + return TRUE; + case Key_Delete: + cw->removeLastNumber( d->focusSection() ); + return TRUE; + case Key_Tab: + case Key_BackTab: { + if ( ke->state() == TQt::ControlButton ) + return FALSE; + + TQWidget *w = this; + bool hadDateEdit = FALSE; + while ( w ) { + if ( ::qt_cast<TQDateTimeSpinWidget*>(w) && qstrcmp( w->name(), "qt_spin_widget" ) != 0 || + ::qt_cast<TQDateTimeEdit*>(w) ) + break; + hadDateEdit = hadDateEdit || ::qt_cast<TQDateEdit*>(w); + w = w->parentWidget(); + } + + if ( w ) { + if ( !::qt_cast<TQDateTimeEdit*>(w) ) { + w = w->parentWidget(); + } else { + TQDateTimeEdit *ed = (TQDateTimeEdit*)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<TQDateTimeEdit*>(w) ) + w = w->parentWidget(); + } + } + + qApp->sendEvent( w, e ); + return TRUE; + } + } break; + default: + TQString 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<TQTimeEdit*>(cw) && focusSection() == (int) d->sectionCount()-1 ) { + // the first character of the AM/PM indicator toggles if the section has focus + TQTimeEdit *te = (TQTimeEdit*)cw; + TQTime time = te->time(); + if ( lAMPM && lAM && lPM && (te->display()&TQTimeEdit::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 TQDateTimeEditor::appendSection( const TQNumberSection& sec ) +{ + d->appendSection( sec ); +} + +/*! + Removes all sections from the editor. +*/ + +void TQDateTimeEditor::clearSections() +{ + d->clearSections(); +} + +/*! + Sets the selection of \a sec to start at \a selstart and end at \a + selend. +*/ + +void TQDateTimeEditor::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 TQDateTimeEditor::setSeparator( const TQString& s ) +{ + d->setSeparator( s ); + update(); +} + + +/*! + Returns the editor's separator. +*/ + +TQString TQDateTimeEditor::separator() const +{ + return d->separator(); +} + +/*! + Returns the number of the section that has focus. +*/ + +int TQDateTimeEditor::focusSection() const +{ + return d->focusSection(); +} + + +/*! + Sets the focus to section \a sec. If \a sec does not exist, + nothing happens. +*/ + +bool TQDateTimeEditor::setFocusSection( int sec ) +{ + return d->setFocusSection( sec ); +} + +/*! \class TQDateTimeEditBase + \brief The TQDateTimeEditBase class provides an abstraction for date and edit editors. + + Small abstract class that provides some functions that are common + for both TQDateEdit and TQTimeEdit. It is used internally by + TQDateTimeEditor. +*/ + +/*! + \fn TQDateTimeEditBase::TQDateTimeEditBase(TQWidget *, const char*) + \internal +*/ + +/*! + \fn TQDateTimeEditBase::setFocusSection(int) + \internal +*/ + +/*! \fn TQString TQDateTimeEditBase::sectionFormattedText( int sec ) + \internal + + Pure virtual function which returns the formatted text of section \a + sec. + +*/ + +/*! \fn void TQDateTimeEditBase::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 TQDateTimeEditBase::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 TQDateTimeEditBase::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 TQDateTimeEditBase::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 TQDateEditPrivate +{ +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; + TQDateEdit::Order ord; + bool overwrite; + bool adv; + int timerId; + bool typing; + TQDate min; + TQDate max; + bool changed; + TQDateTimeEditor *ed; + TQSpinWidget *controls; +}; + + +/*! + \class TQDateEdit qdatetimeedit.h + \brief The TQDateEdit class provides a date editor. + + \ingroup advanced + \ingroup time + \mainclass + + TQDateEdit 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 TQDateEdit + 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 TQDateEdit be initialised + with a date, e.g. + + \code + TQDateEdit *dateEdit = new TQDateEdit( TQDate::currentDate(), this ); + dateEdit->setRange( TQDate::currentDate().addDays( -365 ), + TQDate::currentDate().addDays( 365 ) ); + dateEdit->setOrder( TQDateEdit::MDY ); + dateEdit->setAutoAdvance( TRUE ); + \endcode + + Here we've created a new TQDateEdit 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 TQDate. You can + change this by calling setMinValue(), setMaxValue() or setRange(). + + Terminology: A TQDateEdit widget comprises three 'sections', one + each for the year, month and day. You can change the separator + character using TQDateTimeEditor::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 TQDate TQTimeEdit TQDateTimeEdit +*/ + +/*! + \enum TQDateEdit::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 TQTimeEdit::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. +*/ + +TQDateEdit::TQDateEdit( TQWidget * parent, const char * name ) + : TQDateTimeEditBase( 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. +*/ + +TQDateEdit::TQDateEdit( const TQDate& date, TQWidget * parent, const char * name ) + : TQDateTimeEditBase( parent, name ) +{ + init(); + setDate( date ); +} + +/*! \internal +*/ +void TQDateEdit::init() +{ + d = new TQDateEditPrivate(); + d->controls = new TQDateTimeSpinWidget( this, qstrcmp( name(), "qt_datetime_dateedit" ) == 0 ? "qt_spin_widget" : "date edit controls" ); + d->ed = new TQDateTimeEditor( 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 TQDate&) ), + SLOT( updateButtons() ) ); + d->ed->appendSection( TQNumberSection( 0,4 ) ); + d->ed->appendSection( TQNumberSection( 5,7 ) ); + d->ed->appendSection( TQNumberSection( 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 = TQDate( 1752, 9, 14 ); + d->max = TQDate( 8000, 12, 31 ); + d->changed = FALSE; + + setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ); + + refcount++; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQDateEdit::~TQDateEdit() +{ + delete d; + if ( !--refcount ) + cleanup(); +} + +/*! + \property TQDateEdit::minValue + + \brief the editor's minimum value + + Setting the minimum date value is equivalent to calling + TQDateEdit::setRange( \e d, maxValue() ), where \e d is the minimum + date. The default minimum date is 1752-09-14. + + \sa maxValue setRange() +*/ + +TQDate TQDateEdit::minValue() const +{ + return d->min; +} + +/*! + \property TQDateEdit::maxValue + + \brief the editor's maximum value + + Setting the maximum date value for the editor is equivalent to + calling TQDateEdit::setRange( minValue(), \e d ), where \e d is the + maximum date. The default maximum date is 8000-12-31. + + \sa minValue setRange() +*/ + +TQDate TQDateEdit::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 TQDateEdit::setRange( const TQDate& min, const TQDate& 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 TQDateEdit::setSeparator( const TQString& s ) +{ + d->ed->setSeparator( s ); +} + +/*! + Returns the editor's separator. +*/ + +TQString TQDateEdit::separator() const +{ + return d->ed->separator(); +} + + +/*! + Enables/disables the push buttons according to the min/max date + for this widget. +*/ + +void TQDateEdit::updateButtons() +{ + if ( !isEnabled() ) + return; + + bool upEnabled = date() < maxValue(); + bool downEnabled = date() > minValue(); + + d->controls->setUpEnabled( upEnabled ); + d->controls->setDownEnabled( downEnabled ); +} + +/*! \reimp + */ +void TQDateEdit::resizeEvent( TQResizeEvent * ) +{ + d->controls->resize( width(), height() ); +} + +/*! \reimp + +*/ +TQSize TQDateEdit::sizeHint() const +{ + constPolish(); + TQFontMetrics fm( font() ); + int fw = style().pixelMetric( TQStyle::PM_DefaultFrameWidth, this ); + int h = TQMAX( fm.lineSpacing(), 14 ) + 2; + int w = 2 + fm.width( '9' ) * 8 + fm.width( d->ed->separator() ) * 2 + d->controls->upRect().width() + fw * 4; + + return TQSize( w, TQMAX(h + fw * 2,20) ).expandedTo( TQApplication::globalStrut() ); +} + +/*! \reimp + +*/ +TQSize TQDateEdit::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() +*/ + +TQString TQDateEdit::sectionFormattedText( int sec ) +{ + TQString 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 ), TQDATETIMEEDIT_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 TQDateEdit::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() +*/ + +TQString TQDateEdit::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 TQString::number( val ); +} + +/*! \internal + + Returns the end of the section offset \a sec. + +*/ + +int TQDateEdit::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 TQDateEdit::order + \brief the order in which the year, month and day appear + + The default order is locale dependent. + + \sa Order +*/ + +void TQDateEdit::setOrder( TQDateEdit::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 ); +} + + +TQDateEdit::Order TQDateEdit::order() const +{ + return d->ord; +} + + +/*! \reimp + +*/ +void TQDateEdit::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 TQDateEdit::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 TQDate +*/ + +void TQDateEdit::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 TQDateEdit::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 TQDateEdit::setDay( int day ) +{ + if ( day < 1 ) + day = 1; + if ( day > 31 ) + day = 31; + if ( d->m > 0 && d->y > 1752 ) { + while ( !TQDate::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 TQDateEdit::date + \brief the editor's date value. + + If the date property is not valid, the editor displays all zeroes + and TQDateEdit::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 TQDateEdit::setDate( const TQDate& 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 ); +} + +TQDate TQDateEdit::date() const +{ + if ( TQDate::isValid( d->y, d->m, d->d ) ) + return TQDate( d->y, d->m, d->d ); + return TQDate(); +} + +/*! \internal + + Returns TRUE if \a y, \a m, \a d is out of range, otherwise returns + FALSE. + + \sa setRange() + +*/ + +bool TQDateEdit::outOfRange( int y, int m, int d ) const +{ + if ( TQDate::isValid( y, m, d ) ) { + TQDate currentDate( y, m, d ); + if ( currentDate > maxValue() || + currentDate < minValue() ) { + //## outOfRange should set overwrite? + return TRUE; + } + return FALSE; + } + return FALSE; /* assume ok */ +} + +/*! \reimp + +*/ + +void TQDateEdit::addNumber( int sec, int num ) +{ + if ( sec == -1 ) + return; + killTimer( d->timerId ); + bool overwrite = FALSE; + bool accepted = FALSE; + d->typing = TRUE; + TQString txt; + if ( sec == d->yearSection ) { + txt = TQString::number( d->y ); + if ( d->overwrite || txt.length() == 4 ) { + accepted = TRUE; + d->y = num; + } else { + txt += TQString::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 = TQString::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 = TQString::number( d->m ); + if ( d->overwrite || txt.length() == 2 ) { + accepted = TRUE; + d->m = num; + } else { + txt += TQString::number( num ); + int temp = txt.toInt(); + if ( temp > 12 ) + temp = num; + if ( outOfRange( d->y, temp, d->d ) ) + txt = TQString::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 = TQString::number( d->d ); + if ( d->overwrite || txt.length() == 2 ) { + accepted = TRUE; + d->d = num; + d->dayCache = d->d; + } else { + txt += TQString::number( num ); + int temp = txt.toInt(); + if ( temp > 31 ) + temp = num; + if ( outOfRange( d->y, d->m, temp ) ) + txt = TQString::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 TQDateEdit::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 TQDateEdit::fix() +{ + bool changed = FALSE; + int currentYear = TQDate::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 TQDateEdit::event( TQEvent *e ) +{ + if( e->type() == TQEvent::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 ( !TQDate::isValid( d->y, d->m, d->d ) ) { + d->dayCache = d->d; + int i = d->d; + for ( ; i > 0; i-- ) { + d->d = i; + if ( TQDate::isValid( d->y, d->m, d->d ) ) + break; + } + d->changed = TRUE; + } + if ( d->changed ) { + emit valueChanged( date() ); + d->changed = FALSE; + } + } else if ( e->type() == TQEvent::LocaleChange ) { + readLocaleSettings(); + d->ed->setSeparator( localDateSep() ); + setOrder( localOrder() ); + } + return TQDateTimeEditBase::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 TQDateEdit::removeFirstNumber( int sec ) +{ + if ( sec == -1 ) + return; + TQString txt; + if ( sec == d->yearSection ) { + txt = TQString::number( d->y ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->y = txt.toInt(); + } else if ( sec == d->monthSection ) { + txt = TQString::number( d->m ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->m = txt.toInt(); + } else if ( sec == d->daySection ) { + txt = TQString::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 TQDateEdit::removeLastNumber( int sec ) +{ + if ( sec == -1 ) + return; + TQString txt; + if ( sec == d->yearSection ) { + txt = TQString::number( d->y ); + txt = txt.mid( 0, txt.length()-1 ); + d->y = txt.toInt(); + } else if ( sec == d->monthSection ) { + txt = TQString::number( d->m ); + txt = txt.mid( 0, txt.length()-1 ); + d->m = txt.toInt(); + } else if ( sec == d->daySection ) { + txt = TQString::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 TQDateEdit::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 TQDateEdit::setAutoAdvance( bool advance ) +{ + d->adv = advance; +} + + +bool TQDateEdit::autoAdvance() const +{ + return d->adv; +} + +/*! \reimp +*/ + +void TQDateEdit::timerEvent( TQTimerEvent * ) +{ + d->overwrite = TRUE; +} + +/*! + \fn void TQDateEdit::valueChanged( const TQDate& date ) + + This signal is emitted whenever the editor's value changes. The \a + date parameter is the new value. +*/ + +/////////// + +class TQTimeEditPrivate +{ +public: + int h; + int m; + int s; + uint display; + bool adv; + bool overwrite; + int timerId; + bool typing; + TQTime min; + TQTime max; + bool changed; + TQDateTimeEditor *ed; + TQSpinWidget *controls; +}; + +/*! + \class TQTimeEdit qdatetimeedit.h + \brief The TQTimeEdit class provides a time editor. + + \ingroup advanced + \ingroup time + \mainclass + + TQTimeEdit 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 TQTimeEdit + 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 TQTimeEdit + is initialised with a time, e.g. + \code + TQTime timeNow = TQTime::currentTime(); + TQTimeEdit *timeEdit = new TQTimeEdit( timeNow, this ); + timeEdit->setRange( timeNow, timeNow.addSecs( 60 * 60 ) ); + \endcode + Here we've created a TQTimeEdit 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 TQTime. You can + change this by calling setMinValue(), setMaxValue() or setRange(). + + Terminology: A TQTimeWidget 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 TQTime TQDateEdit TQDateTimeEdit +*/ + + +/*! + Constructs an empty time edit with parent \a parent and called \a + name. +*/ + +TQTimeEdit::TQTimeEdit( TQWidget * parent, const char * name ) + : TQDateTimeEditBase( parent, name ) +{ + init(); +} + +/*! + \overload + + Constructs a time edit with the initial time value, \a time, + parent \a parent and called \a name. +*/ + +TQTimeEdit::TQTimeEdit( const TQTime& time, TQWidget * parent, const char * name ) + : TQDateTimeEditBase( parent, name ) +{ + init(); + setTime( time ); +} + +/*! \internal + */ + +void TQTimeEdit::init() +{ + d = new TQTimeEditPrivate(); + d->controls = new TQDateTimeSpinWidget( this, qstrcmp( name(), "qt_datetime_timeedit" ) == 0 ? "qt_spin_widget" : "time edit controls" ); + d->ed = new TQDateTimeEditor( 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( TQNumberSection( 0,0, TRUE, 0 ) ); + d->ed->appendSection( TQNumberSection( 0,0, TRUE, 1 ) ); + d->ed->appendSection( TQNumberSection( 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( TQNumberSection( 0,0, FALSE, 3 ) ); + } + d->adv = FALSE; + d->overwrite = TRUE; + d->timerId = 0; + d->typing = FALSE; + d->min = TQTime( 0, 0, 0 ); + d->max = TQTime( 23, 59, 59 ); + d->changed = FALSE; + + setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ); + + refcount++; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQTimeEdit::~TQTimeEdit() +{ + delete d; + if ( !--refcount ) + cleanup(); +} + +/*! + \property TQTimeEdit::minValue + \brief the minimum time value + + Setting the minimum time value is equivalent to calling + TQTimeEdit::setRange( \e t, maxValue() ), where \e t is the minimum + time. The default minimum time is 00:00:00. + + \sa maxValue setRange() +*/ + +TQTime TQTimeEdit::minValue() const +{ + return d->min; +} + +/*! + \property TQTimeEdit::maxValue + \brief the maximum time value + + Setting the maximum time value is equivalent to calling + TQTimeEdit::setRange( minValue(), \e t ), where \e t is the maximum + time. The default maximum time is 23:59:59. + + \sa minValue setRange() +*/ + +TQTime TQTimeEdit::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 TQTimeEdit::setRange( const TQTime& min, const TQTime& max ) +{ + if ( min.isValid() ) + d->min = min; + if ( max.isValid() ) + d->max = max; +} + +/*! + \property TQTimeEdit::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 TQTimeEdit::setDisplay( uint display ) +{ + if ( d->display == display ) + return; + + d->ed->clearSections(); + d->display = display; + if ( d->display & Hours ) + d->ed->appendSection( TQNumberSection( 0,0, TRUE, 0 ) ); + if ( d->display & Minutes ) + d->ed->appendSection( TQNumberSection( 0,0, TRUE, 1 ) ); + if ( d->display & Seconds ) + d->ed->appendSection( TQNumberSection( 0,0, TRUE, 2 ) ); + if ( d->display & AMPM ) + d->ed->appendSection( TQNumberSection( 0,0, FALSE, 3 ) ); + + d->ed->setFocusSection( 0 ); + d->ed->update(); +} + +uint TQTimeEdit::display() const +{ + return d->display; +} + +/*! + \property TQTimeEdit::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 TQTimeEdit::setTime( const TQTime& 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 ); +} + +TQTime TQTimeEdit::time() const +{ + if ( TQTime::isValid( d->h, d->m, d->s ) ) + return TQTime( d->h, d->m, d->s ); + return TQTime(); +} + +/*! + \property TQTimeEdit::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 TQTimeEdit::setAutoAdvance( bool advance ) +{ + d->adv = advance; +} + +bool TQTimeEdit::autoAdvance() const +{ + return d->adv; +} + +/*! + Sets the separator to \a s. Note that currently only the first + character of \a s is used. +*/ + +void TQTimeEdit::setSeparator( const TQString& s ) +{ + d->ed->setSeparator( s ); +} + +/*! + Returns the editor's separator. +*/ + +TQString TQTimeEdit::separator() const +{ + return d->ed->separator(); +} + + +/*! + \fn void TQTimeEdit::valueChanged( const TQTime& time ) + + This signal is emitted whenever the editor's value changes. The \a + time parameter is the new value. +*/ + +/*! \reimp + +*/ + +bool TQTimeEdit::event( TQEvent *e ) +{ + if ( e->type() == TQEvent::FocusOut ) { + d->typing = FALSE; + if ( d->changed ) { + emit valueChanged( time() ); + d->changed = FALSE; + } + } else if ( e->type() == TQEvent::LocaleChange ) { + readLocaleSettings(); + d->ed->setSeparator( localTimeSep() ); + } + return TQDateTimeEditBase::event( e ); +} + +/*! \reimp + +*/ + +void TQTimeEdit::timerEvent( TQTimerEvent * ) +{ + d->overwrite = TRUE; +} + + +/*! \reimp + +*/ + +void TQTimeEdit::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( "TQTimeEdit::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 TQTimeEdit::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( "TQTimeEdit::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. +*/ + +TQString TQTimeEdit::sectionFormattedText( int sec ) +{ + TQString txt; + txt = sectionText( sec ); + txt = txt.rightJustify( 2, TQDATETIMEEDIT_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 TQTimeEdit::setFocusSection( int sec ) +{ + if ( sec != d->ed->focusSection() ) { + killTimer( d->timerId ); + d->overwrite = TRUE; + d->typing = FALSE; + TQString txt = sectionText( sec ); + txt = txt.rightJustify( 2, TQDATETIMEEDIT_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 TQTimeEdit::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 TQTimeEdit::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 TQTimeEdit::setSecond( int s ) +{ + if ( s < 0 ) + s = 0; + if ( s > 59 ) + s = 59; + d->s = s; +} + + +/*! \internal + + Returns the text of section \a sec. + +*/ + +TQString TQTimeEdit::sectionText( int sec ) +{ + sec = d->ed->mapSection( sec ); + + TQString 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 = TQString::number( d->h ); + } else { + if ( d->h ) + txt = TQString::number( d->h - 12 ); + else + txt = "12"; + } + break; + case 1: + txt = TQString::number( d->m ); + break; + case 2: + txt = TQString::number( d->s ); + break; + case 3: + if ( d->h < 12 ) { + if ( lAM ) + txt = *lAM; + else + txt = TQString::fromLatin1( "AM" ); + } else { + if ( lPM ) + txt = *lPM; + else + txt = TQString::fromLatin1( "PM" ); + } + break; + default: + break; + } + return txt; +} + + +/*! \internal + Returns TRUE if \a h, \a m, and \a s are out of range. + */ + +bool TQTimeEdit::outOfRange( int h, int m, int s ) const +{ + if ( TQTime::isValid( h, m, s ) ) { + TQTime currentTime( h, m, s ); + if ( currentTime > maxValue() || + currentTime < minValue() ) + return TRUE; + else + return FALSE; + } + return TRUE; +} + +/*! \reimp + +*/ + +void TQTimeEdit::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; + TQString txt; + + switch( sec ) { + case 0: + txt = ( d->display & AMPM && d->h > 12 ) ? + TQString::number( d->h - 12 ) : TQString::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 += TQString::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 = TQString::number( d->h ); + } else { + if ( d->h > 11 ) { + temp += 12; + } + accepted = TRUE; + } + } else if ( !(d->display & AMPM) && outOfRange( temp, d->m, d->s ) ) { + txt = TQString::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 = TQString::number( d->m ); + if ( d->overwrite || txt.length() == 2 ) { + if ( !outOfRange( d->h, num, d->s ) ) { + accepted = TRUE; + d->m = num; + } + } else { + txt += TQString::number( num ); + int temp = txt.toInt(); + if ( temp > 59 ) + temp = num; + if ( outOfRange( d->h, temp, d->s ) ) + txt = TQString::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 = TQString::number( d->s ); + if ( d->overwrite || txt.length() == 2 ) { + if ( !outOfRange( d->h, d->m, num ) ) { + accepted = TRUE; + d->s = num; + } + } else { + txt += TQString::number( num ); + int temp = txt.toInt(); + if ( temp > 59 ) + temp = num; + if ( outOfRange( d->h, d->m, temp ) ) + txt = TQString::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 TQTimeEdit::removeFirstNumber( int sec ) +{ + if ( sec == -1 ) + return; + sec = d->ed->mapSection( sec ); + TQString txt; + switch( sec ) { + case 0: + txt = TQString::number( d->h ); + break; + case 1: + txt = TQString::number( d->m ); + break; + case 2: + txt = TQString::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 TQTimeEdit::removeLastNumber( int sec ) +{ + if ( sec == -1 ) + return; + sec = d->ed->mapSection( sec ); + TQString txt; + switch( sec ) { + case 0: + txt = TQString::number( d->h ); + break; + case 1: + txt = TQString::number( d->m ); + break; + case 2: + txt = TQString::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 TQTimeEdit::resizeEvent( TQResizeEvent * ) +{ + d->controls->resize( width(), height() ); +} + +/*! \reimp +*/ +TQSize TQTimeEdit::sizeHint() const +{ + constPolish(); + TQFontMetrics fm( font() ); + int fw = style().pixelMetric( TQStyle::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( TQString::fromLatin1( "AM" ) ) + 4; + } + + return TQSize( w, TQMAX(h + fw * 2,20) ).expandedTo( TQApplication::globalStrut() ); +} + +/*! \reimp +*/ +TQSize TQTimeEdit::minimumSizeHint() const +{ + return sizeHint(); +} + +/*! + \internal + Enables/disables the push buttons according to the min/max time + for this widget. +*/ + +// ### Remove in 4.0? + +void TQTimeEdit::updateButtons() +{ + if ( !isEnabled() ) + return; + + bool upEnabled = time() < maxValue(); + bool downEnabled = time() > minValue(); + + d->controls->setUpEnabled( upEnabled ); + d->controls->setDownEnabled( downEnabled ); +} + + +class TQDateTimeEditPrivate +{ +public: + bool adv; +}; + +/*! + \class TQDateTimeEdit qdatetimeedit.h + \brief The TQDateTimeEdit class combines a TQDateEdit and TQTimeEdit + widget into a single widget for editing datetimes. + + \ingroup advanced + \ingroup time + \mainclass + + TQDateTimeEdit consists of a TQDateEdit and TQTimeEdit 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 TQDateTimeEdit 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 + TQDateEdit::setOrder() to change this. Times appear in the order + hours, minutes, seconds using the 24 hour clock. + + It is recommended that the TQDateTimeEdit is initialised with a + datetime, e.g. + \code + TQDateTimeEdit *dateTimeEdit = new TQDateTimeEdit( TQDateTime::currentDateTime(), this ); + dateTimeEdit->dateEdit()->setRange( TQDateTime::currentDate(), + TQDateTime::currentDate().addDays( 7 ) ); + \endcode + Here we've created a new TQDateTimeEdit 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 TQDateEdit widget consists of three 'sections', one + each for the year, month and day. Similarly a TQTimeEdit 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 TQDateEdit TQTimeEdit +*/ + +/*! + Constructs an empty datetime edit with parent \a parent and called + \a name. +*/ +TQDateTimeEdit::TQDateTimeEdit( TQWidget * parent, const char * name ) + : TQWidget( parent, name ) +{ + init(); +} + + +/*! + \overload + + Constructs a datetime edit with the initial value \a datetime, + parent \a parent and called \a name. +*/ +TQDateTimeEdit::TQDateTimeEdit( const TQDateTime& datetime, + TQWidget * parent, const char * name ) + : TQWidget( parent, name ) +{ + init(); + setDateTime( datetime ); +} + + + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQDateTimeEdit::~TQDateTimeEdit() +{ + delete d; +} + + +/*! + \reimp + + Intercepts and handles resize events which have special meaning + for the TQDateTimeEdit. +*/ + +void TQDateTimeEdit::resizeEvent( TQResizeEvent * ) +{ + 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 +*/ + +TQSize TQDateTimeEdit::minimumSizeHint() const +{ + TQSize dsh = de->minimumSizeHint(); + TQSize tsh = te->minimumSizeHint(); + return TQSize( dsh.width() + tsh.width(), + TQMAX( dsh.height(), tsh.height() ) ); +} + +/*! \internal + */ + +void TQDateTimeEdit::init() +{ + d = new TQDateTimeEditPrivate(); + de = new TQDateEdit( this, "qt_datetime_dateedit" ); + te = new TQTimeEdit( this, "qt_datetime_timeedit" ); + d->adv = FALSE; + connect( de, SIGNAL( valueChanged(const TQDate&) ), + this, SLOT( newValue(const TQDate&) ) ); + connect( te, SIGNAL( valueChanged(const TQTime&) ), + this, SLOT( newValue(const TQTime&) ) ); + setFocusProxy( de ); + setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ); +} + +/*! \reimp + */ + +TQSize TQDateTimeEdit::sizeHint() const +{ + constPolish(); + TQSize dsh = de->sizeHint(); + TQSize tsh = te->sizeHint(); + return TQSize( dsh.width() + tsh.width(), + TQMAX( dsh.height(), tsh.height() ) ); +} + +/*! + \property TQDateTimeEdit::dateTime + \brief the editor's datetime value + + The datetime edit's datetime which may be an invalid datetime. +*/ + +void TQDateTimeEdit::setDateTime( const TQDateTime & dt ) +{ + if ( dt.isValid() ) { + de->setDate( dt.date() ); + te->setTime( dt.time() ); + emit valueChanged( dt ); + } +} + +TQDateTime TQDateTimeEdit::dateTime() const +{ + return TQDateTime( de->date(), te->time() ); +} + +/*! + \fn void TQDateTimeEdit::valueChanged( const TQDateTime& 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 TQDateTimeEdit::newValue( const TQDate& ) +{ + TQDateTime dt = dateTime(); + emit valueChanged( dt ); +} + +/*! \internal + \overload + Re-emits the value \a t. + */ + +void TQDateTimeEdit::newValue( const TQTime& ) +{ + TQDateTime 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 TQDateTimeEdit::setAutoAdvance( bool advance ) +{ + de->setAutoAdvance( advance ); + te->setAutoAdvance( advance ); +} + +/*! + Returns TRUE if auto-advance is enabled, otherwise returns FALSE. + + \sa setAutoAdvance() +*/ + +bool TQDateTimeEdit::autoAdvance() const +{ + return de->autoAdvance(); +} + +/*! + \fn TQDateEdit* TQDateTimeEdit::dateEdit() + + Returns the internal widget used for editing the date part of the + datetime. +*/ + +/*! + \fn TQTimeEdit* TQDateTimeEdit::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 000000000..d50d0c43f --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQDATETIMEEDIT_H +#define TQDATETIMEEDIT_H + +#ifndef QT_H +#include "qwidget.h" +#include "qstring.h" +#include "qdatetime.h" +#endif // QT_H + +#ifndef QT_NO_DATETIMEEDIT + +class Q_EXPORT TQDateTimeEditBase : public TQWidget +{ + Q_OBJECT +public: + TQDateTimeEditBase( TQWidget* parent=0, const char* name=0 ) + : TQWidget( parent, name ) {} + + virtual bool setFocusSection( int sec ) = 0; + virtual TQString 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= + TQDateTimeEditBase( const TQDateTimeEditBase & ); + TQDateTimeEditBase &operator=( const TQDateTimeEditBase & ); +#endif +}; + +class TQDateEditPrivate; + +class Q_EXPORT TQDateEdit : public TQDateTimeEditBase +{ + Q_OBJECT + Q_ENUMS( Order ) + Q_PROPERTY( Order order READ order WRITE setOrder ) + Q_PROPERTY( TQDate date READ date WRITE setDate ) + Q_PROPERTY( bool autoAdvance READ autoAdvance WRITE setAutoAdvance ) + Q_PROPERTY( TQDate maxValue READ maxValue WRITE setMaxValue ) + Q_PROPERTY( TQDate minValue READ minValue WRITE setMinValue ) + +public: + TQDateEdit( TQWidget* parent=0, const char* name=0 ); + TQDateEdit( const TQDate& date, TQWidget* parent=0, const char* name=0 ); + ~TQDateEdit(); + + enum Order { DMY, MDY, YMD, YDM }; + + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + +public slots: + virtual void setDate( const TQDate& date ); + +public: + TQDate date() const; + virtual void setOrder( Order order ); + Order order() const; + virtual void setAutoAdvance( bool advance ); + bool autoAdvance() const; + + virtual void setMinValue( const TQDate& d ) { setRange( d, maxValue() ); } + TQDate minValue() const; + virtual void setMaxValue( const TQDate& d ) { setRange( minValue(), d ); } + TQDate maxValue() const; + virtual void setRange( const TQDate& min, const TQDate& max ); + TQString separator() const; + virtual void setSeparator( const TQString& s ); + + // Make removeFirstNumber() virtual in TQDateTimeEditBase in 4.0 + void removeFirstNumber( int sec ); + +signals: + void valueChanged( const TQDate& date ); + +protected: + bool event( TQEvent *e ); + void timerEvent( TQTimerEvent * ); + void resizeEvent( TQResizeEvent * ); + void stepUp(); + void stepDown(); + TQString 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; + TQString sectionText( int sec ) const; + TQDateEditPrivate* d; + +#if defined(Q_DISABLE_COPY) + TQDateEdit( const TQDateEdit & ); + TQDateEdit &operator=( const TQDateEdit & ); +#endif +}; + +class TQTimeEditPrivate; + +class Q_EXPORT TQTimeEdit : public TQDateTimeEditBase +{ + Q_OBJECT + Q_SETS( Display ) + Q_PROPERTY( TQTime time READ time WRITE setTime ) + Q_PROPERTY( bool autoAdvance READ autoAdvance WRITE setAutoAdvance ) + Q_PROPERTY( TQTime maxValue READ maxValue WRITE setMaxValue ) + Q_PROPERTY( TQTime 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 + }; + + TQTimeEdit( TQWidget* parent=0, const char* name=0 ); + TQTimeEdit( const TQTime& time, TQWidget* parent=0, const char* name=0 ); + ~TQTimeEdit(); + + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + +public slots: + virtual void setTime( const TQTime& time ); + +public: + TQTime time() const; + virtual void setAutoAdvance( bool advance ); + bool autoAdvance() const; + + virtual void setMinValue( const TQTime& d ) { setRange( d, maxValue() ); } + TQTime minValue() const; + virtual void setMaxValue( const TQTime& d ) { setRange( minValue(), d ); } + TQTime maxValue() const; + virtual void setRange( const TQTime& min, const TQTime& max ); + TQString separator() const; + virtual void setSeparator( const TQString& s ); + + uint display() const; + void setDisplay( uint disp ); + + // Make removeFirstNumber() virtual in TQDateTimeEditBase in 4.0 + void removeFirstNumber( int sec ); + +signals: + void valueChanged( const TQTime& time ); + +protected: + bool event( TQEvent *e ); + void timerEvent( TQTimerEvent *e ); + void resizeEvent( TQResizeEvent * ); + void stepUp(); + void stepDown(); + TQString 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(); + TQString sectionText( int sec ); + TQTimeEditPrivate* d; + +#if defined(Q_DISABLE_COPY) + TQTimeEdit( const TQTimeEdit & ); + TQTimeEdit &operator=( const TQTimeEdit & ); +#endif +}; + + +class TQDateTimeEditPrivate; + +class Q_EXPORT TQDateTimeEdit : public TQWidget +{ + Q_OBJECT + Q_PROPERTY( TQDateTime dateTime READ dateTime WRITE setDateTime ) + +public: + TQDateTimeEdit( TQWidget* parent=0, const char* name=0 ); + TQDateTimeEdit( const TQDateTime& datetime, TQWidget* parent=0, + const char* name=0 ); + ~TQDateTimeEdit(); + + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + +public slots: + virtual void setDateTime( const TQDateTime & dt ); + +public: + TQDateTime dateTime() const; + + TQDateEdit* dateEdit() { return de; } + TQTimeEdit* timeEdit() { return te; } + + virtual void setAutoAdvance( bool advance ); + bool autoAdvance() const; + +signals: + void valueChanged( const TQDateTime& datetime ); + +protected: + // ### make init() private in TQt 4.0 + void init(); + void resizeEvent( TQResizeEvent * ); + +protected slots: + // ### make these two functions private in TQt 4.0, + // and merge them into one with no parameter + void newValue( const TQDate& d ); + void newValue( const TQTime& t ); + +private: + TQDateEdit* de; + TQTimeEdit* te; + TQDateTimeEditPrivate* d; + +#if defined(Q_DISABLE_COPY) + TQDateTimeEdit( const TQDateTimeEdit & ); + TQDateTimeEdit &operator=( const TQDateTimeEdit & ); +#endif +}; + +#endif +#endif diff --git a/src/widgets/qdial.cpp b/src/widgets/qdial.cpp new file mode 100644 index 000000000..e41140527 --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQDialPrivate +{ +public: + TQDialPrivate() + { + wrapping = FALSE; + tracking = TRUE; + doNotEmit = FALSE; + target = 3.7; + mousePressed = FALSE; + } + + bool wrapping; + bool tracking; + bool doNotEmit; + double target; + TQRect eraseArea; + bool eraseAreaValid; + bool showNotches; + bool onlyOutside; + bool mousePressed; + + TQPointArray lines; +}; + + +/*! + \class TQDial qdial.h + + \brief The TQDial class provides a rounded range control (like a speedometer or potentiometer). + + \ingroup basic + \mainclass + + TQDial 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 TQSlider + 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, TQDial 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, TQDial 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 TQRangeControl 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 TQScrollBar TQSpinBox + \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 TQWidget constructor. It has the default range of + a TQRangeControl. +*/ + +TQDial::TQDial( TQWidget *parent, const char *name, WFlags f ) + : TQWidget( parent, name, f | WNoAutoErase ), TQRangeControl() +{ + d = new TQDialPrivate; + d->eraseAreaValid = FALSE; + d->showNotches = FALSE; + d->onlyOutside = FALSE; + setFocusPolicy( TQWidget::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. +*/ + +TQDial::TQDial( int minValue, int maxValue, int pageStep, int value, + TQWidget *parent, const char *name ) + : TQWidget( parent, name, WNoAutoErase ), + TQRangeControl( minValue, maxValue, 1, pageStep, value ) +{ + d = new TQDialPrivate; + d->eraseAreaValid = FALSE; + d->showNotches = FALSE; + d->onlyOutside = FALSE; + setFocusPolicy( TQWidget::WheelFocus ); +} + +/*! + Destroys the dial. +*/ +TQDial::~TQDial() +{ + delete d; +} + + +void TQDial::setTracking( bool enable ) +{ + d->tracking = enable; +} + + +/*! + \property TQDial::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 TQDial::tracking() const +{ + return d ? d->tracking : TRUE; +} + +void TQDial::setValue( int newValue ) +{ // ### set doNotEmit? Matthias? + TQRangeControl::setValue( newValue ); +} + + +/*! + Increments the dial's value() by one lineStep(). +*/ + +void TQDial::addLine() +{ + TQRangeControl::addLine(); +} + + +/*! + Decrements the dial's value() by one lineStep(). +*/ + +void TQDial::subtractLine() +{ + TQRangeControl::subtractLine(); +} + + +/*! \reimp */ + +void TQDial::resizeEvent( TQResizeEvent * e ) +{ + d->lines.resize( 0 ); + TQWidget::resizeEvent( e ); +} + + +/*! + \reimp +*/ + +void TQDial::paintEvent( TQPaintEvent * e ) +{ + repaintScreen( &e->rect() ); +} + +/*! + Paints the dial using clip region \a cr. +*/ + +void TQDial::repaintScreen( const TQRect *cr ) +{ + TQPainter p; + p.begin( this ); + + bool resetClipping = FALSE; + + // calculate clip-region for erasing background + if ( cr ) { + p.setClipRect( *cr ); + } else if ( !d->onlyOutside && d->eraseAreaValid ) { + TQRegion reg = d->eraseArea; + double a; + reg = reg.subtract( calcArrow( a ) ); + p.setClipRegion( reg ); + resetClipping = TRUE; + } + + TQRect br( calcDial() ); + p.setPen( NoPen ); + // if ( style() == MotifStyle ) + // p.setBrush( colorGroup().brush( TQColorGroup::Mid ) ); + // else { + TQBrush b; + if ( colorGroup().brush( TQColorGroup::Light ).pixmap() ) + b = TQBrush( colorGroup().brush( TQColorGroup::Light ) ); + else + b = TQBrush( colorGroup().light(), Dense4Pattern ); + p.setBrush( b ); + p.setBackgroundMode( OpaqueMode ); + // } + + TQRect 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 + TQRegion remaining( 0, 0, width(), height() ); + remaining = remaining.subtract( TQRegion( te, TQRegion::Ellipse ) ); + if ( p.hasClipping() ) + remaining = remaining.intersect( p.clipRegion() ); + erase(remaining); + + if ( resetClipping ) { + if ( cr ) + p.setClipRect( *cr ); + else + p.setClipRect( TQRect( 0, 0, width(), height() ) ); + } + + // draw notches + if ( d->showNotches ) { + calcLines(); + p.setPen( colorGroup().foreground() ); + p.drawLineSegments( d->lines ); + } + + // calculate and paint arrow + p.setPen( TQPen( colorGroup().dark() ) ); + p.drawArc( te, 60 * 16, 180 * 16 ); + p.setPen( TQPen( colorGroup().light() ) ); + p.drawArc( te, 240 * 16, 180 * 16 ); + + double a; + TQPointArray arrow( calcArrow( a ) ); + TQRect ea( arrow.boundingRect() ); + d->eraseArea = ea; + d->eraseAreaValid = TRUE; + + p.setPen( NoPen ); + p.setBrush( colorGroup().brush( TQColorGroup::Button ) ); + if ( !d->onlyOutside ) + p.drawPolygon( arrow ); + + a = angle( TQPoint( width() / 2, height() / 2 ), arrow[ 0 ] ); + p.setBrush( TQt::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 = TQMIN( 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( TQPen( colorGroup().background() ) ); + p.setBrush( NoBrush ); + p.drawRect( br ); + p.restore(); + style().drawPrimitive( TQStyle::PE_FocusRect, &p, br, colorGroup()); + } + p.end(); +} + + +/*! + \reimp +*/ + +void TQDial::keyPressEvent( TQKeyEvent * 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 TQDial::mousePressEvent( TQMouseEvent * e ) +{ + d->mousePressed = TRUE; + setValue( valueFromPoint( e->pos() ) ); + emit dialPressed(); +} + + +/*! + \reimp +*/ + +void TQDial::mouseReleaseEvent( TQMouseEvent * e ) +{ + d->mousePressed = FALSE; + setValue( valueFromPoint( e->pos() ) ); + emit dialReleased(); +} + + +/*! + \reimp +*/ + +void TQDial::mouseMoveEvent( TQMouseEvent * 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 TQDial::wheelEvent( TQWheelEvent *e ) +{ + setValue( value() - e->delta() / 120 ); +} +#endif + +/*! + \reimp +*/ + +void TQDial::focusInEvent( TQFocusEvent * ) +{ + d->onlyOutside = TRUE; + repaintScreen(); + d->onlyOutside = FALSE; +} + + +/*! + \reimp +*/ + +void TQDial::focusOutEvent( TQFocusEvent * ) +{ + d->onlyOutside = TRUE; + repaintScreen(); + d->onlyOutside = FALSE; +} + +/*! + Reimplemented to ensure the display is correct and to emit the + valueChanged(int) signal when appropriate. +*/ + +void TQDial::valueChange() +{ + d->lines.resize( 0 ); + repaintScreen(); + if ( d->tracking || !d->doNotEmit ) { + emit valueChanged( value() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif + } +} + + +/*! + Reimplemented to ensure tick-marks are consistent with the new range. +*/ + +void TQDial::rangeChange() +{ + d->lines.resize( 0 ); + repaintScreen(); +} + + +/*! + \internal +*/ + +int TQDial::valueFromPoint( const TQPoint & 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 TQDial::angle( const TQPoint &p1, const TQPoint &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 TQDial::setWrapping( bool enable ) +{ + if ( d->wrapping == enable ) + return; + d->lines.resize( 0 ); + d->wrapping = enable; + d->eraseAreaValid = FALSE; + repaintScreen(); +} + + +/*! + \property TQDial::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 TQDial::wrapping() const +{ + return d->wrapping; +} + + +/*! + \property TQDial::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 TQDial::notchSize() const +{ + // radius of the arc + int r = TQMIN( 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 TQDial::setNotchTarget( double target ) +{ + d->lines.resize( 0 ); + d->target = target; + d->eraseAreaValid = FALSE; + d->onlyOutside = TRUE; + repaintScreen(); + d->onlyOutside = FALSE; +} + + +/*! + \property TQDial::notchTarget + \brief the target number of pixels between notches + + The notch target is the number of pixels TQDial attempts to put + between each notch. + + The actual size may differ from the target size. +*/ + +double TQDial::notchTarget() const +{ + return d->target; +} + + +/*! + Increments the dial's value() by one pageStep() of steps. +*/ + +void TQDial::addPage() +{ + TQRangeControl::addPage(); +} + + +/*! + Decrements the dial's value() by one pageStep() of steps. +*/ + +void TQDial::subtractPage() +{ + TQRangeControl::subtractPage(); +} + + +/*! + \fn void TQDial::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 TQDial::dialPressed() + + This signal is emitted when the user begins mouse interaction with + the dial. + + \sa dialReleased() +*/ + +/*! + \fn void TQDial::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 TQDial::dialReleased() + + This signal is emitted when the user ends mouse interaction with + the dial. + + \sa dialPressed() +*/ + +void TQDial::setNotchesVisible( bool b ) +{ + d->showNotches = b; + d->eraseAreaValid = FALSE; + d->onlyOutside = TRUE; + repaintScreen(); + d->onlyOutside = FALSE; +} + +/*! + \property TQDial::notchesVisible + \brief whether the notches are shown + + If TRUE, the notches are shown. If FALSE (the default) notches are + not shown. +*/ +bool TQDial::notchesVisible() const +{ + return d->showNotches; +} + +/*! + \reimp +*/ + +TQSize TQDial::minimumSizeHint() const +{ + return TQSize( 50, 50 ); +} + +/*! + \reimp +*/ + +TQSize TQDial::sizeHint() const +{ + return TQSize( 100, 100 ).expandedTo( TQApplication::globalStrut() ); +} + + + +/*! + \internal +*/ + +TQPointArray TQDial::calcArrow( double &a ) const +{ + int r = TQMIN( 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; + + TQPointArray arrow( 3 ); + arrow[0] = TQPoint( (int)( 0.5 + xc + len * cos(a) ), + (int)( 0.5 + yc -len * sin( a ) ) ); + arrow[1] = TQPoint( (int)( 0.5 + xc + back * cos( a + m_pi * 5 / 6 ) ), + (int)( 0.5 + yc - back * sin( a + m_pi * 5 / 6 ) ) ); + arrow[2] = TQPoint( (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 +*/ + +TQRect TQDial::calcDial() const +{ + double r = TQMIN( 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 TQRect( int(dx), int(dy), + int(r * 2 - 2 * d_ - 2), int(r * 2 - 2 * d_ - 2) ); +} + +/*! + \internal +*/ + +int TQDial::calcBigLineSize() const +{ + int r = TQMIN( width(), height() ) / 2; + int bigLineSize = r / 6; + if ( bigLineSize < 4 ) + bigLineSize = 4; + if ( bigLineSize > r / 2 ) + bigLineSize = r / 2; + return bigLineSize; +} + +/*! + \internal +*/ + +void TQDial::calcLines() +{ + if ( !d->lines.size() ) { + double r = TQMIN( 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] = TQPoint( (int)( xc + ( r - bigLineSize ) * c ), + (int)( yc - ( r - bigLineSize ) * s ) ); + d->lines[2*i+1] = TQPoint( (int)( xc + r * c ), + (int)( yc - r * s ) ); + } else { + d->lines[2*i] = TQPoint( (int)( xc + ( r - 1 - smallLineSize ) * c ), + (int)( yc - ( r - 1 - smallLineSize ) * s ) ); + d->lines[2*i+1] = TQPoint( (int)( xc + ( r - 1 ) * c ), + (int)( yc -( r - 1 ) * s ) ); + } + } + } +} + +/*! + \property TQDial::minValue + \brief the current minimum value + + When setting this property, the \l TQDial::maxValue is adjusted if + necessary to ensure that the range remains valid. + + \sa setRange() +*/ +int TQDial::minValue() const +{ + return TQRangeControl::minValue(); +} + +/*! + \property TQDial::maxValue + \brief the current maximum value + + When setting this property, the \l TQDial::minValue is adjusted if + necessary to ensure that the range remains valid. + + \sa setRange() +*/ +int TQDial::maxValue() const +{ + return TQRangeControl::maxValue(); +} + +void TQDial::setMinValue( int minVal ) +{ + TQRangeControl::setMinValue( minVal ); +} + +void TQDial::setMaxValue( int maxVal ) +{ + TQRangeControl::setMaxValue( maxVal ); +} + +/*! + \property TQDial::lineStep + \brief the current line step + + setLineStep() calls the virtual stepChange() function if the new + line step is different from the previous setting. + + \sa TQRangeControl::setSteps() pageStep setRange() +*/ + +int TQDial::lineStep() const +{ + return TQRangeControl::lineStep(); +} + +/*! + \property TQDial::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 TQDial::pageStep() const +{ + return TQRangeControl::pageStep(); +} + +void TQDial::setLineStep( int i ) +{ + setSteps( i, pageStep() ); +} + +void TQDial::setPageStep( int i ) +{ + setSteps( lineStep(), i ); +} + +/*! + \property TQDial::value + \brief the current dial value + + This is guaranteed to be within the range + \l{TQDial::minValue}..\l{TQDial::maxValue}. + + \sa minValue maxValue +*/ + +int TQDial::value() const +{ + return TQRangeControl::value(); +} + +#endif // QT_FEATURE_DIAL diff --git a/src/widgets/qdial.h b/src/widgets/qdial.h new file mode 100644 index 000000000..036c81959 --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + + +#ifndef TQDIAL_H +#define TQDIAL_H + +#ifndef QT_H +#include "qwidget.h" +#include "qrangecontrol.h" +#endif // QT_H + +#ifndef QT_NO_DIAL + +class TQDialPrivate; + +class Q_EXPORT TQDial: public TQWidget, public TQRangeControl +{ + 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: + TQDial( TQWidget* parent=0, const char* name=0, WFlags f = 0 ); + TQDial( int minValue, int maxValue, int pageStep, int value, + TQWidget* parent=0, const char* name=0 ); + ~TQDial(); + + bool tracking() const; + + bool wrapping() const; + + int notchSize() const; + + virtual void setNotchTarget( double ); + double notchTarget() const; + + bool notchesVisible() const; + + TQSize sizeHint() const; + TQSize 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( TQResizeEvent * ); + void paintEvent( TQPaintEvent * ); + + void keyPressEvent( TQKeyEvent * ); + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( TQWheelEvent * ); +#endif + void focusInEvent( TQFocusEvent * ); + void focusOutEvent( TQFocusEvent * ); + + void valueChange(); + void rangeChange(); + + virtual void repaintScreen( const TQRect *cr = 0 ); + +private: + TQDialPrivate * d; + + int valueFromPoint( const TQPoint & ) const; + double angle( const TQPoint &, const TQPoint & ) const; + TQPointArray calcArrow( double &a ) const; + TQRect calcDial() const; + int calcBigLineSize() const; + void calcLines(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQDial( const TQDial & ); + TQDial &operator=( const TQDial & ); +#endif + +}; + +#endif // QT_NO_DIAL + +#endif diff --git a/src/widgets/qdialogbuttons.cpp b/src/widgets/qdialogbuttons.cpp new file mode 100644 index 000000000..cd831be7f --- /dev/null +++ b/src/widgets/qdialogbuttons.cpp @@ -0,0 +1,456 @@ +/**************************************************************************** +** +** Implementation of TQDialogButtons class +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQDialogButtonsPrivate +{ + TQMap<int, TQString> text; + TQMap<TQDialogButtons::Button, TQWidget *> buttons; + TQGuardedPtr<TQWidget> custom; + Q_UINT32 enabled, visible; + TQDialogButtons::Button def; + TQt::Orientation orient; + bool questionMode; +}; + +#ifndef QT_NO_DIALOG +TQDialogButtons::TQDialogButtons(TQDialog *parent, bool autoConnect, Q_UINT32 buttons, + Orientation orient, const char *name ) : TQWidget(parent, name) +{ + init(buttons, orient); + if(parent && autoConnect) { + TQObject::connect(this, SIGNAL(acceptClicked()), parent, SLOT(accept())); + TQObject::connect(this, SIGNAL(rejectClicked()), parent, SLOT(reject())); + } +} +#endif // QT_NO_DIALOG + +TQDialogButtons::TQDialogButtons(TQWidget *parent, Q_UINT32 buttons, + Orientation orient, const char *name ) : TQWidget(parent, name) +{ + init(buttons, orient); +} + +void +TQDialogButtons::init(Q_UINT32 buttons, Orientation orient) +{ + if(buttons == All) { + qWarning("TQDialogButtons: cannot specify All by itself!"); + buttons = None; + } + d = new TQDialogButtonsPrivate; + d->questionMode = FALSE; + d->orient = orient; + d->def = (Button)style().styleHint(TQStyle::SH_DialogButtons_DefaultButton, this); + d->enabled = d->visible = buttons; +} + +TQDialogButtons::~TQDialogButtons() +{ + delete (TQWidget *)d->custom; + delete d; +} + +void +TQDialogButtons::setQuestionMode(bool b) +{ + d->questionMode = b; +} + +bool +TQDialogButtons::questionMode() const +{ + return d->questionMode; +} + +void +TQDialogButtons::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 +TQDialogButtons::isButtonEnabled(Button button) const +{ + return ((int)(d->enabled & button)) == button; +} + +void +TQDialogButtons::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 +TQDialogButtons::isButtonVisible(Button button) const +{ + return ((int)(d->visible & button)) == button; +} + +void +TQDialogButtons::addWidget(TQWidget *w) +{ + TQBoxLayout *lay = NULL; + if(!d->custom) { + d->custom = new TQWidget(this, "dialog_custom_area"); + if(orientation() == Horizontal) + lay = new TQHBoxLayout(d->custom); + else + lay = new TQVBoxLayout(d->custom); + layoutButtons(); + } else { + lay = (TQBoxLayout*)d->custom->layout(); + } + if(w->parent() != d->custom) + w->reparent(d->custom, 0, TQPoint(0, 0), TRUE); + lay->addWidget(w); +} + +void +TQDialogButtons::setDefaultButton(Button button) +{ + if(!((int)(d->visible & button) == button)) { + qWarning("TQDialogButtons: 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", TQVariant(FALSE,0)); +#endif + d->def = button; +#ifndef QT_NO_PROPERTIES + if(d->buttons.contains(d->def)) + d->buttons[d->def]->setProperty("default", TQVariant(FALSE,0)); +#endif + } +} + +TQDialogButtons::Button +TQDialogButtons::defaultButton() const +{ + return d->def; +} + +void +TQDialogButtons::setButtonText(Button button, const TQString &str) +{ + d->text[button] = str; +#ifndef QT_NO_PROPERTIES + if(d->buttons.contains(button)) + d->buttons[button]->setProperty("text", TQVariant(str)); +#endif + layoutButtons(); +} + +TQString +TQDialogButtons::buttonText(Button b) const +{ + if(d->text.contains(b)) + return d->text[b]; + return TQString(); //null if it is default.. +} + +void +TQDialogButtons::setOrientation(Orientation orient) +{ + if(d->orient != orient) { + d->orient = orient; + if(d->custom && d->custom->layout()) + ((TQBoxLayout*)d->custom->layout())->setDirection(orient == Horizontal ? TQBoxLayout::LeftToRight : + TQBoxLayout::TopToBottom); + layoutButtons(); + } +} + +TQt::Orientation +TQDialogButtons::orientation() const +{ + return d->orient; +} + +TQWidget * +TQDialogButtons::createButton(Button b) +{ + TQPushButton *ret = new TQPushButton(this, "qdialog_button"); + TQObject::connect(ret, SIGNAL(clicked()), this, SLOT(handleClicked())); + if(d->text.contains(b)) { + ret->setText(d->text[b]); + } else { + switch(b) { + case All: { + TQString 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 +TQDialogButtons::handleClicked() +{ + const TQObject *s = sender(); + if(!s) + return; + + for(TQMapIterator<TQDialogButtons::Button, TQWidget *> it = d->buttons.begin(); it != d->buttons.end(); ++it) { + if(it.data() == s) { + emit clicked((TQDialogButtons::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 +TQDialogButtons::resizeEvent(TQResizeEvent *) +{ + layoutButtons(); +} + +void +TQDialogButtons::showEvent(TQShowEvent *) +{ + layoutButtons(); +} + +void +TQDialogButtons::styleChanged(TQStyle &old) +{ + layoutButtons(); + TQWidget::styleChange(old); +} + +void +TQDialogButtons::layoutButtons() +{ + if(!isVisible()) //nah.. + return; + + TQStyle::SubRect rects[] = { + TQStyle::SR_DialogButtonAccept, TQStyle::SR_DialogButtonReject, + TQStyle::SR_DialogButtonApply, TQStyle::SR_DialogButtonHelp, + TQStyle::SR_DialogButtonCustom, TQStyle::SR_DialogButtonAll, + TQStyle::SR_DialogButtonRetry, TQStyle::SR_DialogButtonIgnore, + TQStyle::SR_DialogButtonAbort }; + for(unsigned int i = 0; i < (sizeof(rects) / sizeof(rects[0])); i++) { + TQWidget *w = NULL; + if(rects[i] == TQStyle::SR_DialogButtonCustom) { + w = d->custom; + } else { + Button b = None; + if(rects[i] == TQStyle::SR_DialogButtonApply) + b = Apply; + else if(rects[i] == TQStyle::SR_DialogButtonAll) + b = All; + else if(rects[i] == TQStyle::SR_DialogButtonAccept) + b = Accept; + else if(rects[i] == TQStyle::SR_DialogButtonReject) + b = Reject; + else if(rects[i] == TQStyle::SR_DialogButtonHelp) + b = Help; + else if(rects[i] == TQStyle::SR_DialogButtonRetry) + b = Retry; + else if(rects[i] == TQStyle::SR_DialogButtonAbort) + b = Abort; + else if(rects[i] == TQStyle::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", TQVariant(TRUE,0)); +#endif + } + w->setEnabled(d->enabled & b); + } + } + } + if(w) { + w->show(); + w->setGeometry(style().subRect(rects[i], this)); + } + } +} + +TQSize +TQDialogButtons::sizeHint() const +{ + constPolish(); + TQSize s; + if(d->custom) + s = d->custom->sizeHint(); + return (style().sizeFromContents(TQStyle::CT_DialogButtons, this, s. + expandedTo(TQApplication::globalStrut()))); +} + +TQSize +TQDialogButtons::sizeHint(TQDialogButtons::Button button) const +{ + TQWidget *w = NULL; + if(d->visible & button) { + if(!d->buttons.contains(button)) { + TQDialogButtons *that = (TQDialogButtons*)this; //ick, constness.. + w = that->createButton(button); + that->d->buttons.insert(button, w); + } else { + w = d->buttons[button]; + } + } + return w->sizeHint(); +} + +TQSize +TQDialogButtons::minimumSizeHint() const +{ + return sizeHint(); +} +#endif diff --git a/src/widgets/qdialogbuttons_p.h b/src/widgets/qdialogbuttons_p.h new file mode 100644 index 000000000..2f8336371 --- /dev/null +++ b/src/widgets/qdialogbuttons_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Definition of TQDialogButtons class. +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQDIALOGBUTTONS_P_H +#define TQDIALOGBUTTONS_P_H + +#ifndef QT_H +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H +#endif + +#ifndef QT_NO_DIALOGBUTTONS +struct TQDialogButtonsPrivate; + +class +TQDialogButtons : public TQWidget +{ + 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 + TQDialogButtons(TQDialog *parent, bool autoConnect = TRUE, Q_UINT32 buttons = Accept | Reject, + Orientation orient = Horizontal, const char *name = NULL); +#endif // QT_NO_DIALOG + TQDialogButtons(TQWidget *parent, Q_UINT32 buttons = Accept | Reject, + Orientation orient = Horizontal, const char *name = NULL); + ~TQDialogButtons(); + + 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(TQWidget *); + + virtual void setDefaultButton(Button); + Button defaultButton() const; + + virtual void setButtonText(Button, const TQString &); + TQString buttonText(Button) const; + + void setOrientation(Orientation); + Orientation orientation() const; + + virtual TQSize sizeHint(Button) const; + TQSize minimumSizeHint() const; + TQSize sizeHint() const; + +protected: + void layoutButtons(); + virtual TQWidget *createButton(Button); + + void showEvent(TQShowEvent *); + void resizeEvent(TQResizeEvent *); + void styleChanged(TQStyle &); + +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: + TQDialogButtonsPrivate *d; + void init(Q_UINT32, Orientation); +}; +#endif //QT_NO_DIALOGBUTTONS +#endif //TQDIALOGBUTTONS_P_H diff --git a/src/widgets/qdockarea.cpp b/src/widgets/qdockarea.cpp new file mode 100644 index 000000000..bb55db8b0 --- /dev/null +++ b/src/widgets/qdockarea.cpp @@ -0,0 +1,1320 @@ +/**************************************************************************** +** +** Implementation of the TQDockArea class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQDOCKAREA_DEBUG + +struct Q_EXPORT DockData +{ + DockData() : w( 0 ), rect() {} + DockData( TQDockWindow *dw, const TQRect &r ) : w( dw ), rect( r ) {} + TQDockWindow *w; + TQRect rect; + + Q_DUMMY_COMPARISON_OPERATOR( DockData ) +}; + +static int fix_x( TQDockWindow* w, int width = -1 ) { + if ( TQApplication::reverseLayout() ) { + if ( width < 0 ) + width = w->width(); + return w->parentWidget()->width() - w->x() - width; + } + return w->x(); +} +static int fix_x( TQDockWindow* w, int x, int width = -1 ) { + if ( TQApplication::reverseLayout() ) { + if ( width < 0 ) + width = w->width(); + return w->parentWidget()->width() - x - width; + } + return x; +} + +static TQPoint fix_pos( TQDockWindow* w ) { + if ( TQApplication::reverseLayout() ) { + TQPoint p = w->pos(); + p.rx() = w->parentWidget()->width() - p.x() - w->width(); + return p; + } + return w->pos(); +} + + +void TQDockAreaLayout::setGeometry( const TQRect &r ) +{ + TQLayout::setGeometry( r ); + layoutItems( r ); +} + +TQLayoutIterator TQDockAreaLayout::iterator() +{ + return 0; +} + +TQSize TQDockAreaLayout::sizeHint() const +{ + if ( !dockWindows || !dockWindows->first() ) + return TQSize( 0, 0 ); + + if ( dirty ) { + TQDockAreaLayout *that = (TQDockAreaLayout *) this; + that->layoutItems( geometry() ); + } + + int w = 0; + int h = 0; + TQPtrListIterator<TQDockWindow> it( *dockWindows ); + TQDockWindow *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 = TQMAX( h, dw->height() + plush ); + w = TQMAX( w, dw->width() + plusw ); + } + + if ( hasHeightForWidth() ) + return TQSize( 0, h ); + return TQSize( w, 0 ); +} + +bool TQDockAreaLayout::hasHeightForWidth() const +{ + return orient == Horizontal; +} + +void TQDockAreaLayout::init() +{ + dirty = TRUE; + cached_width = 0; + cached_height = 0; + cached_hfw = -1; + cached_wfh = -1; +} + +void TQDockAreaLayout::invalidate() +{ + dirty = TRUE; + cached_width = 0; + cached_height = 0; +} + +static int start_pos( const TQRect &r, TQt::Orientation o ) +{ + if ( o == TQt::Horizontal ) { + return TQMAX( 0, r.x() ); + } else { + return TQMAX( 0, r.y() ); + } +} + +static void add_size( int s, int &pos, TQt::Orientation o ) +{ + if ( o == TQt::Horizontal ) { + pos += s; + } else { + pos += s; + } +} + +static int space_left( const TQRect &r, int pos, TQt::Orientation o ) +{ + if ( o == TQt::Horizontal ) { + return ( r.x() + r.width() ) - pos; + } else { + return ( r.y() + r.height() ) - pos; + } +} + +static int dock_extent( TQDockWindow *w, TQt::Orientation o, int maxsize ) +{ + if ( o == TQt::Horizontal ) + return TQMIN( maxsize, TQMAX( w->sizeHint().width(), w->fixedExtent().width() ) ); + else + return TQMIN( maxsize, TQMAX( w->sizeHint().height(), w->fixedExtent().height() ) ); +} + +static int dock_strut( TQDockWindow *w, TQt::Orientation o ) +{ + if ( o != TQt::Horizontal ) { + int wid; + if ( ( wid = w->fixedExtent().width() ) != -1 ) + return TQMAX( wid, TQMAX( w->minimumSize().width(), w->minimumSizeHint().width() ) ); + return TQMAX( w->sizeHint().width(), TQMAX( w->minimumSize().width(), w->minimumSizeHint().width() ) ); + } else { + int hei; + if ( ( hei = w->fixedExtent().height() ) != -1 ) + return TQMAX( hei, TQMAX( w->minimumSizeHint().height(), w->minimumSize().height() ) ); + return TQMAX( w->sizeHint().height(), TQMAX( w->minimumSizeHint().height(), w->minimumSize().height() ) ); + } +} + +static void set_geometry( TQDockWindow *w, int pos, int sectionpos, int extent, int strut, TQt::Orientation o ) +{ + if ( o == TQt::Horizontal ) + w->setGeometry( fix_x( w, pos, extent), sectionpos, extent, strut ); + else + w->setGeometry( sectionpos, pos, strut, extent ); +} + +static int size_extent( const TQSize &s, TQt::Orientation o, bool swap = FALSE ) +{ + return o == TQt::Horizontal ? ( swap ? s.height() : s.width() ) : ( swap ? s.width() : s.height() ); +} + +static int point_pos( const TQPoint &p, TQt::Orientation o, bool swap = FALSE ) +{ + return o == TQt::Horizontal ? ( swap ? p.y() : p.x() ) : ( swap ? p.x() : p.y() ); +} + +static void shrink_extend( TQDockWindow *dw, int &dockExtend, int /*spaceLeft*/, TQt::Orientation o ) +{ + TQToolBar *tb = ::qt_cast<TQToolBar*>(dw); + if ( o == TQt::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( TQValueList<DockData> &lastLine, TQt::Orientation o, int linestrut, int fullextent, int tbstrut, int maxsize, TQDockAreaLayout * ) +{ + TQDockWindow *last = 0; + TQRect lastRect; + for ( TQValueList<DockData>::Iterator it = lastLine.begin(); it != lastLine.end(); ++it ) { + if ( tbstrut != -1 && ::qt_cast<TQToolBar*>((*it).w) ) + (*it).rect.setHeight( tbstrut ); + if ( !last ) { + last = (*it).w; + lastRect = (*it).rect; + continue; + } + if ( !last->isStretchable() ) { + int w = TQMIN( lastRect.width(), maxsize ); + set_geometry( last, lastRect.x(), lastRect.y(), w, lastRect.height(), o ); + } else { + int w = TQMIN( (*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 = TQMIN( lastRect.width(), maxsize ); + set_geometry( last, lastRect.x(), lastRect.y(), w, lastRect.height(), o ); + } else { + int w = TQMIN( fullextent - lastRect.x() - ( o == TQt::Vertical ? 1 : 0 ), maxsize ); + set_geometry( last, lastRect.x(), lastRect.y(), w, + last->isResizeEnabled() ? linestrut : lastRect.height(), o ); + } +} + + +TQSize TQDockAreaLayout::minimumSize() const +{ + if ( !dockWindows || !dockWindows->first() ) + return TQSize( 0, 0 ); + + if ( dirty ) { + TQDockAreaLayout *that = (TQDockAreaLayout *) this; + that->layoutItems( geometry() ); + } + + int s = 0; + + TQPtrListIterator<TQDockWindow> it( *dockWindows ); + TQDockWindow *dw = 0; + while ( ( dw = it.current() ) != 0 ) { + ++it; + if ( dw->isHidden() ) + continue; + s = TQMAX( s, dock_strut( dw, orientation() ) ); + } + + return orientation() == Horizontal ? TQSize( 0, s ? s+2 : 0 ) : TQSize( s, 0 ); +} + + + +int TQDockAreaLayout::layoutItems( const TQRect &rect, bool testonly ) +{ + if ( !dockWindows || !dockWindows->first() ) + return 0; + + dirty = FALSE; + + // some corrections + TQRect r = rect; + if ( orientation() == Vertical ) + r.setHeight( r.height() - 3 ); + + // init + lines.clear(); + ls.clear(); + TQPtrListIterator<TQDockWindow> it( *dockWindows ); + TQDockWindow *dw = 0; + int start = start_pos( r, orientation() ); + int pos = start; + int sectionpos = 0; + int linestrut = 0; + TQValueList<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 = TQMAX( pos, dw->offset() ); + if ( pos + dockExtend > size_extent( r.size(), orientation() ) - 1 ) + pos = TQMAX( 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( TQRect( 0, sectionpos, r.width(), linestrut ) ); + else + lines.append( TQRect( 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 = TQMAX( pos, dw->offset() ); + if ( pos + dockExtend > size_extent( r.size(), orientation() ) - 1 ) + pos = TQMAX( op, size_extent( r.size(), orientation() ) - 1 - dockExtend ); + } + // do some calculations and add the remember the rect which the docking widget retquires for the placing + TQRect dwRect(pos, sectionpos, dockExtend, dock_strut( dw, orientation() ) ); + lastLine.append( DockData( dw, dwRect ) ); + if ( ::qt_cast<TQToolBar*>(dw) ) + tbstrut = TQMAX( tbstrut, dock_strut( dw, orientation() ) ); + linestrut = TQMAX( 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( TQRect( 0, sectionpos, r.width(), linestrut ) ); + else + lines.append( TQRect( 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 TQDockAreaLayout::heightForWidth( int w ) const +{ + if ( dockWindows->isEmpty() && parentWidget ) + return parentWidget->minimumHeight(); + + if ( cached_width != w ) { + TQDockAreaLayout * mthis = (TQDockAreaLayout*)this; + mthis->cached_width = w; + int h = mthis->layoutItems( TQRect( 0, 0, w, 0 ), TRUE ); + mthis->cached_hfw = h; + return h; + } + + return cached_hfw; +} + +int TQDockAreaLayout::widthForHeight( int h ) const +{ + if ( cached_height != h ) { + TQDockAreaLayout * mthis = (TQDockAreaLayout*)this; + mthis->cached_height = h; + int w = mthis->layoutItems( TQRect( 0, 0, 0, h ), TRUE ); + mthis->cached_wfh = w; + return w; + } + return cached_wfh; +} + + + + +/*! + \class TQDockArea qdockarea.h + \brief The TQDockArea class manages and lays out TQDockWindows. + + \ingroup application + + A TQDockArea is a container which manages a list of + \l{TQDockWindow}s which it lays out within its area. In cooperation + with the \l{TQDockWindow}s it is responsible for the docking and + undocking of \l{TQDockWindow}s and moving them inside the dock + area. TQDockAreas also handle the wrapping of \l{TQDockWindow}s to + fill the available space as compactly as possible. TQDockAreas can + contain TQToolBars since TQToolBar is a TQDockWindow subclass. + + TQMainWindow contains four TQDockAreas which you can use for your + TQToolBars and TQDockWindows, so in most situations you do not need + to use the TQDockArea class directly. Although TQMainWindow contains + support for its own dock areas it isn't convenient for adding new + TQDockAreas. If you need to create your own dock areas we suggest + that you create a subclass of TQWidget and add your TQDockAreas to + your subclass. + + \img qmainwindow-qdockareas.png TQMainWindow's TQDockAreas + + \target lines + \e Lines. TQDockArea 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 TQDockArea 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 TQDockWindow::dock() and + TQDockWindow::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 TQTextStream. The positions can be + read back later to restore the saved positions. + + Save the positions to a TQTextStream: + \code + ts << *myDockArea; + \endcode + + Restore the positions from a TQTextStream: + \code + ts >> *myDockArea; + \endcode +*/ + +/*! + \property TQDockArea::handlePosition + \brief where the dock window splitter handle is placed in the dock + area + + The default position is \c Normal. +*/ + +/*! + \property TQDockArea::orientation + \brief the dock area's orientation + + There is no default value; the orientation is specified in the + constructor. +*/ + +/*! + \enum TQDockArea::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 TQDockArea with orientation \a o, HandlePosition \a h, + parent \a parent and called \a name. +*/ + +TQDockArea::TQDockArea( Orientation o, HandlePosition h, TQWidget *parent, const char *name ) + : TQWidget( parent, name ), orient( o ), layout( 0 ), hPos( h ) +{ + dockWindows = new TQPtrList<TQDockWindow>; + layout = new TQDockAreaLayout( 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. +*/ + +TQDockArea::~TQDockArea() +{ + dockWindows->setAutoDelete( TRUE ); + delete dockWindows; + dockWindows = 0; +} + +/*! + Moves the TQDockWindow \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 TQDockArea::moveDockWindow( TQDockWindow *w, int index ) +{ + invalidateFixedSizes(); + TQDockWindow *dockWindow = 0; + int dockWindowIndex = findDockWindow( w ); + if ( dockWindowIndex == -1 ) { + dockWindow = w; + dockWindow->reparent( this, TQPoint( 0, 0 ), TRUE ); + w->installEventFilter( this ); + updateLayout(); + setSizePolicy( TQSizePolicy( orientation() == Horizontal ? TQSizePolicy::Expanding : TQSizePolicy::Minimum, + orientation() == Vertical ? TQSizePolicy::Expanding : TQSizePolicy::Minimum ) ); + dockWindows->append( w ); + } else { + if ( w->parent() != this ) + w->reparent( this, TQPoint( 0, 0 ), TRUE ); + if ( index == - 1 ) { + dockWindows->removeRef( w ); + dockWindows->append( w ); + } + } + + w->dockArea = this; + w->curPlace = TQDockWindow::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 TQDockArea::hasDockWindow( TQDockWindow *w, int *index ) +{ + int i = dockWindows->findRef( w ); + if ( index ) + *index = i; + return i != -1; +} + +int TQDockArea::lineOf( int index ) +{ + TQPtrList<TQDockWindow> lineStarts = layout->lineStarts(); + int i = 0; + for ( TQDockWindow *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 TQDockWindow. You shouldn't + need to call it yourself. +*/ + +void TQDockArea::moveDockWindow( TQDockWindow *w, const TQPoint &p, const TQRect &r, bool swap ) +{ + invalidateFixedSizes(); + int mse = -10; + bool hasResizable = FALSE; + for ( TQDockWindow *dw = dockWindows->first(); dw; dw = dockWindows->next() ) { + if ( dw->isHidden() ) + continue; + if ( dw->isResizeEnabled() ) + hasResizable = TRUE; + if ( orientation() != TQt::Horizontal ) + mse = TQMAX( TQMAX( dw->fixedExtent().width(), dw->width() ), mse ); + else + mse = TQMAX( TQMAX( dw->fixedExtent().height(), dw->height() ), mse ); + } + if ( !hasResizable && w->isResizeEnabled() ) { + if ( orientation() != TQt::Horizontal ) + mse = TQMAX( w->fixedExtent().width(), mse ); + else + mse = TQMAX( w->fixedExtent().height(), mse ); + } + + TQDockWindow *dockWindow = 0; + int dockWindowIndex = findDockWindow( w ); + TQPtrList<TQDockWindow> lineStarts = layout->lineStarts(); + TQValueList<TQRect> lines = layout->lineList(); + bool wasAloneInLine = FALSE; + TQPoint pos = mapFromGlobal( p ); + TQRect 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( TQRect( 0, 0, width(), height() ), TRUE ); + } + } else { + dockWindow = w; + dockWindow->reparent( this, TQPoint( 0, 0 ), TRUE ); + if ( swap ) + dockWindow->resize( dockWindow->height(), dockWindow->width() ); + w->installEventFilter( this ); + } + + lineStarts = layout->lineStarts(); + lines = layout->lineList(); + + TQRect rect = TQRect( mapFromGlobal( r.topLeft() ), r.size() ); + if ( orientation() == Horizontal && TQApplication::reverseLayout() ) { + rect = TQRect( 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; + TQRect lineRect; + // find the line which we touched with the mouse + for ( TQValueList<TQRect>::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(TQDOCKAREA_DEBUG) + qDebug( "insert in line %d, and insert that line: %d", dockLine, insertLine ); + qDebug( " (btw, we have %d lines)", lines.count() ); +#endif + TQDockWindow *dw = 0; + if ( dockLine >= (int)lines.count() ) { // insert after last line + dockWindows->append( dockWindow ); + dockWindow->setNewLine( TRUE ); +#if defined(TQDOCKAREA_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(TQDOCKAREA_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(TQDOCKAREA_DEBUG) + qDebug( "search line start of %d", searchLine ); +#endif + TQDockWindow *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(TQDOCKAREA_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(TQDOCKAREA_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(TQDOCKAREA_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(TQDOCKAREA_DEBUG) + qDebug( "insert a new line" ); +#endif + if ( index < (int)dockWindows->count() ) { +#if defined(TQDOCKAREA_DEBUG) + qDebug( "give the widget at %d a newline", index ); +#endif + TQDockWindow* nldw = dockWindows->at( index ); + if ( nldw ) + nldw->setNewLine( TRUE ); + } +#if defined(TQDOCKAREA_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() != TQt::Horizontal ) + w->setFixedExtentWidth( TQMIN( TQMAX( w->minimumWidth(), mse ), w->sizeHint().width() ) ); + else + w->setFixedExtentHeight( TQMIN( TQMAX( w->minimumHeight(), mse ), w->sizeHint().height() ) ); + } + + updateLayout(); + setSizePolicy( TQSizePolicy( orientation() == Horizontal ? TQSizePolicy::Expanding : TQSizePolicy::Minimum, + orientation() == Vertical ? TQSizePolicy::Expanding : TQSizePolicy::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 + TQDockWindow::dock() and TQDockWindow::undock() instead. +*/ + +void TQDockArea::removeDockWindow( TQDockWindow *w, bool makeFloating, bool swap, bool fixNewLines ) +{ + w->removeEventFilter( this ); + TQDockWindow *dockWindow = 0; + int i = findDockWindow( w ); + if ( i == -1 ) + return; + dockWindow = dockWindows->at( i ); + dockWindows->remove( i ); + TQPtrList<TQDockWindow> lineStarts = layout->lineStarts(); + if ( fixNewLines && lineStarts.findRef( dockWindow ) != -1 && i < (int)dockWindows->count() ) + dockWindows->at( i )->setNewLine( TRUE ); + if ( makeFloating ) { + TQWidget *p = parentWidget() ? parentWidget() : topLevelWidget(); + dockWindow->reparent( p, WType_Dialog | WStyle_Customize | WStyle_NoBorder | WStyle_Tool, TQPoint( 0, 0 ), FALSE ); + } + if ( swap ) + dockWindow->resize( dockWindow->height(), dockWindow->width() ); + updateLayout(); + if ( dockWindows->isEmpty() ) + setSizePolicy( TQSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Preferred ) ); +} + +int TQDockArea::findDockWindow( TQDockWindow *w ) +{ + return dockWindows ? dockWindows->findRef( w ) : -1; +} + +void TQDockArea::updateLayout() +{ + layout->invalidate(); + layout->activate(); +} + +/*! \reimp + */ + +bool TQDockArea::eventFilter( TQObject *o, TQEvent *e ) +{ + if ( e->type() == TQEvent::Close ) { + if ( ::qt_cast<TQDockWindow*>(o) ) { + o->removeEventFilter( this ); + TQApplication::sendEvent( o, e ); + if ( ( (TQCloseEvent*)e )->isAccepted() ) + removeDockWindow( (TQDockWindow*)o, FALSE, FALSE ); + return TRUE; + } + } + return FALSE; +} + +/*! \internal + + Invalidates the offset of the next dock window in the dock area. + */ + +void TQDockArea::invalidNextOffset( TQDockWindow *dw ) +{ + int i = dockWindows->find( dw ); + if ( i == -1 || i >= (int)dockWindows->count() - 1 ) + return; + if ( ( dw = dockWindows->at( ++i ) ) ) + dw->setOffset( 0 ); +} + +/*! + \property TQDockArea::count + \brief the number of dock windows in the dock area +*/ +int TQDockArea::count() const +{ + return dockWindows->count(); +} + +/*! + \property TQDockArea::empty + \brief whether the dock area is empty +*/ + +bool TQDockArea::isEmpty() const +{ + return dockWindows->isEmpty(); +} + + +/*! + Returns a list of the dock windows in the dock area. +*/ + +TQPtrList<TQDockWindow> TQDockArea::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 TQDockArea::lineUp( bool keepNewLines ) +{ + for ( TQDockWindow *dw = dockWindows->first(); dw; dw = dockWindows->next() ) { + dw->setOffset( 0 ); + if ( !keepNewLines ) + dw->setNewLine( FALSE ); + } + layout->activate(); +} + +TQDockArea::DockWindowData *TQDockArea::dockWindowData( TQDockWindow *w ) +{ + DockWindowData *data = new DockWindowData; + data->index = findDockWindow( w ); + if ( data->index == -1 ) { + delete data; + return 0; + } + TQPtrList<TQDockWindow> lineStarts = layout->lineStarts(); + int i = -1; + for ( TQDockWindow *dw = dockWindows->first(); dw; dw = dockWindows->next() ) { + if ( lineStarts.findRef( dw ) != -1 ) + ++i; + if ( dw == w ) + break; + } + data->line = i; + data->offset = point_pos( TQPoint( fix_x(w), w->y() ), orientation() ); + data->area = this; + data->fixedExtent = w->fixedExtent(); + return data; +} + +void TQDockArea::dockWindow( TQDockWindow *dockWindow, DockWindowData *data ) +{ + if ( !data ) + return; + + dockWindow->reparent( this, TQPoint( 0, 0 ), FALSE ); + dockWindow->installEventFilter( this ); + dockWindow->dockArea = this; + dockWindow->updateGui(); + + if ( dockWindows->isEmpty() ) { + dockWindows->append( dockWindow ); + } else { + TQPtrList<TQDockWindow> 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 ( TQDockWindow *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( TQSizePolicy( orientation() == Horizontal ? TQSizePolicy::Expanding : TQSizePolicy::Minimum, + orientation() == Vertical ? TQSizePolicy::Expanding : TQSizePolicy::Minimum ) ); + +} + +/*! + Returns TRUE if dock window \a dw could be docked into the dock + area; otherwise returns FALSE. + + \sa setAcceptDockWindow() +*/ + +bool TQDockArea::isDockWindowAccepted( TQDockWindow *dw ) +{ + if ( !dw ) + return FALSE; + if ( forbiddenWidgets.findRef( dw ) != -1 ) + return FALSE; + + TQMainWindow *mw = ::qt_cast<TQMainWindow*>(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 TQDockArea::setAcceptDockWindow( TQDockWindow *dw, bool accept ) +{ + if ( accept ) + forbiddenWidgets.removeRef( dw ); + else if ( forbiddenWidgets.findRef( dw ) == -1 ) + forbiddenWidgets.append( dw ); +} + +void TQDockArea::invalidateFixedSizes() +{ + for ( TQDockWindow *dw = dockWindows->first(); dw; dw = dockWindows->next() ) { + if ( orientation() == TQt::Horizontal ) + dw->setFixedExtentWidth( -1 ); + else + dw->setFixedExtentHeight( -1 ); + } +} + +int TQDockArea::maxSpace( int hint, TQDockWindow *dw ) +{ + int index = findDockWindow( dw ); + if ( index == -1 || index + 1 >= (int)dockWindows->count() ) { + if ( orientation() == Horizontal ) + return dw->width(); + return dw->height(); + } + + TQDockWindow *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; + TQToolBar *tb = ::qt_cast<TQToolBar*>(w); + if ( orientation() == Horizontal ) { + w->setFixedExtentWidth( -1 ); + if ( !tb ) + min = TQMAX( w->minimumSize().width(), w->minimumSizeHint().width() ); + else + min = w->sizeHint().width(); + } else { + w->setFixedExtentHeight( -1 ); + if ( !tb ) + min = TQMAX( 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 TQDockArea::setFixedExtent( int d, TQDockWindow *dw ) +{ + TQPtrList<TQDockWindow> lst; + TQDockWindow *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 = TQMAX( d, w->minimumHeight() ); + else + d = TQMAX( 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 TQDockArea::isLastDockWindow( TQDockWindow *dw ) +{ + int i = dockWindows->find( dw ); + if ( i == -1 || i >= (int)dockWindows->count() - 1 ) + return TRUE; + TQDockWindow *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 TQDockArea + + Writes the layout of the dock windows in dock area \a dockArea to + the text stream \a ts. + + \sa operator>>() +*/ + +TQTextStream &operator<<( TQTextStream &ts, const TQDockArea &dockArea ) +{ + TQString str; + TQPtrList<TQDockWindow> l = dockArea.dockWindowList(); + + for ( TQDockWindow *dw = l.first(); dw; dw = l.next() ) + str += "[" + TQString( dw->caption() ) + "," + TQString::number( (int)dw->offset() ) + + "," + TQString::number( (int)dw->newLine() ) + "," + TQString::number( dw->fixedExtent().width() ) + + "," + TQString::number( dw->fixedExtent().height() ) + "," + TQString::number( (int)!dw->isHidden() ) + "]"; + ts << str << endl; + + return ts; +} + +/*! + \relates TQDockArea + + 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<<() +*/ + +TQTextStream &operator>>( TQTextStream &ts, TQDockArea &dockArea ) +{ + TQString s = ts.readLine(); + + TQString name, offset, newLine, width, height, visible; + + enum State { Pre, Name, Offset, NewLine, Width, Height, Visible, Post }; + int state = Pre; + TQChar c; + TQPtrList<TQDockWindow> 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 ( TQDockWindow *dw = l.first(); dw; dw = l.next() ) { + if ( TQString( 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.TQWidget::layout()->invalidate(); + dockArea.TQWidget::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 000000000..11f6795c0 --- /dev/null +++ b/src/widgets/qdockarea.h @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Definition of the TQDockArea class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQDOCKAREA_H +#define TQDOCKAREA_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 TQSplitter; +class TQBoxLayout; +class TQDockAreaLayout; +class TQMouseEvent; +class TQDockWindowResizeHandle; +class TQDockAreaPrivate; + +class Q_EXPORT TQDockAreaLayout : public TQLayout +{ + Q_OBJECT + friend class TQDockArea; + +public: + TQDockAreaLayout( TQWidget* parent, TQt::Orientation o, TQPtrList<TQDockWindow> *wl, int space = -1, int margin = -1, const char *name = 0 ) + : TQLayout( parent, space, margin, name ), orient( o ), dockWindows( wl ), parentWidget( parent ) { init(); } + ~TQDockAreaLayout() {} + + void addItem( TQLayoutItem * ) {} + bool hasHeightForWidth() const; + int heightForWidth( int ) const; + int widthForHeight( int ) const; + TQSize sizeHint() const; + TQSize minimumSize() const; + TQLayoutIterator iterator(); + TQSizePolicy::ExpandData expanding() const { return TQSizePolicy::NoDirection; } + void invalidate(); + TQt::Orientation orientation() const { return orient; } + TQValueList<TQRect> lineList() const { return lines; } + TQPtrList<TQDockWindow> lineStarts() const { return ls; } + +protected: + void setGeometry( const TQRect& ); + +private: + void init(); + int layoutItems( const TQRect&, bool testonly = FALSE ); + TQt::Orientation orient; + bool dirty; + int cached_width, cached_height; + int cached_hfw, cached_wfh; + TQPtrList<TQDockWindow> *dockWindows; + TQWidget *parentWidget; + TQValueList<TQRect> lines; + TQPtrList<TQDockWindow> ls; +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQDockAreaLayout( const TQDockAreaLayout & ); + TQDockAreaLayout &operator=( const TQDockAreaLayout & ); +#endif +}; + +class Q_EXPORT TQDockArea : public TQWidget +{ + 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 TQDockWindow; + friend class TQDockWindowResizeHandle; + friend class TQDockAreaLayout; + +public: + enum HandlePosition { Normal, Reverse }; + + TQDockArea( Orientation o, HandlePosition h = Normal, TQWidget* parent=0, const char* name=0 ); + ~TQDockArea(); + + void moveDockWindow( TQDockWindow *w, const TQPoint &globalPos, const TQRect &rect, bool swap ); + void removeDockWindow( TQDockWindow *w, bool makeFloating, bool swap, bool fixNewLines = TRUE ); + void moveDockWindow( TQDockWindow *w, int index = -1 ); + bool hasDockWindow( TQDockWindow *w, int *index = 0 ); + + void invalidNextOffset( TQDockWindow *dw ); + + Orientation orientation() const { return orient; } + HandlePosition handlePosition() const { return hPos; } + + bool eventFilter( TQObject *, TQEvent * ); + bool isEmpty() const; + int count() const; + TQPtrList<TQDockWindow> dockWindowList() const; + + bool isDockWindowAccepted( TQDockWindow *dw ); + void setAcceptDockWindow( TQDockWindow *dw, bool accept ); + +public slots: + void lineUp( bool keepNewLines ); + +private: + struct DockWindowData + { + int index; + int offset; + int line; + TQSize fixedExtent; + TQGuardedPtr<TQDockArea> area; + }; + + int findDockWindow( TQDockWindow *w ); + int lineOf( int index ); + DockWindowData *dockWindowData( TQDockWindow *w ); + void dockWindow( TQDockWindow *dockWindow, DockWindowData *data ); + void updateLayout(); + void invalidateFixedSizes(); + int maxSpace( int hint, TQDockWindow *dw ); + void setFixedExtent( int d, TQDockWindow *dw ); + bool isLastDockWindow( TQDockWindow *dw ); + +private: + Orientation orient; + TQPtrList<TQDockWindow> *dockWindows; + TQDockAreaLayout *layout; + HandlePosition hPos; + TQPtrList<TQDockWindow> forbiddenWidgets; + TQDockAreaPrivate *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQDockArea( const TQDockArea & ); + TQDockArea& operator=( const TQDockArea & ); +#endif + +}; + +#ifndef QT_NO_TEXTSTREAM +Q_EXPORT TQTextStream &operator<<( TQTextStream &, const TQDockArea & ); +Q_EXPORT TQTextStream &operator>>( TQTextStream &, TQDockArea & ); +#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 000000000..5818fa40f --- /dev/null +++ b/src/widgets/qdockwindow.cpp @@ -0,0 +1,2123 @@ +/**************************************************************************** +** +** Implementation of the TQDockWindow class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQDockWindowPrivate +{ +}; + +class TQDockWindowResizeHandle : public TQWidget +{ + Q_OBJECT + +public: + TQDockWindowResizeHandle( TQt::Orientation o, TQWidget *parent, TQDockWindow *w, const char* /*name*/=0 ); + void setOrientation( TQt::Orientation o ); + TQt::Orientation orientation() const { return orient; } + + TQSize sizeHint() const; + +protected: + void paintEvent( TQPaintEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + +private: + void startLineDraw(); + void endLineDraw(); + void drawLine( const TQPoint &globalPos ); + +private: + TQt::Orientation orient; + bool mousePressed; + TQPainter *unclippedPainter; + TQPoint lastPos, firstPos; + TQDockWindow *dockWindow; + +}; + +TQDockWindowResizeHandle::TQDockWindowResizeHandle( TQt::Orientation o, TQWidget *parent, + TQDockWindow *w, const char * ) + : TQWidget( parent, "qt_dockwidget_internal" ), mousePressed( FALSE ), unclippedPainter( 0 ), dockWindow( w ) +{ + setOrientation( o ); +} + +TQSize TQDockWindowResizeHandle::sizeHint() const +{ + int sw = 2 * style().pixelMetric(TQStyle::PM_SplitterWidth, this) / 3; + return (style().sizeFromContents(TQStyle::CT_DockWindow, this, TQSize(sw, sw)). + expandedTo(TQApplication::globalStrut())); +} + +void TQDockWindowResizeHandle::setOrientation( TQt::Orientation o ) +{ + orient = o; + if ( o == TQDockArea::Horizontal ) { +#ifndef QT_NO_CURSOR + setCursor( splitVCursor ); +#endif + setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed ) ); + } else { +#ifndef QT_NO_CURSOR + setCursor( splitHCursor ); +#endif + setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Expanding ) ); + } +} + +void TQDockWindowResizeHandle::mousePressEvent( TQMouseEvent *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 TQDockWindowResizeHandle::mouseMoveEvent( TQMouseEvent *e ) +{ + if ( !mousePressed ) + return; + if ( !dockWindow->opaqueMoving() ) { + if ( orientation() != dockWindow->area()->orientation() ) { + if ( orientation() == Horizontal ) { + int minpos = dockWindow->area()->mapToGlobal( TQPoint( 0, 0 ) ).y(); + int maxpos = dockWindow->area()->mapToGlobal( TQPoint( 0, 0 ) ).y() + dockWindow->area()->height(); + if ( e->globalPos().y() < minpos || e->globalPos().y() > maxpos ) + return; + } else { + int minpos = dockWindow->area()->mapToGlobal( TQPoint( 0, 0 ) ).x(); + int maxpos = dockWindow->area()->mapToGlobal( TQPoint( 0, 0 ) ).x() + dockWindow->area()->width(); + if ( e->globalPos().x() < minpos || e->globalPos().x() > maxpos ) + return; + } + } else { + TQWidget *w = dockWindow->area()->topLevelWidget(); + if ( w ) { + if ( orientation() == Horizontal ) { + int minpos = w->mapToGlobal( TQPoint( 0, 0 ) ).y(); + int maxpos = w->mapToGlobal( TQPoint( 0, 0 ) ).y() + w->height(); + if ( e->globalPos().y() < minpos || e->globalPos().y() > maxpos ) + return; + } else { + int minpos = w->mapToGlobal( TQPoint( 0, 0 ) ).x(); + int maxpos = w->mapToGlobal( TQPoint( 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 TQDockWindowResizeHandle::mouseReleaseEvent( TQMouseEvent *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() == TQDockArea::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 = TQMAX( d, dockWindow->minimumHeight() ); + int ms = dockWindow->area()->maxSpace( d, dockWindow ); + d = TQMIN( d, ms ); + dockWindow->setFixedExtentHeight( d ); + } else { + dockWindow->area()->setFixedExtent( d, dockWindow ); + } + } else { + int dx; + if ( dockWindow->area()->handlePosition() == TQDockArea::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 = TQMAX( d, dockWindow->minimumWidth() ); + int ms = dockWindow->area()->maxSpace( d, dockWindow ); + d = TQMIN( d, ms ); + dockWindow->setFixedExtentWidth( d ); + } else { + dockWindow->area()->setFixedExtent( d, dockWindow ); + } + } + } + + TQApplication::postEvent( dockWindow->area(), new TQEvent( TQEvent::LayoutHint ) ); + mousePressed = FALSE; +} + +void TQDockWindowResizeHandle::paintEvent( TQPaintEvent * ) +{ + TQPainter p( this ); + style().drawPrimitive(TQStyle::PE_DockWindowResizeHandle, &p, rect(), colorGroup(), + (isEnabled() ? + TQStyle::Style_Enabled : TQStyle::Style_Default) | + (orientation() == TQt::Horizontal ? + TQStyle::Style_Horizontal : TQStyle::Style_Default )); +} + +void TQDockWindowResizeHandle::startLineDraw() +{ + if ( unclippedPainter ) + endLineDraw(); +#ifdef MAC_DRAG_HACK + TQWidget *paint_on = topLevelWidget(); +#else + int scr = TQApplication::desktop()->screenNumber( this ); + TQWidget *paint_on = TQApplication::desktop()->screen( scr ); +#endif + unclippedPainter = new TQPainter( paint_on, TRUE ); + unclippedPainter->setPen( TQPen( gray, orientation() == Horizontal ? height() : width() ) ); + unclippedPainter->setRasterOp( XorROP ); +} + +void TQDockWindowResizeHandle::endLineDraw() +{ + if ( !unclippedPainter ) + return; + delete unclippedPainter; + unclippedPainter = 0; +} + +void TQDockWindowResizeHandle::drawLine( const TQPoint &globalPos ) +{ +#ifdef MAC_DRAG_HACK + TQPoint start = mapTo(topLevelWidget(), TQPoint(0, 0)); + TQPoint starta = dockWindow->area()->mapTo(topLevelWidget(), TQPoint(0, 0)); + TQPoint end = globalPos - topLevelWidget()->pos(); +#else + TQPoint start = mapToGlobal( TQPoint( 0, 0 ) ); + TQPoint starta = dockWindow->area()->mapToGlobal( TQPoint( 0, 0 ) ); + TQPoint 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 TQPoint realWidgetPos( TQDockWindow *w ) +{ + if ( !w->parentWidget() || w->place() == TQDockWindow::OutsideDock ) + return w->pos(); + return w->parentWidget()->mapToGlobal( w->geometry().topLeft() ); +} + +class TQDockWindowHandle : public TQWidget +{ + Q_OBJECT + Q_PROPERTY( TQString caption READ caption ) + friend class TQDockWindow; + friend class TQDockWindowTitleBar; + +public: + TQDockWindowHandle( TQDockWindow *dw ); + void updateGui(); + + TQSize minimumSizeHint() const; + TQSize minimumSize() const { return minimumSizeHint(); } + TQSize sizeHint() const { return minimumSize(); } + TQSizePolicy sizePolicy() const; + void setOpaqueMoving( bool b ) { opaque = b; } + + TQString caption() const { return dockWindow->caption(); } + +signals: + void doubleClicked(); + +protected: + void paintEvent( TQPaintEvent *e ); + void resizeEvent( TQResizeEvent *e ); + void mousePressEvent( TQMouseEvent *e ); + void mouseMoveEvent( TQMouseEvent *e ); + void mouseReleaseEvent( TQMouseEvent *e ); + void mouseDoubleClickEvent( TQMouseEvent *e ); + void keyPressEvent( TQKeyEvent *e ); + void keyReleaseEvent( TQKeyEvent *e ); +#ifndef QT_NO_STYLE + void styleChange( TQStyle& ); +#endif + +private slots: + void minimize(); + +private: + TQDockWindow *dockWindow; + TQPoint offset; + TQToolButton *closeButton; + TQTimer *timer; + uint opaque : 1; + uint mousePressed : 1; + uint hadDblClick : 1; + uint ctrlDown : 1; + TQGuardedPtr<TQWidget> oldFocus; +}; + +class TQDockWindowTitleBar : public TQTitleBar +{ + Q_OBJECT + friend class TQDockWindow; + friend class TQDockWindowHandle; + +public: + TQDockWindowTitleBar( TQDockWindow *dw ); + void updateGui(); + void setOpaqueMoving( bool b ) { opaque = b; } + +protected: + void resizeEvent( TQResizeEvent *e ); + void mousePressEvent( TQMouseEvent *e ); + void mouseMoveEvent( TQMouseEvent *e ); + void mouseReleaseEvent( TQMouseEvent *e ); + void mouseDoubleClickEvent( TQMouseEvent *e ); + void keyPressEvent( TQKeyEvent *e ); + void keyReleaseEvent( TQKeyEvent *e ); + +private: + TQDockWindow *dockWindow; + TQPoint offset; + uint mousePressed : 1; + uint hadDblClick : 1; + uint opaque : 1; + uint ctrlDown : 1; + TQGuardedPtr<TQWidget> oldFocus; + +}; + +TQDockWindowHandle::TQDockWindowHandle( TQDockWindow *dw ) + : TQWidget( dw, "qt_dockwidget_internal", WNoAutoErase ), dockWindow( dw ), + closeButton( 0 ), opaque( default_opaque ), mousePressed( FALSE ) +{ + ctrlDown = FALSE; + timer = new TQTimer( this ); + connect( timer, SIGNAL( timeout() ), this, SLOT( minimize() ) ); +#ifdef Q_WS_WIN + setCursor( SizeAllCursor ); +#endif +} + +void TQDockWindowHandle::paintEvent( TQPaintEvent *e ) +{ + if ( (!dockWindow->dockArea || mousePressed) && !opaque ) + return; + erase(); + TQPainter p( this ); + TQStyle::SFlags flags = TQStyle::Style_Default; + if ( isEnabled() ) + flags |= TQStyle::Style_Enabled; + if ( !dockWindow->area() || dockWindow->area()->orientation() == Horizontal ) + flags |= TQStyle::Style_Horizontal; + + style().drawPrimitive( TQStyle::PE_DockWindowHandle, &p, + TQStyle::visualRect( style().subRect( TQStyle::SR_DockWindowHandleRect, + this ), this ), + colorGroup(), flags ); + TQWidget::paintEvent( e ); +} + +void TQDockWindowHandle::keyPressEvent( TQKeyEvent *e ) +{ + if ( !mousePressed ) + return; + if ( e->key() == Key_Control ) { + ctrlDown = TRUE; + dockWindow->handleMove( mapFromGlobal(TQCursor::pos()) - offset, TQCursor::pos(), !opaque ); + } +} + +void TQDockWindowHandle::keyReleaseEvent( TQKeyEvent *e ) +{ + if ( !mousePressed ) + return; + if ( e->key() == Key_Control ) { + ctrlDown = FALSE; + dockWindow->handleMove( mapFromGlobal(TQCursor::pos()) - offset, TQCursor::pos(), !opaque ); + } +} + +void TQDockWindowHandle::mousePressEvent( TQMouseEvent *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 TQDockWindowHandle::mouseMoveEvent( TQMouseEvent *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 TQDockWindowHandle::mouseReleaseEvent( TQMouseEvent *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( TQApplication::doubleClickInterval(), TRUE ); + } else if ( !hadDblClick ) { + dockWindow->updatePosition( e->globalPos() ); + } + if ( opaque ) + dockWindow->titleBar->mousePressed = FALSE; +} + +void TQDockWindowHandle::minimize() +{ + if ( !dockWindow->area() ) + return; + + TQMainWindow *mw = ::qt_cast<TQMainWindow*>(dockWindow->area()->parentWidget()); + if ( mw && mw->isDockEnabled( dockWindow, TQt::DockMinimized ) ) + mw->moveDockWindow( dockWindow, TQt::DockMinimized ); +} + +void TQDockWindowHandle::resizeEvent( TQResizeEvent * ) +{ + updateGui(); +} + +void TQDockWindowHandle::updateGui() +{ + if ( !closeButton ) { + closeButton = new TQToolButton( this, "qt_close_button1" ); +#ifndef QT_NO_CURSOR + closeButton->setCursor( arrowCursor ); +#endif + closeButton->setPixmap( style().stylePixmap( TQStyle::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 = TQApplication::reverseLayout() ? 2 : width() - closeButton->width() - 2; + closeButton->move( x, off ); + } +} + +#ifndef QT_NO_STYLE +void TQDockWindowHandle::styleChange( TQStyle& ) +{ + if ( closeButton ) + closeButton->setPixmap( style().stylePixmap( TQStyle::SP_DockWindowCloseButton, closeButton ) ); +} +#endif + +TQSize TQDockWindowHandle::minimumSizeHint() const +{ + if ( !dockWindow->dockArea ) + return TQSize( 0, 0 ); + int wh = dockWindow->isCloseEnabled() ? 17 : style().pixelMetric( TQStyle::PM_DockWindowHandleExtent, this ); + if ( dockWindow->orientation() == Horizontal ) + return TQSize( wh, 0 ); + return TQSize( 0, wh ); +} + +TQSizePolicy TQDockWindowHandle::sizePolicy() const +{ + if ( dockWindow->orientation() != Horizontal ) + return TQSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Fixed ); + return TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Preferred ); +} + +void TQDockWindowHandle::mouseDoubleClickEvent( TQMouseEvent *e ) +{ + e->ignore(); + if ( e->button() != LeftButton ) + return; + e->accept(); + timer->stop(); + emit doubleClicked(); + hadDblClick = TRUE; +} + +TQDockWindowTitleBar::TQDockWindowTitleBar( TQDockWindow *dw ) + : TQTitleBar( 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( TQStyle::PM_TitleBarHeight, this ) ); + connect( this, SIGNAL(doClose()), dockWindow, SLOT(hide()) ); +} + +void TQDockWindowTitleBar::keyPressEvent( TQKeyEvent *e ) +{ + if ( !mousePressed ) + return; + if ( e->key() == Key_Control ) { + ctrlDown = TRUE; + dockWindow->handleMove( mapFromGlobal( TQCursor::pos() ) - offset, TQCursor::pos(), !opaque ); + } +} + +void TQDockWindowTitleBar::keyReleaseEvent( TQKeyEvent *e ) +{ + if ( !mousePressed ) + return; + if ( e->key() == Key_Control ) { + ctrlDown = FALSE; + dockWindow->handleMove( mapFromGlobal( TQCursor::pos() ) - offset, TQCursor::pos(), !opaque ); + } +} + +void TQDockWindowTitleBar::mousePressEvent( TQMouseEvent *e ) +{ + TQStyle::SubControl tbctrl = style().querySubControl( TQStyle::CC_TitleBar, this, e->pos() ); + if ( tbctrl > TQStyle::SC_TitleBarLabel ) { + TQTitleBar::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 retquired 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 retquired 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 TQDockWindowTitleBar::mouseMoveEvent( TQMouseEvent *e ) +{ + if ( !mousePressed ) { + TQTitleBar::mouseMoveEvent( e ); + return; + } + + ctrlDown = ( e->state() & ControlButton ) == ControlButton; + e->accept(); + dockWindow->handleMove( e->pos() - offset, e->globalPos(), !opaque ); +} + +void TQDockWindowTitleBar::mouseReleaseEvent( TQMouseEvent *e ) +{ + if ( !mousePressed ) { + TQTitleBar::mouseReleaseEvent( e ); + return; + } + + ctrlDown = FALSE; + qApp->removeEventFilter( dockWindow ); + if ( oldFocus ) + oldFocus->setFocus(); + + if ( dockWindow->place() == TQDockWindow::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 TQDockWindowTitleBar::resizeEvent( TQResizeEvent *e ) +{ + updateGui(); + TQTitleBar::resizeEvent( e ); +} + +void TQDockWindowTitleBar::updateGui() +{ + if ( dockWindow->isCloseEnabled() ) { + setWFlags( getWFlags() | WStyle_SysMenu ); + } else { + setWFlags( getWFlags() & ~WStyle_SysMenu ); + } +} + +void TQDockWindowTitleBar::mouseDoubleClickEvent( TQMouseEvent * ) +{ + emit doubleClicked(); + hadDblClick = TRUE; +} + +/*! + \class TQDockWindow qdockwindow.h + \brief The TQDockWindow class provides a widget which can be docked + inside a TQDockArea or floated as a top level window on the + desktop. + + \ingroup application + \mainclass + + This class handles moving, resizing, docking and undocking dock + windows. TQToolBar is a subclass of TQDockWindow so the + functionality provided for dock windows is available with the same + API for toolbars. + + \img qmainwindow-qdockareas.png TQDockWindows in a TQDockArea + \caption Two TQDockWindows (\l{TQToolBar}s) in a \l TQDockArea + + \img qdockwindow.png A TQDockWindow + \caption A Floating TQDockWindow + + 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(). + + TQMainWindow 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 TQMainWindow is sufficient. (See the \l + TQDockArea documentation if you want to create your own dock + areas.) In TQMainWindow 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 TQDockArea + or a TQMainWindow as its parent if you want it docked. Pass 0 for + the parent if you want it floated. + + \code + TQToolBar *fileTools = new TQToolBar( this, "File Actions" ); + moveDockWindow( fileTools, Left ); + \endcode + + In the example above we create a new TQToolBar in the constructor + of a TQMainWindow subclass (so that the \e this pointer points to + the TQMainWindow). 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 TQBoxLayout::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 TQt + Designer\endlink and \link linguist-manual.book TQt + 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 TQDockArea::moveDockWindow(). If you're using a + TQMainWindow, TQMainWindow::moveDockWindow() and + TQMainWindow::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 TQDockWindow::Place + + This enum specifies the possible locations for a TQDockWindow: + + \value InDock Inside a TQDockArea. + \value OutsideDock Floating as a top level window on the desktop. +*/ + +/*! + \enum TQDockWindow::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 TQDockWindow::setHorizontalStretchable( bool b ) + \obsolete +*/ +/*! + \fn void TQDockWindow::setVerticalStretchable( bool b ) + \obsolete +*/ +/*! + \fn bool TQDockWindow::isHorizontalStretchable() const + \obsolete +*/ +/*! + \fn bool TQDockWindow::isVerticalStretchable() const + \obsolete +*/ +/*! + \fn void TQDockWindow::orientationChanged( Orientation o ) + + This signal is emitted when the orientation of the dock window is + changed. The new orientation is \a o. +*/ + +/*! + \fn void TQDockWindow::placeChanged( TQDockWindow::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 TQDockArea::moveDockWindow(), TQDockArea::removeDockWindow(), + TQMainWindow::moveDockWindow(), TQMainWindow::removeDockWindow() +*/ + +/*! + \fn void TQDockWindow::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 + TQDockWindow 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 TQMainWindow a dock window can + have its visibility changed (hidden or shown) by clicking its name + in the dock window menu that lists the TQMainWindow's dock windows. +*/ + +/*! + \fn TQDockArea *TQDockWindow::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 TQDockWindow::place() const + + This function returns where the dock window is placed. This is + either \c InDock or \c OutsideDock. + + \sa TQDockArea::moveDockWindow(), TQDockArea::removeDockWindow(), + TQMainWindow::moveDockWindow(), TQMainWindow::removeDockWindow() +*/ + + +/*! + Constructs a TQDockWindow with parent \a parent, called \a name and + with widget flags \a f. +*/ + +TQDockWindow::TQDockWindow( TQWidget* parent, const char* name, WFlags f ) + : TQFrame( parent, name, f | WType_Dialog | WStyle_Customize | WStyle_NoBorder ) +{ + curPlace = InDock; + isToolbar = FALSE; + init(); +} + +/*! + Constructs a TQDockWindow 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 TQDockArea or a TQMainWindow. If the \a + parent is a TQMainWindow 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 TQMainWindow + as parent then calling TQMainWindow::moveDockWindow() to move the + dock window where you want it. +*/ + +TQDockWindow::TQDockWindow( Place p, TQWidget *parent, const char *name, WFlags f ) + : TQFrame( parent, name, f | WType_Dialog | WStyle_Customize | WStyle_NoBorder ) +{ + curPlace = p; + isToolbar = FALSE; + init(); +} + +/*! \internal +*/ + +TQDockWindow::TQDockWindow( Place p, TQWidget *parent, const char *name, WFlags f, bool toolbar ) + : TQFrame( parent, name, f | WType_Dialog | WStyle_Customize | WStyle_NoBorder ) +{ + curPlace = p; + isToolbar = toolbar; + init(); +} + +class TQDockWindowGridLayout : public TQGridLayout +{ +public: + TQDockWindowGridLayout( TQWidget *parent, int nRows, int nCols ) + : TQGridLayout( parent, nRows, nCols ) {}; + + TQSizePolicy::ExpandData expanding() const + { + return TQSizePolicy::NoDirection; + } +}; + +void TQDockWindow::init() +{ + wid = 0; + unclippedPainter = 0; + dockArea = 0; + tmpDockArea = 0; + resizeEnabled = FALSE; + moveEnabled = TRUE; + nl = FALSE; + opaque = default_opaque; + cMode = Never; + offs = 0; + fExtent = TQSize( -1, -1 ); + dockWindowData = 0; + lastPos = TQPoint( -1, -1 ); + lastSize = TQSize( -1, -1 ); + + widgetResizeHandler = new TQWidgetResizeHandler( this ); + widgetResizeHandler->setMovingEnabled( FALSE ); + + titleBar = new TQDockWindowTitleBar( this ); + verHandle = new TQDockWindowHandle( this ); + horHandle = new TQDockWindowHandle( this ); + + vHandleLeft = new TQDockWindowResizeHandle( TQt::Vertical, this, this, "vert. handle" ); + vHandleRight = new TQDockWindowResizeHandle( TQt::Vertical, this, this, "vert. handle" ); + hHandleTop = new TQDockWindowResizeHandle( TQt::Horizontal, this, this, "horz. handle" ); + hHandleBottom = new TQDockWindowResizeHandle( TQt::Horizontal, this, this, "horz. handle" ); + + // Creating inner layout + hbox = new TQVBoxLayout(); + vbox = new TQHBoxLayout(); + childBox = new TQBoxLayout(TQBoxLayout::LeftToRight); + vbox->addWidget( verHandle ); + vbox->addLayout( childBox ); + + hbox->setResizeMode( TQLayout::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() + TQGridLayout *glayout = new TQDockWindowGridLayout( this, 3, 3 ); + glayout->setResizeMode( TQLayout::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( TQFrame::StyledPanel | TQFrame::Raised ); + setLineWidth( 2 ); + + if ( parentWidget() ) + parentWidget()->installEventFilter( this ); + TQWidget *mw = parentWidget(); + TQDockArea *da = ::qt_cast<TQDockArea*>(parentWidget()); + if ( da ) { + if ( curPlace == InDock ) + da->moveDockWindow( this ); + mw = da->parentWidget(); + } + if ( ::qt_cast<TQMainWindow*>(mw) ) { + if ( place() == InDock ) { + Dock myDock = TQt::DockTop; + // make sure we put the window in the correct dock. + if ( dockArea ) { + TQMainWindow *mainw = (TQMainWindow*)mw; + // I'm not checking if it matches the top because I've + // done the assignment to it above. + if ( dockArea == mainw->leftDock() ) + myDock = TQt::DockLeft; + else if ( dockArea == mainw->rightDock() ) + myDock = TQt::DockRight; + else if ( dockArea == mainw->bottomDock() ) + myDock = TQt::DockBottom; + } + ( (TQMainWindow*)mw )->addDockWindow( this, myDock ); + } + moveEnabled = ((TQMainWindow*)mw)->dockWindowsMovable(); + opaque = ((TQMainWindow*)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 TQToolBars will always have a horizontal orientation. +*/ + +void TQDockWindow::setOrientation( Orientation o ) +{ + TQGridLayout *glayout = (TQGridLayout*)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 ? TQBoxLayout::LeftToRight : TQBoxLayout::TopToBottom ); + TQApplication::sendPostedEvents( this, TQEvent::LayoutHint ); + TQEvent *e = new TQEvent( TQEvent::LayoutHint ); + TQApplication::postEvent( this, e ); +} + +/*! + \reimp + + Destroys the dock window and its child widgets. +*/ + +TQDockWindow::~TQDockWindow() +{ + qApp->removeEventFilter( this ); + if ( area() ) + area()->removeDockWindow( this, FALSE, FALSE ); + TQDockArea *a = area(); + if ( !a && dockWindowData ) + a = ( (TQDockArea::DockWindowData*)dockWindowData )->area; + TQMainWindow *mw = a ? ::qt_cast<TQMainWindow*>(a->parentWidget()) : 0; + if ( mw ) + mw->removeDockWindow( this ); + + delete (TQDockArea::DockWindowData*)dockWindowData; +} + +/*! \reimp +*/ + +void TQDockWindow::resizeEvent( TQResizeEvent *e ) +{ + TQFrame::resizeEvent( e ); + updateGui(); +} + + +void TQDockWindow::swapRect( TQRect &r, TQt::Orientation o, const TQPoint &offset, TQDockArea * ) +{ + TQBoxLayout *bl = boxLayout()->createTmpCopy(); + bl->setDirection( o == Horizontal ? TQBoxLayout::LeftToRight : TQBoxLayout::TopToBottom ); + bl->activate(); + r.setSize( bl->sizeHint() ); + bl->data = 0; + delete bl; + bool reverse = TQApplication::reverseLayout(); + if ( o == TQt::Horizontal ) + r.moveBy( -r.width()/2, 0 ); + else + r.moveBy( reverse ? - r.width() : 0, -r.height() / 2 ); + r.moveBy( offset.x(), offset.y() ); +} + +TQWidget *TQDockWindow::areaAt( const TQPoint &gp ) +{ + TQWidget *w = qApp->widgetAt( gp, TRUE ); + + if ( w && ( w == this || w == titleBar ) && parentWidget() ) + w = parentWidget()->childAt( parentWidget()->mapFromGlobal( gp ) ); + + while ( w ) { + if ( ::qt_cast<TQDockArea*>(w) ) { + TQDockArea *a = (TQDockArea*)w; + if ( a->isDockWindowAccepted( this ) ) + return w; + } + if ( ::qt_cast<TQMainWindow*>(w) ) { + TQMainWindow *mw = (TQMainWindow*)w; + TQDockArea *a = mw->dockingArea( mw->mapFromGlobal( gp ) ); + if ( a && a->isDockWindowAccepted( this ) ) + return a; + } + w = w->parentWidget( TRUE ); + } + return 0; +} + +void TQDockWindow::handleMove( const TQPoint &pos, const TQPoint &gp, bool drawRect ) +{ + if ( !unclippedPainter ) + return; + + if ( drawRect ) { + TQRect dr(currRect); +#ifdef MAC_DRAG_HACK + dr.moveBy(-topLevelWidget()->geometry().x(), -topLevelWidget()->geometry().y()); +#endif + unclippedPainter->drawRect( dr ); + } + currRect = TQRect( realWidgetPos( this ), size() ); + TQWidget *w = areaAt( gp ); + if ( titleBar->ctrlDown || horHandle->ctrlDown || verHandle->ctrlDown ) + w = 0; + currRect.moveBy( pos.x(), pos.y() ); + if ( !::qt_cast<TQDockArea*>(w) ) { + if ( startOrientation != Horizontal && ::qt_cast<TQToolBar*>(this) ) + swapRect( currRect, Horizontal, startOffset, (TQDockArea*)w ); + if ( drawRect ) { + unclippedPainter->setPen( TQPen( gray, 3 ) ); + TQRect dr(currRect); +#ifdef MAC_DRAG_HACK + dr.moveBy(-topLevelWidget()->geometry().x(), -topLevelWidget()->geometry().y()); +#endif + unclippedPainter->drawRect( dr ); + } else { + TQPoint mp( mapToGlobal( pos )); + if(place() == InDock) { + undock(); + if(titleBar) { + mp = TQPoint(titleBar->width() / 2, titleBar->height() / 2); + TQMouseEvent me(TQEvent::MouseButtonPress, mp, LeftButton, 0); + TQApplication::sendEvent(titleBar, &me); + mp = titleBar->mapToGlobal( mp ); + } + } + move( mp ); + } + state = OutsideDock; + return; + } + + TQDockArea *area = (TQDockArea*)w; + if( area->isVisible() ) { + state = InDock; + Orientation o = ( area ? area->orientation() : + ( boxLayout()->direction() == TQBoxLayout::LeftToRight || + boxLayout()->direction() == TQBoxLayout::RightToLeft ? + Horizontal : Vertical ) ); + if ( startOrientation != o ) + swapRect( currRect, o, startOffset, area ); + if ( drawRect ) { + unclippedPainter->setPen( TQPen( gray, 1 ) ); + TQRect dr(currRect); +#ifdef MAC_DRAG_HACK + dr.moveBy(-topLevelWidget()->geometry().x(), -topLevelWidget()->geometry().y()); +#endif + unclippedPainter->drawRect( dr ); + } + tmpDockArea = area; + } +} + +void TQDockWindow::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() == TQDockArea::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() == TQDockArea::Normal) != TQApplication::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 TQDockWindow::updatePosition( const TQPoint &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(); + TQApplication::sendPostedEvents(); + } + Orientation oo = orientation(); + + if ( state == InDock ) { + if ( tmpDockArea ) { + bool differentDocks = FALSE; + if ( dockArea && dockArea != tmpDockArea ) { + differentDocks = TRUE; + delete (TQDockArea::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 ) { + TQMainWindow *mw = (TQMainWindow*)dockArea->parentWidget(); + if ( ::qt_cast<TQMainWindow*>(mw) && + ( !mw->isDockEnabled( TQMainWindow::DockTornOff ) || + !mw->isDockEnabled( this, TQMainWindow::DockTornOff ) ) ) + return; + delete (TQDockArea::DockWindowData*)dockWindowData; + dockWindowData = dockArea->dockWindowData( this ); + dockArea->removeDockWindow( this, TRUE, + startOrientation != Horizontal && ::qt_cast<TQToolBar*>(this) ); + } + dockArea = 0; + TQPoint topLeft = currRect.topLeft(); + TQRect screen = qApp->desktop()->availableGeometry( topLeft ); + if ( !screen.contains( topLeft ) ) { + topLeft.setY(TQMAX(topLeft.y(), screen.top())); + topLeft.setY(TQMIN(topLeft.y(), screen.bottom()-height())); + topLeft.setX(TQMAX(topLeft.x(), screen.left())); + topLeft.setX(TQMIN(topLeft.x(), screen.right()-width())); + } + move( topLeft ); + } + + if ( curPlace == InDock && state == OutsideDock && !::qt_cast<TQToolBar*>(this) ) { + if ( lastSize != TQSize( -1, -1 ) ) + resize( lastSize ); + } + + if ( doUpdate ) { + curPlace = state; + updateGui(); + } + if ( doOrientationChange ) + emit orientationChanged( orientation() ); + tmpDockArea = 0; + if ( doAdjustSize ) { + TQApplication::sendPostedEvents( this, TQEvent::LayoutHint ); + if ( ::qt_cast<TQToolBar*>(this) ) + adjustSize(); + if (lastSize == TQSize(-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 TQDockWindow::setWidget( TQWidget *w ) +{ + wid = w; + boxLayout()->addWidget( w ); + updateGui(); +} + +/*! + Returns the dock window's main widget. + + \sa setWidget() +*/ + +TQWidget *TQDockWindow::widget() const +{ + return wid; +} + +void TQDockWindow::startRectDraw( const TQPoint &so, bool drawRect ) +{ + state = place(); + if ( unclippedPainter ) + endRectDraw( !opaque ); +#ifdef MAC_DRAG_HACK + TQWidget *paint_on = topLevelWidget(); +#else + int scr = TQApplication::desktop()->screenNumber( this ); + TQWidget *paint_on = TQApplication::desktop()->screen( scr ); +#endif + unclippedPainter = new TQPainter( paint_on, TRUE ); + unclippedPainter->setPen( TQPen( gray, curPlace == OutsideDock ? 3 : 1 ) ); + unclippedPainter->setRasterOp( XorROP ); + currRect = TQRect( realWidgetPos( this ), size() ); + if ( drawRect ) { + TQRect dr(currRect); +#ifdef MAC_DRAG_HACK + dr.moveBy(-topLevelWidget()->geometry().x(), -topLevelWidget()->geometry().y()); +#endif + unclippedPainter->drawRect( dr ); + } + startOrientation = orientation(); + startOffset = mapFromGlobal( so ); +} + +void TQDockWindow::endRectDraw( bool drawRect ) +{ + if ( !unclippedPainter ) + return; + if ( drawRect ) { + TQRect dr(currRect); +#ifdef MAC_DRAG_HACK + dr.moveBy(-topLevelWidget()->geometry().x(), -topLevelWidget()->geometry().y()); +#endif + unclippedPainter->drawRect( dr ); + } + delete unclippedPainter; + unclippedPainter = 0; +} + +/*! + \reimp +*/ +void TQDockWindow::drawFrame( TQPainter *p ) +{ + if ( place() == InDock ) { + TQFrame::drawFrame( p ); + return; + } + + TQStyle::SFlags flags = TQStyle::Style_Default; + TQStyleOption opt(lineWidth(),midLineWidth()); + + if ( titleBar->isActive() ) + flags |= TQStyle::Style_Active; + + style().drawPrimitive( TQStyle::PE_WindowFrame, p, rect(), colorGroup(), flags, opt ); +} + +/*! + \reimp +*/ +void TQDockWindow::drawContents( TQPainter *p ) +{ + TQStyle::SFlags flags = TQStyle::Style_Default; + if ( titleBar->isActive() ) + flags |= TQStyle::Style_Active; + style().drawControl( TQStyle::CE_DockWindowEmptyArea, p, this, + rect(), colorGroup(), flags ); +} + +/*! + \property TQDockWindow::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 TQDockWindow::setResizeEnabled( bool b ) +{ + resizeEnabled = b; + hbox->setMargin( b ? 0 : 2 ); + updateGui(); +} + +/*! + \property TQDockWindow::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 TQDockWindow::setMovingEnabled( bool b ) +{ + moveEnabled = b; + updateGui(); +} + +bool TQDockWindow::isResizeEnabled() const +{ + return resizeEnabled; +} + +bool TQDockWindow::isMovingEnabled() const +{ + return moveEnabled; +} + +/*! + \property TQDockWindow::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 TQDockWindow::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 TQDockWindow::isCloseEnabled() const +{ + return ( ( cMode & Docked ) == Docked && place() == InDock || + ( cMode & Undocked ) == Undocked && place() == OutsideDock ); +} + +int TQDockWindow::closeMode() const +{ + return cMode; +} + +/*! + \property TQDockWindow::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 TQDockWindow does not limit stretchablilty. +*/ + +void TQDockWindow::setHorizontallyStretchable( bool b ) +{ + stretchable[ Horizontal ] = b; +} + +/*! + \property TQDockWindow::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 TQDockWindow does not limit stretchablilty. +*/ + +void TQDockWindow::setVerticallyStretchable( bool b ) +{ + stretchable[ Vertical ] = b; +} + +bool TQDockWindow::isHorizontallyStretchable() const +{ + return isResizeEnabled() || stretchable[ Horizontal ]; +} + +bool TQDockWindow::isVerticallyStretchable() const +{ + return isResizeEnabled() || stretchable[ Vertical ]; +} + +/*! + \property TQDockWindow::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 TQDockWindow does not limit stretchablilty. +*/ + +bool TQDockWindow::isStretchable() const +{ + if ( orientation() == Horizontal ) + return isHorizontallyStretchable(); + return isVerticallyStretchable(); +} + +/*! + Returns the orientation of the dock window. + + \sa orientationChanged() +*/ + +TQt::Orientation TQDockWindow::orientation() const +{ + if ( dockArea ) + return dockArea->orientation(); + if ( ::qt_cast<TQToolBar*>(this) ) + return Horizontal; + return ( ((TQDockWindow*)this)->boxLayout()->direction() == TQBoxLayout::LeftToRight || + ((TQDockWindow*)this)->boxLayout()->direction() == TQBoxLayout::RightToLeft ? + Horizontal : Vertical ); +} + +int TQDockWindow::offset() const +{ + return offs; +} + +/*! + \property TQDockWindow::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 TQDockWindow::setOffset( int o ) +{ + offs = o; +} + +/*! + Returns the dock window's preferred size (fixed extent). + + \sa setFixedExtentWidth() setFixedExtentHeight() +*/ + +TQSize TQDockWindow::fixedExtent() const +{ + return fExtent; +} + +/*! + Sets the dock window's preferred width for its fixed extent (size) + to \a w. + + \sa setFixedExtentHeight() +*/ + +void TQDockWindow::setFixedExtentWidth( int w ) +{ + fExtent.setWidth( w ); +} + +/*! + Sets the dock window's preferred height for its fixed extent + (size) to \a h. + + \sa setFixedExtentWidth() +*/ + +void TQDockWindow::setFixedExtentHeight( int h ) +{ + fExtent.setHeight( h ); +} + +/*! + \property TQDockWindow::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 retquire a new + line in the dock area. +*/ + +void TQDockWindow::setNewLine( bool b ) +{ + nl = b; +} + +bool TQDockWindow::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 TQBoxLayout::addWidget() function. + + If the dock window only needs to contain a single widget use + setWidget() instead. + + \sa setWidget() setOrientation() +*/ + +TQBoxLayout *TQDockWindow::boxLayout() +{ + return childBox; +} + +/*! \reimp + */ + +TQSize TQDockWindow::sizeHint() const +{ + TQSize sh( TQFrame::sizeHint() ); + if ( place() == InDock ) + sh = sh.expandedTo( fixedExtent() ); + sh = sh.expandedTo( TQSize( 16, 16 ) ); + if ( area() ) { + if ( area()->orientation() == Horizontal && !vHandleRight->isVisible() ) + sh.setWidth( sh.width() + 2 * style().pixelMetric(TQStyle::PM_SplitterWidth, this) / 3 ); + else if ( area()->orientation() == Vertical && !hHandleBottom->isVisible() ) + sh.setHeight( sh.height() + 2 * style().pixelMetric(TQStyle::PM_SplitterWidth, this) / 3 ); + } + return sh; +} + +/*! \reimp + */ + +TQSize TQDockWindow::minimumSize() const +{ + TQSize ms( TQFrame::minimumSize() ); + if ( place() == InDock ) + ms = ms.expandedTo( fixedExtent() ); + ms = ms.expandedTo( TQSize( 16, 16 ) ); + if ( area() ) { + if ( area()->orientation() == Horizontal && !vHandleRight->isVisible() ) + ms.setWidth( ms.width() + 2 * style().pixelMetric(TQStyle::PM_SplitterWidth, this) / 3 ); + else if ( area()->orientation() == Vertical && !hHandleBottom->isVisible() ) + ms.setHeight( ms.height() + 2 * style().pixelMetric(TQStyle::PM_SplitterWidth, this) / 3 ); + } + return ms; +} + +/*! \reimp + */ + +TQSize TQDockWindow::minimumSizeHint() const +{ + TQSize msh( TQFrame::minimumSize() ); + if ( place() == InDock ) + msh = msh.expandedTo( fixedExtent() ); + msh = msh.expandedTo( TQSize( 16, 16 ) ); + if ( area() ) { + if ( area()->orientation() == Horizontal && !vHandleRight->isVisible() ) + msh.setWidth( msh.width() + 2 * style().pixelMetric(TQStyle::PM_SplitterWidth, this) / 3 ); + else if ( area()->orientation() == Vertical && !hHandleBottom->isVisible() ) + msh.setHeight( msh.height() + 2 * style().pixelMetric(TQStyle::PM_SplitterWidth, this) / 3 ); + } + return msh; +} + +/*! \internal */ +void TQDockWindow::undock( TQWidget *w ) +{ + TQMainWindow *mw = 0; + if ( area() ) + mw = ::qt_cast<TQMainWindow*>(area()->parentWidget()); + if ( mw && !mw->isDockEnabled( this, DockTornOff ) ) + return; + if ( (place() == OutsideDock && !w) ) + return; + + TQPoint p( 50, 50 ); + if ( topLevelWidget() ) + p = topLevelWidget()->pos() + TQPoint( 20, 20 ); + if ( dockArea ) { + delete (TQDockArea::DockWindowData*)dockWindowData; + dockWindowData = dockArea->dockWindowData( this ); + dockArea->removeDockWindow( this, TRUE, orientation() != Horizontal && ::qt_cast<TQToolBar*>(this) ); + } + dockArea = 0; + if ( lastPos != TQPoint( -1, -1 ) && lastPos.x() > 0 && lastPos.y() > 0 ) + move( lastPos ); + else + move( p ); + if ( lastSize != TQSize( -1, -1 ) ) + resize( lastSize ); + curPlace = OutsideDock; + updateGui(); + emit orientationChanged( orientation() ); + TQApplication::sendPostedEvents( this, TQEvent::LayoutHint ); + if ( ::qt_cast<TQToolBar*>(this) ) + adjustSize(); + if ( !w ) { + if ( !parentWidget() || parentWidget()->isVisible() ) { + if (lastSize == TQSize(-1, -1)) + clearWState(WState_Resized); // Ensures size is recalculated (opaque). + show(); + } + } else { + reparent( w, 0, TQPoint( 0, 0 ), FALSE ); + move( -width() - 5, -height() - 5 ); + resize( 1, 1 ); + show(); + } + if ( parentWidget() && isTopLevel() ) + parentWidget()->setActiveWindow(); + emit placeChanged( place() ); +} + +/*! + \fn void TQDockWindow::undock() + + Undocks the TQDockWindow from its current dock area if it is + docked; otherwise does nothing. + + \sa dock() TQDockArea::moveDockWindow(), + TQDockArea::removeDockWindow(), TQMainWindow::moveDockWindow(), + TQMainWindow::removeDockWindow() +*/ + +void TQDockWindow::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 TQDockArea::isDockWindowAccepted(). + + \sa undock() TQDockArea::moveDockWindow(), + TQDockArea::removeDockWindow(), TQMainWindow::moveDockWindow(), + TQMainWindow::removeDockWindow(), TQDockArea::isDockWindowAccepted() + +*/ + +void TQDockWindow::dock() +{ + if ( !(TQDockArea::DockWindowData*)dockWindowData || + !( (TQDockArea::DockWindowData*)dockWindowData )->area ) + return; + curPlace = InDock; + lastPos = pos(); + lastSize = size(); + ( (TQDockArea::DockWindowData*)dockWindowData )-> + area->dockWindow( this, (TQDockArea::DockWindowData*)dockWindowData ); + emit orientationChanged( orientation() ); + emit placeChanged( place() ); +} + +/*! \reimp + */ + +void TQDockWindow::hideEvent( TQHideEvent *e ) +{ + TQFrame::hideEvent( e ); +} + +/*! \reimp + */ + +void TQDockWindow::showEvent( TQShowEvent *e ) +{ + if (curPlace == OutsideDock && (parent() && strcmp(parent()->name(), "qt_hide_dock") != 0)) { + TQRect sr = qApp->desktop()->availableGeometry( this ); + if ( !sr.contains( pos() ) ) { + int nx = TQMIN( TQMAX( x(), sr.x() ), sr.right()-width() ); + int ny = TQMIN( TQMAX( y(), sr.y() ), sr.bottom()-height() ); + move( nx, ny ); + } + } + + TQFrame::showEvent( e ); +} + +/*! + \property TQDockWindow::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 TQDockWindow::setOpaqueMoving( bool b ) +{ + opaque = b; + horHandle->setOpaqueMoving( b ); + verHandle->setOpaqueMoving( b ); + titleBar->setOpaqueMoving( b ); +} + +bool TQDockWindow::opaqueMoving() const +{ + return opaque; +} + +/*! \reimp */ + +void TQDockWindow::setCaption( const TQString &s ) +{ + titleBar->setCaption( s ); + verHandle->update(); + horHandle->update(); +#ifndef QT_NO_WIDGET_TOPEXTRA + TQFrame::setCaption( s ); +#endif +#ifndef QT_NO_TOOLTIP + TQToolTip::remove( horHandle ); + TQToolTip::remove( verHandle ); + if ( !s.isEmpty() ) { + TQToolTip::add( horHandle, s ); + TQToolTip::add( verHandle, s ); + } +#endif +} + +void TQDockWindow::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 TQDockWindow::eventFilter( TQObject * o, TQEvent *e ) +{ + if ( !o->isWidgetType() ) + return FALSE; + + if ( e->type() == TQEvent::KeyPress && + ( horHandle->mousePressed || + verHandle->mousePressed || + titleBar->mousePressed ) ) { + TQKeyEvent *ke = (TQKeyEvent*)e; + if ( ke->key() == Key_Escape ) { + horHandle->mousePressed = + verHandle->mousePressed = + titleBar->mousePressed = FALSE; + endRectDraw( !opaque ); + qApp->removeEventFilter( this ); + return TRUE; + } + } else if ( ((TQWidget*)o)->topLevelWidget() != this && place() == OutsideDock && isTopLevel() ) { + if ( (e->type() == TQEvent::WindowDeactivate || + e->type() == TQEvent::WindowActivate ) ) + event( e ); + } + return FALSE; +} + +/*! \reimp */ +bool TQDockWindow::event( TQEvent *e ) +{ + switch ( e->type() ) { + case TQEvent::WindowDeactivate: + if ( place() == OutsideDock && isTopLevel() && parentWidget() + && parentWidget()->isActiveWindow() ) + return TRUE; + case TQEvent::Hide: + if ( !isHidden() ) + break; + // fall through + case TQEvent::HideToParent: + emit visibilityChanged( FALSE ); + break; + case TQEvent::Show: + if ( e->spontaneous() ) + break; + case TQEvent::ShowToParent: + emit visibilityChanged( TRUE ); + break; + default: + break; + } + return TQFrame::event( e ); +} + +#ifdef QT_NO_WIDGET_TOPEXTRA +TQString TQDockWindow::caption() const +{ + return titleBar->caption(); +} +#endif + +/*! \reimp */ +void TQDockWindow::contextMenuEvent( TQContextMenuEvent *e ) +{ + TQObject *o = this; + while ( o ) { + if ( ::qt_cast<TQMainWindow*>(o) ) + break; + o = o->parent(); + } + if ( !o || ! ( (TQMainWindow*)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 000000000..eb6daa200 --- /dev/null +++ b/src/widgets/qdockwindow.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Definition of the TQDockWindow class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQDOCKWINDOW_H +#define TQDOCKWINDOW_H + +#ifndef QT_H +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_MAINWINDOW + +class TQDockWindowHandle; +class TQDockWindowTitleBar; +class TQPainter; +class TQDockWindowResizeHandle; +class TQBoxLayout; +class TQHBoxLayout; +class TQVBoxLayout; +class TQDockArea; +class TQWidgetResizeHandler; +class TQMainWindow; +class TQDockAreaLayout; +class TQDockWindowPrivate; +class TQToolBar; +class TQWindowsXPStyle; + +class Q_EXPORT TQDockWindow : public TQFrame +{ + 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 TQDockWindowHandle; + friend class TQDockWindowTitleBar; + friend class TQDockArea; + friend class TQDockAreaLayout; + friend class TQMainWindow; + friend class TQCEMainWindow; + friend class TQToolBar; + friend class TQWindowsXPStyle; + +public: + enum Place { InDock, OutsideDock }; + enum CloseMode { Never = 0, Docked = 1, Undocked = 2, Always = Docked | Undocked }; + + TQDockWindow( Place p = InDock, TQWidget* parent=0, const char* name=0, WFlags f = 0 ); + TQDockWindow( TQWidget* parent, const char* name=0, WFlags f = 0 ); + ~TQDockWindow(); + + virtual void setWidget( TQWidget *w ); + TQWidget *widget() const; + + Place place() const { return curPlace; } + + TQDockArea *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 ); + TQSize fixedExtent() const; + + virtual void setNewLine( bool b ); + bool newLine() const; + + TQt::Orientation orientation() const; + + TQSize sizeHint() const; + TQSize minimumSize() const; + TQSize minimumSizeHint() const; + + TQBoxLayout *boxLayout(); + + virtual void setOpaqueMoving( bool b ); + bool opaqueMoving() const; + + bool eventFilter( TQObject *o, TQEvent *e ); + +#ifdef QT_NO_WIDGET_TOPEXTRA + TQString caption() const; +#endif + +signals: + void orientationChanged( Orientation o ); + void placeChanged( TQDockWindow::Place p ); + void visibilityChanged( bool ); + +public slots: + virtual void undock( TQWidget *w ); + virtual void undock() { undock( 0 ); } + virtual void dock(); + virtual void setOrientation( Orientation o ); + void setCaption( const TQString &s ); + +protected: + void resizeEvent( TQResizeEvent *e ); + void showEvent( TQShowEvent *e ); + void hideEvent( TQHideEvent *e ); + void contextMenuEvent( TQContextMenuEvent *e ); + + void drawFrame( TQPainter * ); + void drawContents( TQPainter * ); + + bool event( TQEvent *e ); + +private slots: + void toggleVisible() { if ( !isVisible() ) show(); else hide(); } + +private: + TQDockWindow( Place p, TQWidget* parent, const char* name, WFlags f, bool toolbar ); + + void handleMove( const TQPoint &pos, const TQPoint &gp, bool drawRect ); + void updateGui(); + void updateSplitterVisibility( bool visible ); + + void startRectDraw( const TQPoint &so, bool drawRect ); + void endRectDraw( bool drawRect ); + void updatePosition( const TQPoint &globalPos ); + TQWidget *areaAt( const TQPoint &gp ); + void removeFromDock( bool fixNewLines = TRUE ); + void swapRect( TQRect &r, TQt::Orientation o, const TQPoint &offset, TQDockArea *area ); + void init(); + +private: + TQDockWindowHandle *horHandle, *verHandle; + TQDockWindowTitleBar *titleBar; + TQWidget *wid; + TQPainter *unclippedPainter; + TQDockArea *dockArea, *tmpDockArea; + TQRect 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; + TQPoint startOffset; + int offs; + TQSize fExtent; + TQDockWindowResizeHandle *hHandleTop, *hHandleBottom, *vHandleLeft, *vHandleRight; + TQVBoxLayout *hbox; + TQHBoxLayout *vbox; + TQBoxLayout *childBox; + void *dockWindowData; + TQPoint lastPos; + TQSize lastSize; + TQWidgetResizeHandler *widgetResizeHandler; + TQDockWindowPrivate *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQDockWindow( const TQDockWindow & ); + TQDockWindow& operator=( const TQDockWindow & ); +#endif +}; + +inline TQDockArea *TQDockWindow::area() const +{ + return dockArea; +} + +#define Q_DEFINED_QDOCKWINDOW +#include "qwinexport.h" +#endif + +#endif // TQDOCKWINDOW_H diff --git a/src/widgets/qeffects.cpp b/src/widgets/qeffects.cpp new file mode 100644 index 000000000..690a7cba7 --- /dev/null +++ b/src/widgets/qeffects.cpp @@ -0,0 +1,675 @@ +/**************************************************************************** +** +** Implementation of TQEffects functions +** +** Created : 000621 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQWidget-members +*/ + +class TQAccessWidget : public TQWidget +{ + friend class TQAlphaWidget; + friend class TQRollEffect; +public: + TQAccessWidget( TQWidget* parent=0, const char* name=0, WFlags f = 0 ) + : TQWidget( parent, name, f ) {} +}; + +/* + Internal class TQAlphaWidget. + + The TQAlphaWidget is shown while the animation lasts + and displays the pixmap resulting from the alpha blending. +*/ + +class TQAlphaWidget: public TQWidget, private TQEffects +{ + Q_OBJECT +public: + TQAlphaWidget( TQWidget* w, WFlags f = 0 ); + + void run( int time ); + +protected: + void paintEvent( TQPaintEvent* e ); + void closeEvent( TQCloseEvent* ); + bool eventFilter( TQObject* o, TQEvent* e ); + void alphaBlend(); + +protected slots: + void render(); + +private: + TQPixmap pm; + double alpha; + TQImage back; + TQImage front; + TQImage mixed; + TQGuardedPtr<TQAccessWidget> widget; + int duration; + int elapsed; + bool showWidget; + TQTimer anim; + TQTime checkTime; +}; + +static TQAlphaWidget* q_blend = 0; + +/* + Constructs a TQAlphaWidget. +*/ +TQAlphaWidget::TQAlphaWidget( TQWidget* w, WFlags f ) + : TQWidget( TQApplication::desktop()->screen(TQApplication::desktop()->screenNumber(w)), + "qt internal alpha effect widget", f ) +{ +#if 1 //ndef Q_WS_WIN + setEnabled( FALSE ); +#endif + + pm.setOptimization( TQPixmap::BestOptim ); + setBackgroundMode( NoBackground ); + widget = (TQAccessWidget*)w; + alpha = 0; +} + +/* + \reimp +*/ +void TQAlphaWidget::paintEvent( TQPaintEvent* ) +{ + bitBlt( this, TQPoint(0,0), &pm ); +} + +/* + Starts the alphablending animation. + The animation will take about \a time ms +*/ +void TQAlphaWidget::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 = TQImage( widget->size(), 32 ); + front = TQPixmap::grabWidget( widget ); + + back = TQImage( widget->size(), 32 ); + back = TQPixmap::grabWindow( TQApplication::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 TQAlphaWidget::eventFilter( TQObject* o, TQEvent* e ) +{ + switch ( e->type() ) { + case TQEvent::Move: + if ( o != widget ) + break; + move( widget->geometry().x(),widget->geometry().y() ); + update(); + break; + case TQEvent::Hide: + case TQEvent::Close: + if ( o != widget ) + break; + case TQEvent::MouseButtonPress: +#ifndef QT_NO_SCROLLVIEW + if ( ::qt_cast<TQScrollView*>(o) ) + break; +#endif + case TQEvent::MouseButtonDblClick: + setEnabled(TRUE); + showWidget = FALSE; + render(); + break; + case TQEvent::KeyPress: + { + TQKeyEvent *ke = (TQKeyEvent*)e; + if ( ke->key() == Key_Escape ) + showWidget = FALSE; + else + duration = 0; + render(); + break; + } + default: + break; + } + return TQWidget::eventFilter( o, e ); +} + +/* + \reimp +*/ +void TQAlphaWidget::closeEvent( TQCloseEvent *e ) +{ + e->accept(); + if ( !q_blend ) + return; + + showWidget = FALSE; + render(); + + TQWidget::closeEvent( e ); +} + +/* + Render alphablending for the time elapsed. + + Show the blended widget and free all allocated source + if the blending is finished. +*/ +void TQAlphaWidget::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(); + TQColor erc = widget->eraseColor(); + const TQPixmap *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 TQAlphaWidget::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 TQRollEffect + + The TQRollEffect widget is shown while the animation lasts + and displays a scrolling pixmap. +*/ + +class TQRollEffect : public TQWidget, private TQEffects +{ + Q_OBJECT +public: + TQRollEffect( TQWidget* w, WFlags f, DirFlags orient ); + + void run( int time ); + +protected: + void paintEvent( TQPaintEvent* ); + bool eventFilter( TQObject*, TQEvent* ); + void closeEvent( TQCloseEvent* ); + +private slots: + void scroll(); + +private: + TQGuardedPtr<TQAccessWidget> widget; + + int currentHeight; + int currentWidth; + int totalHeight; + int totalWidth; + + int duration; + int elapsed; + bool done; + bool showWidget; + int orientation; + + TQTimer anim; + TQTime checkTime; + + TQPixmap pm; +}; + +static TQRollEffect* q_roll = 0; + +/* + Construct a TQRollEffect widget. +*/ +TQRollEffect::TQRollEffect( TQWidget* w, WFlags f, DirFlags orient ) + : TQWidget( TQApplication::desktop()->screen(TQApplication::desktop()->screenNumber(w)), + "qt internal roll effect widget", f ), orientation(orient) +{ +#if 1 //ndef Q_WS_WIN + setEnabled( FALSE ); +#endif + widget = (TQAccessWidget*) 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( TQPixmap::BestOptim ); + pm = TQPixmap::grabWidget( widget ); +} + +/* + \reimp +*/ +void TQRollEffect::paintEvent( TQPaintEvent* ) +{ + int x = orientation & RightScroll ? TQMIN(0, currentWidth - totalWidth) : 0; + int y = orientation & DownScroll ? TQMIN(0, currentHeight - totalHeight) : 0; + + bitBlt( this, x, y, &pm, + 0, 0, pm.width(), pm.height(), CopyROP, TRUE ); +} + +/* + \reimp +*/ +bool TQRollEffect::eventFilter( TQObject* o, TQEvent* e ) +{ + switch ( e->type() ) { + case TQEvent::Move: + if ( o != widget ) + break; + move( widget->geometry().x(),widget->geometry().y() ); + update(); + break; + case TQEvent::Hide: + case TQEvent::Close: + if ( o != widget || done ) + break; + setEnabled(TRUE); + showWidget = FALSE; + done = TRUE; + scroll(); + break; + case TQEvent::MouseButtonPress: +#ifndef QT_NO_SCROLLVIEW + if ( ::qt_cast<TQScrollView*>(o) ) + break; +#endif + case TQEvent::MouseButtonDblClick: + if ( done ) + break; + setEnabled(TRUE); + showWidget = FALSE; + done = TRUE; + scroll(); + break; + case TQEvent::KeyPress: + { + TQKeyEvent *ke = (TQKeyEvent*)e; + if ( ke->key() == Key_Escape ) + showWidget = FALSE; + done = TRUE; + scroll(); + break; + } + default: + break; + } + return TQWidget::eventFilter( o, e ); +} + +/* + \reimp +*/ +void TQRollEffect::closeEvent( TQCloseEvent *e ) +{ + e->accept(); + if ( done ) + return; + + showWidget = FALSE; + done = TRUE; + scroll(); + + TQWidget::closeEvent( e ); +} + +/* + Start the animation. + + The animation will take about \a time ms, or is + calculated if \a time is negative +*/ +void TQRollEffect::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 = TQMIN( TQMAX( dist/3, 50 ), 120 ); + } + + connect( &anim, SIGNAL(timeout()), this, SLOT(scroll())); + + widget->setWState( WState_Visible ); + + move( widget->geometry().x(),widget->geometry().y() ); + resize( TQMIN( currentWidth, totalWidth ), TQMIN( currentHeight, totalHeight ) ); + + show(); + setEnabled(FALSE); + + qApp->installEventFilter( this ); + + showWidget = TRUE; + done = FALSE; + anim.start( 1 ); + checkTime.start(); +} + +/* + Roll according to the time elapsed. +*/ +void TQRollEffect::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 = TQMIN( currentWidth, totalWidth ); + if ( orientation & DownScroll || orientation & UpScroll ) + h = TQMIN( currentHeight, totalHeight ); + + setUpdatesEnabled( FALSE ); + if ( orientation & UpScroll ) + y = widget->geometry().y() + TQMAX( 0, totalHeight - currentHeight ); + if ( orientation & LeftScroll ) + x = widget->geometry().x() + TQMAX( 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(); + TQColor erc = widget->eraseColor(); + const TQPixmap *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( TQWidget* w, TQEffects::DirFlags orient, int time ) +{ + if ( q_roll ) { + delete q_roll; + q_roll = 0; + } + + qApp->sendPostedEvents( w, TQEvent::Move ); + qApp->sendPostedEvents( w, TQEvent::Resize ); +#ifdef Q_WS_X11 + uint flags = TQt::WStyle_Customize | TQt::WNoAutoErase | TQt::WStyle_StaysOnTop + | (w->isPopup() ? TQt::WType_Popup : (TQt::WX11BypassWM | TQt::WStyle_Tool)); +#else + uint flags = TQt::WStyle_Customize | TQt::WType_Popup | TQt::WX11BypassWM | TQt::WNoAutoErase | TQt::WStyle_StaysOnTop; +#endif + + // those can popups - they would steal the focus, but are disabled + q_roll = new TQRollEffect( w, flags, orient ); + q_roll->run( time ); +} + +/*! + Fade in widget \a w in \a time ms. +*/ +void qFadeEffect( TQWidget* w, int time ) +{ + if ( q_blend ) { + delete q_blend; + q_blend = 0; + } + + qApp->sendPostedEvents( w, TQEvent::Move ); + qApp->sendPostedEvents( w, TQEvent::Resize ); + +#ifdef Q_WS_X11 + uint flags = TQt::WStyle_Customize | TQt::WNoAutoErase | TQt::WStyle_StaysOnTop + | (w->isPopup() ? TQt::WType_Popup : (TQt::WX11BypassWM | TQt::WStyle_Tool)); +#else + uint flags = TQt::WStyle_Customize | TQt::WType_Popup | TQt::WX11BypassWM | TQt::WNoAutoErase | TQt::WStyle_StaysOnTop; +#endif + + // those can popups - they would steal the focus, but are disabled + q_blend = new TQAlphaWidget( 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 000000000..ea7347e85 --- /dev/null +++ b/src/widgets/qeffects_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Definition of TQEffects functions +** +** Created : 000621 +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQEFFECTS_P_H +#define TQEFFECTS_P_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the TQt 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 TQWidget; + +struct TQEffects +{ + enum Direction { + LeftScroll = 0x0001, + RightScroll = 0x0002, + UpScroll = 0x0004, + DownScroll = 0x0008 + }; + + typedef uint DirFlags; +}; + +extern void Q_EXPORT qScrollEffect( TQWidget*, TQEffects::DirFlags dir = TQEffects::DownScroll, int time = -1 ); +extern void Q_EXPORT qFadeEffect( TQWidget*, int time = -1 ); +#endif // QT_NO_EFFECTS + +#endif // TQEFFECTS_P_H diff --git a/src/widgets/qframe.cpp b/src/widgets/qframe.cpp new file mode 100644 index 000000000..1e3b3568e --- /dev/null +++ b/src/widgets/qframe.cpp @@ -0,0 +1,754 @@ +/**************************************************************************** +** +** Implementation of TQFrame widget class +** +** Created : 950201 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qframe.h" +#ifndef QT_NO_FRAME +#include "qpainter.h" +#include "qdrawutil.h" +#include "qframe.h" +#include "qbitmap.h" +#include "qstyle.h" + +/*! + \class TQFrame + \brief The TQFrame 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(). + + TQPopupMenu uses this to "raise" the menu above the surrounding + screen. TQProgressBar has a "sunken" look. TQLabel has a flat look. + The frames of widgets like these can be changed. + + \code + TQLabel label(...); + label.setFrameStyle( TQFrame::Panel | TQFrame::Raised ); + label.setLineWidth( 2 ); + + TQProgressBar pbar(...); + label.setFrameStyle( TQFrame::NoFrame ); + \endcode + + The TQFrame class can also be used directly for creating simple + frames without any contents, although usually you would use a + TQHBox or TQVBox 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 TQFrame::Shape frame + shape\endlink and a \link TQFrame::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 TQFrame::Shape + + This enum type defines the shapes of a TQFrame's frame. + + \value NoFrame TQFrame draws nothing + \value Box TQFrame draws a box around its contents + \value Panel TQFrame 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 TQFrame draws a horizontal line that frames nothing + (useful as separator) + \value VLine TQFrame 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 TQStyle, Shape interacts with TQFrame::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 TQFrame::Shadow TQFrame::style() TQStyle::drawPrimitive() +*/ + + +/*! + \enum TQFrame::Shadow + + This enum type defines the 3D effect used for TQFrame'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 TQFrame::Shape, the lineWidth() and the + midLineWidth(). See the \link #picture picture of the frames\endlink + in the class description. + + \sa TQFrame::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 + TQWidget constructor. +*/ + +TQFrame::TQFrame( TQWidget *parent, const char *name, WFlags f ) + : TQWidget( parent, name, f ) +{ + frect = TQRect( 0, 0, 0, 0 ); + fstyle = NoFrame | Plain; + lwidth = 1; + mwidth = 0; + mlwidth = 0; + updateFrameWidth(); +} + +static const int wpwidth = 2; // WinPanel lwidth + +/*! + \fn int TQFrame::frameStyle() const + + Returns the frame style. + + The default value is TQFrame::NoFrame. + + \sa setFrameStyle(), frameShape(), frameShadow() +*/ + +/*! + \property TQFrame::frameShape + \brief the frame shape value from the frame style + + \sa frameStyle(), frameShadow() +*/ + +/*! + \property TQFrame::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{TQFrame::Shape} and the shadow + styles in \l{TQFrame::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(), TQColorGroup +*/ + +void TQFrame::setFrameStyle( int style ) +{ + if ( !testWState( WState_OwnSizePolicy ) ) { + switch ( style & MShape ) { + case HLine: + setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ); + break; + case VLine: + setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Minimum ); + break; + default: + if ( (fstyle & MShape) == HLine || (fstyle & MShape) == VLine) + setSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Preferred ); + } + clearWState( WState_OwnSizePolicy ); + } + fstyle = (short)style; + updateFrameWidth( TRUE ); +} + +/*! + \property TQFrame::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 TQFrame::setLineWidth( int w ) +{ + lwidth = (short)w; + updateFrameWidth(); +} + +/*! + \property TQFrame::midLineWidth + \brief the width of the mid-line + + The default value is 0. + + \sa lineWidth(), frameWidth() +*/ + +void TQFrame::setMidLineWidth( int w ) +{ + mlwidth = (short)w; + updateFrameWidth(); +} + + + +/*! + \property TQFrame::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 TQFrame::setMargin( int w ) +{ + mwidth = (short)w; + updateFrameWidth(); +} + + +/*! + \internal + Updated the fwidth parameter. +*/ + +void TQFrame::updateFrameWidth( bool resetLineMetrics ) +{ + int frameType = fstyle & MShape; + int frameStyle = fstyle & MShadow; + + if ( resetLineMetrics ) { + switch ( frameType ) { + case MenuBarPanel: + mwidth = 0; + lwidth = style().pixelMetric( TQStyle::PM_MenuBarFrameWidth, this ); + break; + case ToolBarPanel: + mwidth = 0; + lwidth = style().pixelMetric( TQStyle::PM_DockWindowFrameWidth, this ); + break; + case LineEditPanel: + case TabWidgetPanel: + case PopupPanel: + mwidth = 0; + lwidth = style().pixelMetric( TQStyle::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 TQFrame::frameWidth + \brief the width of the frame that is drawn. + + Note that the frame width depends on the \link + TQFrame::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 TQFrame::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{TQRect(0, 0, 0, 0)}), then the frame rectangle is equivalent to + the \link TQWidget::rect() widget rectangle\endlink. + + \sa contentsRect() +*/ + +TQRect TQFrame::frameRect() const +{ + if ( frect.isNull() ) + return rect(); + else + return frect; +} + +void TQFrame::setFrameRect( const TQRect &r ) +{ + frect = r.isValid() ? r : rect(); +} + + +/*! + \property TQFrame::contentsRect + \brief the rectangle inside the frame + + \sa frameRect(), drawContents() +*/ + +TQRect TQFrame::contentsRect() const +{ + TQRect r = frameRect(); + int w = frameWidth(); // total width + int frameType = fstyle & MShape; + if (frameType == PopupPanel) { + int vExtra = style().pixelMetric(TQStyle::PM_PopupMenuFrameVerticalExtra, this); + int hExtra = style().pixelMetric(TQStyle::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 +*/ +TQSize TQFrame::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, TQWidget::sizeHint() is used. + switch (fstyle & MShape) { + case HLine: + return TQSize(-1,3); + case VLine: + return TQSize(3,-1); + default: + return TQWidget::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 TQFrame::paintEvent( TQPaintEvent *event ) +{ + const int m = margin(); + if ( m && testWFlags( WNoAutoErase ) ) { + TQRect r = contentsRect(); + r.addCoords( -m, -m, m, m ); + erase( event->region().intersect( TQRegion( r ) - contentsRect() ) ); + } + + TQPainter 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 TQRect::isNull() + null rectangle\endlink already. +*/ + +void TQFrame::resizeEvent( TQResizeEvent *e ) +{ + if ( !frect.isNull() ) { + TQRect r( frect.x(), frect.y(), + width() - (e->oldSize().width() - frect.width()), + height() - (e->oldSize().height() - frect.height()) ); + setFrameRect( r ); + } + TQWidget::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 TQPainter is already open + and must remain open. + + \sa frameRect(), contentsRect(), drawContents(), frameStyle(), setPalette() +*/ + +void TQFrame::drawFrame( TQPainter *p ) +{ + TQPoint p1, p2; + TQRect 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 TQColorGroup & g = colorGroup(); + +#ifndef QT_NO_STYLE + TQStyleOption opt(lineWidth(),midLineWidth()); + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (cstyle == Sunken) + flags |= TQStyle::Style_Sunken; + else if (cstyle == Raised) + flags |= TQStyle::Style_Raised; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + if (hasMouse()) + flags |= TQStyle::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( TQStyle::PE_PanelLineEdit, p, r, g, flags, opt ); + break; + + case GroupBoxPanel: + style().drawPrimitive( TQStyle::PE_PanelGroupBox, p, r, g, flags, opt ); + break; + + case TabWidgetPanel: + style().drawPrimitive( TQStyle::PE_PanelTabWidget, p, r, g, flags, opt ); + break; + + case MenuBarPanel: +#ifndef QT_NO_STYLE + style().drawPrimitive(TQStyle::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( TQStyle::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(TQStyle::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(TQStyle::PM_PopupMenuFrameVerticalExtra, this), + hextra = style().pixelMetric(TQStyle::PM_PopupMenuFrameHorizontalExtra, this); + if(vextra > 0 || hextra > 0) { + TQRect fr = frameRect(); + int fw = frameWidth(); + if(vextra > 0) { + style().drawControl(TQStyle::CE_PopupMenuVerticalExtra, p, this, + TQRect(fr.x() + fw, fr.y() + fw, fr.width() - (fw*2), vextra), + g, flags, opt); + style().drawControl(TQStyle::CE_PopupMenuVerticalExtra, p, this, + TQRect(fr.x() + fw, fr.bottom() - fw - vextra, fr.width() - (fw*2), vextra), + g, flags, opt); + } + if(hextra > 0) { + style().drawControl(TQStyle::CE_PopupMenuHorizontalExtra, p, this, + TQRect(fr.x() + fw, fr.y() + fw + vextra, hextra, fr.height() - (fw*2) - vextra), + g, flags, opt); + style().drawControl(TQStyle::CE_PopupMenuHorizontalExtra, p, this, + TQRect(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(TQStyle::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 = TQPoint( r.x(), r.height()/2 ); + p2 = TQPoint( r.x()+r.width(), p1.y() ); + } + else { + p1 = TQPoint( r.x()+r.width()/2, 0 ); + p2 = TQPoint( p1.x(), r.height() ); + } + if ( cstyle == Plain ) { + TQPen oldPen = p->pen(); + p->setPen( TQPen(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 TQPainter is already open when you get it, and you must leave + it open. Painter \link TQPainter::setWorldMatrix() + transformations\endlink are switched off on entry. If you + transform the painter, remember to take the frame into account and + \link TQPainter::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(), TQPainter::setClipRect() +*/ + +void TQFrame::drawContents( TQPainter * ) +{ +} + + +/*! + 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 TQFrame::frameChanged() +{ + update(); + updateGeometry(); +} + +/*!\reimp + */ +void TQFrame::styleChange( TQStyle& old ) +{ + updateFrameWidth( TRUE ); + TQWidget::styleChange( old ); +} + +#endif //QT_NO_FRAME diff --git a/src/widgets/qframe.h b/src/widgets/qframe.h new file mode 100644 index 000000000..4854012e7 --- /dev/null +++ b/src/widgets/qframe.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Definition of TQFrame widget class +** +** Created : 950201 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQFRAME_H +#define TQFRAME_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_FRAME + +class Q_EXPORT TQFrame : public TQWidget +{ + Q_OBJECT + Q_ENUMS( Shape Shadow ) + Q_PROPERTY( int frameWidth READ frameWidth ) + Q_PROPERTY( TQRect 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( TQRect frameRect READ frameRect WRITE setFrameRect DESIGNABLE false ) + +public: + TQFrame( TQWidget* parent=0, const char* name=0, WFlags f=0 ); + + int frameStyle() const; + virtual void setFrameStyle( int ); + + int frameWidth() const; + TQRect contentsRect() const; + +#ifndef Q_QDOC + bool lineShapesOk() const { return TRUE; } +#endif + + TQSize 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 ); + + TQRect frameRect() const; + virtual void setFrameRect( const TQRect & ); + +protected: + void paintEvent( TQPaintEvent * ); + void resizeEvent( TQResizeEvent * ); + virtual void drawFrame( TQPainter * ); + virtual void drawContents( TQPainter * ); + virtual void frameChanged(); + void styleChange( TQStyle& ); + +private: + void updateFrameWidth(bool=FALSE); + TQRect frect; + int fstyle; + short lwidth; + short mwidth; + short mlwidth; + short fwidth; + + void * d; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQFrame( const TQFrame & ); + TQFrame &operator=( const TQFrame & ); +#endif +}; + + +inline int TQFrame::frameStyle() const +{ return fstyle; } + +inline TQFrame::Shape TQFrame::frameShape() const +{ return (Shape) ( fstyle & MShape ); } + +inline TQFrame::Shadow TQFrame::frameShadow() const +{ return (Shadow) ( fstyle & MShadow ); } + +inline void TQFrame::setFrameShape( TQFrame::Shape s ) +{ setFrameStyle( ( fstyle & MShadow ) | s ); } + +inline void TQFrame::setFrameShadow( TQFrame::Shadow s ) +{ setFrameStyle( ( fstyle & MShape ) | s ); } + +inline int TQFrame::lineWidth() const +{ return lwidth; } + +inline int TQFrame::midLineWidth() const +{ return mlwidth; } + +inline int TQFrame::margin() const +{ return mwidth; } + +inline int TQFrame::frameWidth() const +{ return fwidth; } + + +#endif // QT_NO_FRAME + +#endif // TQFRAME_H diff --git a/src/widgets/qgrid.cpp b/src/widgets/qgrid.cpp new file mode 100644 index 000000000..f3fecf4e6 --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + + +#include "qgrid.h" +#ifndef QT_NO_GRID +#include "qlayout.h" +#include "qapplication.h" + +/*! + \class TQGrid qgrid.h + \brief The TQGrid 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 TQGrid + + \sa TQVBox TQHBox TQGridLayout +*/ + +/*! \enum TQGrid::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 TQFrame constructor. +*/ +TQGrid::TQGrid( int n, Orientation orient, TQWidget *parent, const char *name, + WFlags f ) + : TQFrame( parent, name, f ) +{ + int nCols, nRows; + if ( orient == Horizontal ) { + nCols = n; + nRows = -1; + } else { + nCols = -1; + nRows = n; + } + lay = new TQGridLayout( 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 TQFrame constructor. + */ +TQGrid::TQGrid( int n, TQWidget *parent, const char *name, WFlags f ) + : TQFrame( parent, name, f ) +{ + lay = new TQGridLayout( this, -1, n, 0, 0, name ); + lay->setAutoAdd( TRUE ); +} + + +/*! + Sets the spacing between the child widgets to \a space. +*/ + +void TQGrid::setSpacing( int space ) +{ + if ( layout() ) + layout()->setSpacing( space ); +} + + +/*!\reimp + */ +void TQGrid::frameChanged() +{ + if ( !layout() ) + return; + layout()->setMargin( frameWidth() ); +} + + +/*! + \reimp +*/ + +TQSize TQGrid::sizeHint() const +{ + TQWidget *mThis = (TQWidget*)this; + TQApplication::sendPostedEvents( mThis, TQEvent::ChildInserted ); + return TQFrame::sizeHint(); +} +#endif diff --git a/src/widgets/qgrid.h b/src/widgets/qgrid.h new file mode 100644 index 000000000..880e88a3a --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQGRID_H +#define TQGRID_H + +#ifndef QT_H +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_GRID + +class TQGridLayout; + +class Q_EXPORT TQGrid : public TQFrame +{ + Q_OBJECT +public: + TQGrid( int n, TQWidget* parent=0, const char* name=0, WFlags f = 0 ); + TQGrid( int n, Orientation orient, TQWidget* parent=0, const char* name=0, + WFlags f = 0 ); + + void setSpacing( int ); + TQSize sizeHint() const; + +#ifndef QT_NO_COMPAT + typedef Orientation Direction; +#endif + +protected: + void frameChanged(); + +private: + TQGridLayout *lay; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQGrid( const TQGrid & ); + TQGrid& operator=( const TQGrid & ); +#endif +}; + +#endif // QT_NO_GRID + +#endif // TQGRID_H diff --git a/src/widgets/qgridview.cpp b/src/widgets/qgridview.cpp new file mode 100644 index 000000000..937cc56a6 --- /dev/null +++ b/src/widgets/qgridview.cpp @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Implementation of TQGridView class +** +** Created : 010523 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + + +#include "qgridview.h" + +#ifndef QT_NO_GRIDVIEW + +#include "qpainter.h" + +/*! + \class TQGridView qgridview.h + \brief The TQGridView 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). + + TQGridView is a very simple abstract class based on TQScrollView. 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 TQTable instead. If you need a + simple list of items, use a TQListBox. If you need to present + hierachical data use a TQListView, and if you need random objects + at random positions, consider using either a TQIconView or a + TQCanvas. +*/ + + +/*! + Constructs a grid view. + + The \a parent, \a name and widget flag, \a f, arguments are passed + to the TQScrollView constructor. +*/ +TQGridView::TQGridView( TQWidget *parent, const char *name, WFlags f ) + :TQScrollView( 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. +*/ +TQGridView::~TQGridView() +{ +} + +void TQGridView::updateGrid() +{ + resizeContents( ncols * cellw, nrows * cellh ); +} + +/*! + \property TQGridView::numRows + \brief The number of rows in the grid + + \sa numCols +*/ +void TQGridView::setNumRows( int numRows ) +{ + int oldnrows = nrows; + nrows = numRows; + dimensionChange( oldnrows, ncols ); + updateGrid(); +} + +/*! + \property TQGridView::numCols + \brief The number of columns in the grid + + \sa numRows +*/ +void TQGridView::setNumCols( int numCols ) +{ + int oldncols = ncols; + ncols = numCols; + dimensionChange( nrows, oldncols ); + updateGrid(); +} + +/*! + \property TQGridView::cellWidth + \brief The width of a grid column + + All columns in a grid view have the same width. + + \sa cellHeight +*/ +void TQGridView::setCellWidth( int cellWidth ) +{ + cellw = cellWidth; + updateGrid(); + updateContents(); +} + +/*! + \property TQGridView::cellHeight + \brief The height of a grid row + + All rows in a grid view have the same height. + + \sa cellWidth +*/ +void TQGridView::setCellHeight( int cellHeight ) +{ + cellh = cellHeight; + updateGrid(); + updateContents(); +} + +/*! + Returns the geometry of cell (\a row, \a column) in the content + coordinate system. + + \sa cellRect() + */ +TQRect TQGridView::cellGeometry( int row, int column ) +{ + TQRect 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, TQt erases the area of the cell before the + paintCell() call; otherwise no erasing takes place. + + \sa TQWidget::repaint() +*/ +void TQGridView::repaintCell( int row, int column, bool erase ) +{ + repaintContents( cellGeometry( row, column ), erase ); +} + +/*! + Updates cell (\a row, \a column). + + \sa TQWidget::update() +*/ +void TQGridView::updateCell( int row, int column ) +{ + updateContents( cellGeometry( row, column ) ); +} + +/*! + Ensures cell (\a row, \a column) is visible, scrolling the grid + view if necessary. +*/ +void TQGridView::ensureCellVisible( int row, int column ) +{ + TQRect 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 TQGridView::paintEmptyArea( TQPainter *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 ); + TQRegion reg( TQRect( cx, cy, cw, ch ) ); + // Subtract the table from it + reg = reg.subtract( TQRect( contentsToViewport( TQPoint( 0, 0 ) ), gridSize() ) ); + + // And draw the rectangles (transformed as needed) + TQMemArray<TQRect> r = reg.rects(); + const TQBrush &brush = backgroundBrush(); + for ( int i = 0; i < (int)r.count(); ++i) + p->fillRect( r[ i ], brush ); +} + +/*!\reimp + */ +void TQGridView::drawContents( TQPainter *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 TQGridView::drawContents( TQPainter * ) +{ +} + +/*! + \fn void TQGridView::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 TQGridView::dimensionChange( int, int ) {} + + + +/*! + \fn int TQGridView::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 TQGridView::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 TQGridView::paintCell( TQPainter *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 TQPainter::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(), TQPainter::CoordPainter ); + //... your drawing code + p->setClipping( FALSE ); + + \endcode +*/ + +/*! + \fn TQRect TQGridView::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 TQRect( 0, 0, cellWidth(), cellHeight() ). + + \sa cellGeometry() + +*/ + +/*! + \fn TQSize TQGridView::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 000000000..428ce48ed --- /dev/null +++ b/src/widgets/qgridview.h @@ -0,0 +1,139 @@ +/********************************************************************** +** +** Definition of TQGridView class +** +** Created : 010523 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQGRIDVIEW_H +#define TQGRIDVIEW_H + +#ifndef QT_H +#include "qscrollview.h" +#endif // QT_H + +#ifndef QT_NO_GRIDVIEW + +class TQGridViewPrivate; + +class Q_EXPORT TQGridView : public TQScrollView +{ + 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: + + TQGridView( TQWidget *parent=0, const char *name=0, WFlags f=0 ); + ~TQGridView(); + + 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 ); + + TQRect cellRect() const; + TQRect cellGeometry( int row, int column ); + TQSize 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( TQPainter *, int row, int col ) = 0; + virtual void paintEmptyArea( TQPainter *p, int cx, int cy, int cw, int ch ); + + void drawContents( TQPainter *p, int cx, int cy, int cw, int ch ); + + virtual void dimensionChange( int, int ); + +private: + void drawContents( TQPainter* ); + void updateGrid(); + + int nrows; + int ncols; + int cellw; + int cellh; + TQGridViewPrivate* d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQGridView( const TQGridView & ); + TQGridView &operator=( const TQGridView & ); +#endif +}; + +inline int TQGridView::cellWidth() const +{ return cellw; } + +inline int TQGridView::cellHeight() const +{ return cellh; } + +inline int TQGridView::rowAt( int y ) const +{ return y / cellh; } + +inline int TQGridView::columnAt( int x ) const +{ return x / cellw; } + +inline int TQGridView::numRows() const +{ return nrows; } + +inline int TQGridView::numCols() const +{return ncols; } + +inline TQRect TQGridView::cellRect() const +{ return TQRect( 0, 0, cellw, cellh ); } + +inline TQSize TQGridView::gridSize() const +{ return TQSize( ncols * cellw, nrows * cellh ); } + + + +#endif // QT_NO_GRIDVIEW + + +#endif // TQTABLEVIEW_H diff --git a/src/widgets/qgroupbox.cpp b/src/widgets/qgroupbox.cpp new file mode 100644 index 000000000..0bdad68f5 --- /dev/null +++ b/src/widgets/qgroupbox.cpp @@ -0,0 +1,989 @@ +/********************************************************************** +** +** Implementation of TQGroupBox widget class +** +** Created : 950203 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQGroupBox qgroupbox.h + \brief The TQGroupBox 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(). + + TQGroupBox 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 TQButtonGroup +*/ + +class TQCheckBox; + +class TQGroupBoxPrivate +{ +public: + TQGroupBoxPrivate(): + spacer( 0 ), + checkbox( 0 ) {} + + TQSpacerItem *spacer; + TQCheckBox *checkbox; +}; + + + + +/*! + Constructs a group box widget with no title. + + The \a parent and \a name arguments are passed to the TQWidget + constructor. + + This constructor does not do automatic layout. +*/ + +TQGroupBox::TQGroupBox( TQWidget *parent, const char *name ) + : TQFrame( parent, name ) +{ + init(); +} + +/*! + Constructs a group box with the title \a title. + + The \a parent and \a name arguments are passed to the TQWidget + constructor. + + This constructor does not do automatic layout. +*/ + +TQGroupBox::TQGroupBox( const TQString &title, TQWidget *parent, const char *name ) + : TQFrame( 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 TQWidget + constructor. +*/ + +TQGroupBox::TQGroupBox( int strips, Orientation orientation, + TQWidget *parent, const char *name ) + : TQFrame( 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 TQWidget + constructor. +*/ + +TQGroupBox::TQGroupBox( int strips, Orientation orientation, + const TQString &title, TQWidget *parent, + const char *name ) + : TQFrame( parent, name ) +{ + init(); + setTitle( title ); + setColumnLayout( strips, orientation ); +} + +/*! + Destroys the group box. +*/ +TQGroupBox::~TQGroupBox() +{ + delete d; +} + +void TQGroupBox::init() +{ + align = AlignAuto; + setFrameStyle( TQFrame::GroupBoxPanel | TQFrame::Sunken ); +#ifndef QT_NO_ACCEL + accel = 0; +#endif + vbox = 0; + grid = 0; + d = new TQGroupBoxPrivate(); + lenvisible = 0; + nCols = nRows = 0; + dir = Horizontal; + marg = 11; + spac = 5; + bFlat = FALSE; +} + +void TQGroupBox::setTextSpacer() +{ + if ( !d->spacer ) + return; + int h = 0; + int w = 0; + if ( isCheckable() || lenvisible ) { + TQFontMetrics 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 ( TQLayoutIterator it = layout()->iterator(); it.current(); ++it ) { + if ( it.current()->layout() ) { + m += it.current()->layout()->margin(); + sp = TQMAX( sp, it.current()->layout()->spacing() ); + break; + } + } + h = TQMAX( fh-m, h ); + h += TQMAX( sp - (h+m - fh), 0 ); + } + } + d->spacer->changeSize( w, h, TQSizePolicy::Minimum, TQSizePolicy::Fixed ); +} + + +void TQGroupBox::setTitle( const TQString &title ) +{ + if ( str == title ) // no change + return; + str = title; +#ifndef QT_NO_ACCEL + if ( accel ) + delete accel; + accel = 0; + int s = TQAccel::shortcutKey( title ); + if ( s ) { + accel = new TQAccel( 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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::NameChanged ); +#endif +} + +/*! + \property TQGroupBox::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 TQGroupBox::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 TQt::AlignmentFlags +*/ + +void TQGroupBox::setAlignment( int alignment ) +{ + align = alignment; +#ifndef QT_NO_CHECKBOX + updateCheckBoxGeometry(); +#endif + update(); +} + +/*! \reimp +*/ +void TQGroupBox::resizeEvent( TQResizeEvent *e ) +{ + TQFrame::resizeEvent(e); +#ifndef QT_NO_CHECKBOX + if ( align & AlignRight || align & AlignCenter || + ( TQApplication::reverseLayout() && !(align & AlignLeft) ) ) + updateCheckBoxGeometry(); +#endif + calculateFrame(); +} + +/*! \reimp + + \internal + overrides TQFrame::paintEvent +*/ + +void TQGroupBox::paintEvent( TQPaintEvent *event ) +{ + TQPainter paint( this ); + + if ( lenvisible && !isCheckable() ) { // draw title + TQFontMetrics fm = paint.fontMetrics(); + int h = fm.height(); + int tw = fm.width( str, lenvisible ) + fm.width(TQChar(' ')); + 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( TQApplication::reverseLayout() ) + x = frameRect().width() - tw - marg; + else + x = marg; + } + TQRect r( x, 0, tw, h ); + int va = style().styleHint(TQStyle::SH_GroupBox_TextLabelVerticalAlignment, this); + if(va & AlignTop) + r.moveBy(0, fm.descent()); + TQColor pen( (TQRgb) style().styleHint(TQStyle::SH_GroupBox_TextLabelColor, this ) ); + if (!style().styleHint(TQStyle::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 ) { + TQRect cbClip = d->checkbox->geometry(); + TQFontMetrics fm = paint.fontMetrics(); + cbClip.setX( cbClip.x() - fm.width(TQChar(' ')) ); + cbClip.setWidth( cbClip.width() + fm.width(TQChar(' ')) ); + paint.setClipRegion( event->region().subtract( cbClip ) ); +#endif + } + if ( bFlat ) { + TQRect fr = frameRect(); + TQPoint p1( fr.x(), fr.y() + 1 ); + TQPoint 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 + TQGroupBox::columns or \l TQGroupBox::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 TQGroupBox::addSpace( int size ) +{ + TQApplication::sendPostedEvents( this, TQEvent::ChildInserted ); + + if ( nCols <= 0 || nRows <= 0 ) + return; + + if ( row >= nRows || col >= nCols ) + grid->expand( row+1, col+1 ); + + if ( size > 0 ) { + TQSpacerItem *spacer + = new TQSpacerItem( ( dir == Horizontal ) ? 0 : size, + ( dir == Vertical ) ? 0 : size, + TQSizePolicy::Fixed, TQSizePolicy::Fixed ); + grid->addItem( spacer, row, col ); + } + + skip(); +} + +/*! + \property TQGroupBox::columns + \brief the number of columns or rows (depending on \l TQGroupBox::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 TQGroupBox::columns() const +{ + if ( dir == Horizontal ) + return nCols; + return nRows; +} + +void TQGroupBox::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 TQGroupBox::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 TQGroupBox::insideSpacing() const +{ + return spac; +} + +/*! + Sets the the width of the inside margin to \a m pixels. + + \sa insideMargin() +*/ +void TQGroupBox::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 TQGroupBox::setInsideSpacing( int s ) +{ + spac = s; + setColumnLayout( columns(), dir ); +} + +/*! + \property TQGroupBox::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 TQGroupBox::setOrientation( TQt::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 TQt 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 TQGroupBox::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 TQVBoxLayout( this, marg, 0 ); + + d->spacer = new TQSpacerItem( 0, 0, TQSizePolicy::Minimum, + TQSizePolicy::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. + TQApplication::sendPostedEvents( this, TQEvent::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 TQGridLayout( nRows, nCols, spac ); + row = col = 0; + grid->setAlignment( AlignTop ); + vbox->addLayout( grid ); + + // Add all children + if ( children() ) { + TQObjectListIt it( *children() ); + TQWidget *w; + while( (w=(TQWidget *)it.current()) != 0 ) { + ++it; + if ( w->isWidgetType() +#ifndef QT_NO_CHECKBOX + && w != d->checkbox +#endif + ) + insertWid( w ); + } + } +} + + +/*! \reimp */ +bool TQGroupBox::event( TQEvent * e ) +{ + if ( e->type() == TQEvent::LayoutHint && layout() ) + setTextSpacer(); + return TQFrame::event( e ); +} + +/*!\reimp */ +void TQGroupBox::childEvent( TQChildEvent *c ) +{ + if ( !c->inserted() || !c->child()->isWidgetType() ) + return; + TQWidget *w = (TQWidget*)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 ); + ((TQGroupBox*)w)->clearWState( WState_ForceDisabled ); + } + } + } +#endif + if ( !grid ) + return; + insertWid( w ); +} + +void TQGroupBox::insertWid( TQWidget* w ) +{ + if ( row >= nRows || col >= nCols ) + grid->expand( row+1, col+1 ); + grid->addWidget( w, row, col ); + skip(); + TQApplication::postEvent( this, new TQEvent( TQEvent::LayoutHint ) ); +} + + +void TQGroupBox::skip() +{ + // Same as TQGrid::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 TQGroupBox::fixFocus() +{ + TQFocusData * fd = focusData(); + TQWidget * orig = fd->home(); + TQWidget * best = 0; + TQWidget * candidate = 0; + TQWidget * w = orig; + do { + TQWidget * 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<TQRadioButton*>(w) + && ((TQRadioButton*)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 TQGroupBox::calculateFrame() +{ + lenvisible = str.length(); + + if ( lenvisible && !isCheckable() ) { // do we have a label? + TQFontMetrics fm = fontMetrics(); + while ( lenvisible ) { + int tw = fm.width( str, lenvisible ) + 4*fm.width(TQChar(' ')); + if ( tw < width() ) + break; + lenvisible--; + } + if ( lenvisible ) { // but do we also have a visible label? + TQRect r = rect(); + int va = style().styleHint(TQStyle::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 + TQRect r = rect(); + int va = style().styleHint(TQStyle::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( TQRect(0,0,0,0) ); // then use client rect +} + + + +/*! \reimp + */ +void TQGroupBox::focusInEvent( TQFocusEvent * ) +{ // note no call to super + fixFocus(); +} + + +/*!\reimp + */ +void TQGroupBox::fontChange( const TQFont & oldFont ) +{ + TQWidget::fontChange( oldFont ); +#ifndef QT_NO_CHECKBOX + updateCheckBoxGeometry(); +#endif + calculateFrame(); + setTextSpacer(); +} + +/*! + \reimp +*/ + +TQSize TQGroupBox::sizeHint() const +{ + TQFontMetrics 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( TQChar(' ') ); +#endif + } else { + tw = fm.width( title() ) + 2 * fm.width( "xx" ); + th = fm.height() + fm.width( TQChar(' ') ); + } + + TQSize s; + if ( layout() ) { + s = TQFrame::sizeHint(); + return s.expandedTo( TQSize( tw, 0 ) ); + } else { + TQRect r = childrenRect(); + TQSize s( 100, 50 ); + s = s.expandedTo( TQSize( tw, th ) ); + if ( r.isNull() ) + return s; + + return s.expandedTo( TQSize( r.width() + 2 * r.x(), r.height()+ 2 * r.y() ) ); + } +} + +/*! + \property TQGroupBox::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 TQGroupBox::isFlat() const +{ + return bFlat; +} + +void TQGroupBox::setFlat( bool b ) +{ + if ( (bool)bFlat == b ) + return; + bFlat = b; + update(); +} + + +/*! + \property TQGroupBox::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 TQGroupBox::setCheckable( bool b ) +{ + if ( (d->checkbox != 0) == b ) + return; + + if ( b ) { + if ( !d->checkbox ) { + d->checkbox = new TQCheckBox( title(), this, "qt_groupbox_checkbox" ); + if (TQButtonGroup *meAsButtonGroup = ::qt_cast<TQButtonGroup*>(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 TQGroupBox::isCheckable() const +{ +#ifndef QT_NO_CHECKBOX + return ( d->checkbox != 0 ); +#else + return FALSE; +#endif +} + + +bool TQGroupBox::isChecked() const +{ +#ifndef QT_NO_CHECKBOX + return d->checkbox && d->checkbox->isChecked(); +#else + return FALSE; +#endif +} + + +/*! + \fn void TQGroupBox::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 TQGroupBox::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 TQGroupBox::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 TQGroupBox::setChildrenEnabled( bool b ) +{ + if ( !children() ) + return; + TQObjectListIt it( *children() ); + TQObject *o; + while( (o = it.current()) ) { + ++it; + if ( o->isWidgetType() +#ifndef QT_NO_CHECKBOX + && o != d->checkbox +#endif + ) { + TQWidget *w = (TQWidget*)o; + if ( b ) { + if ( !w->testWState( WState_ForceDisabled ) ) + w->setEnabled( TRUE ); + } else { + if ( w->isEnabled() ) { + w->setEnabled( FALSE ); + ((TQGroupBox*)w)->clearWState( WState_ForceDisabled ); + } + } + } + } +} + +/*! \reimp */ +void TQGroupBox::setEnabled(bool on) +{ + TQFrame::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 TQGroupBox::updateCheckBoxGeometry() +{ + if ( d->checkbox ) { + TQSize cbSize = d->checkbox->sizeHint(); + TQRect cbRect( 0, 0, cbSize.width(), cbSize.height() ); + + int marg = bFlat ? 2 : 8; + marg += fontMetrics().width( TQChar(' ') ); + + 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( TQApplication::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 000000000..a293ab6e2 --- /dev/null +++ b/src/widgets/qgroupbox.h @@ -0,0 +1,165 @@ +/********************************************************************** +** +** Definition of TQGroupBox widget class +** +** Created : 950203 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQGROUPBOX_H +#define TQGROUPBOX_H + +#ifndef QT_H +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_GROUPBOX + + +class TQAccel; +class TQGroupBoxPrivate; +class TQVBoxLayout; +class TQGridLayout; +class TQSpacerItem; + +class Q_EXPORT TQGroupBox : public TQFrame +{ + Q_OBJECT + Q_PROPERTY( TQString 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: + TQGroupBox( TQWidget* parent=0, const char* name=0 ); + TQGroupBox( const TQString &title, + TQWidget* parent=0, const char* name=0 ); + TQGroupBox( int strips, Orientation o, + TQWidget* parent=0, const char* name=0 ); + TQGroupBox( int strips, Orientation o, const TQString &title, + TQWidget* parent=0, const char* name=0 ); + ~TQGroupBox(); + + virtual void setColumnLayout(int strips, Orientation o); + + TQString title() const { return str; } + virtual void setTitle( const TQString &); + + 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 ); + TQSize 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( TQEvent * ); + void childEvent( TQChildEvent * ); + void resizeEvent( TQResizeEvent * ); + void paintEvent( TQPaintEvent * ); + void focusInEvent( TQFocusEvent * ); + void fontChange( const TQFont & ); + +private slots: + void fixFocus(); + void setChildrenEnabled( bool b ); + +private: + void skip(); + void init(); + void calculateFrame(); + void insertWid( TQWidget* ); + void setTextSpacer(); +#ifndef QT_NO_CHECKBOX + void updateCheckBoxGeometry(); +#endif + TQString str; + int align; + int lenvisible; +#ifndef QT_NO_ACCEL + TQAccel * accel; +#endif + TQGroupBoxPrivate * d; + + TQVBoxLayout *vbox; + TQGridLayout *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) + TQGroupBox( const TQGroupBox & ); + TQGroupBox &operator=( const TQGroupBox & ); +#endif +}; + + +#endif // QT_NO_GROUPBOX + +#endif // TQGROUPBOX_H diff --git a/src/widgets/qhbox.cpp b/src/widgets/qhbox.cpp new file mode 100644 index 000000000..b9375769d --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qhbox.h" +#ifndef QT_NO_HBOX +#include "qlayout.h" +#include "qapplication.h" +#include "qobjectlist.h" + + +/*! + \class TQHBox qhbox.h + \brief The TQHBox 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 TQHBox + + \sa TQHBoxLayout TQVBox TQGrid +*/ + + +/*! + Constructs an hbox widget with parent \a parent, called \a name. + The parent, name and widget flags, \a f, are passed to the TQFrame + constructor. +*/ +TQHBox::TQHBox( TQWidget *parent, const char *name, WFlags f ) + :TQFrame( parent, name, f ) +{ + lay = new TQHBoxLayout( 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 TQVBox class. You should never + need to use it directly. + + The \a parent, \a name and widget flags, \a f, are passed to the + TQFrame constructor. +*/ + +TQHBox::TQHBox( bool horizontal, TQWidget *parent , const char *name, WFlags f ) + :TQFrame( parent, name, f ) +{ + lay = new TQBoxLayout( this, + horizontal ? TQBoxLayout::LeftToRight : TQBoxLayout::Down, + frameWidth(), frameWidth(), name ); + lay->setAutoAdd( TRUE ); +} + +/*!\reimp + */ +void TQHBox::frameChanged() +{ + if ( !layout() ) + return; + layout()->setMargin( frameWidth() ); +} + + +/*! + Sets the spacing between the child widgets to \a space. +*/ + +void TQHBox::setSpacing( int space ) +{ + if ( layout() ) // ### why not use this->lay? + layout()->setSpacing( space ); +} + + +/*! + \reimp +*/ + +TQSize TQHBox::sizeHint() const +{ + TQWidget *mThis = (TQWidget*)this; + TQApplication::sendPostedEvents( mThis, TQEvent::ChildInserted ); + return TQFrame::sizeHint(); +} + +/*! + Sets the stretch factor of widget \a w to \a stretch. Returns TRUE if + \a w is found. Otherwise returns FALSE. + + \sa TQBoxLayout::setStretchFactor() \link layout.html Layouts\endlink +*/ +bool TQHBox::setStretchFactor( TQWidget* w, int stretch ) +{ + TQWidget *mThis = (TQWidget*)this; + TQApplication::sendPostedEvents( mThis, TQEvent::ChildInserted ); + return lay->setStretchFactor( w, stretch ); +} +#endif diff --git a/src/widgets/qhbox.h b/src/widgets/qhbox.h new file mode 100644 index 000000000..cb3886262 --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + + +#ifndef TQHBOX_H +#define TQHBOX_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_HBOX + +#include "qframe.h" + +class TQBoxLayout; + +class Q_EXPORT TQHBox : public TQFrame +{ + Q_OBJECT +public: + TQHBox( TQWidget* parent=0, const char* name=0, WFlags f=0 ); + + void setSpacing( int ); + bool setStretchFactor( TQWidget*, int stretch ); + TQSize sizeHint() const; + +protected: + TQHBox( bool horizontal, TQWidget* parent, const char* name, WFlags f = 0 ); + void frameChanged(); + +private: + TQBoxLayout *lay; + +#if defined(Q_DISABLE_COPY) + TQHBox( const TQHBox & ); + TQHBox &operator=( const TQHBox & ); +#endif +}; + +#endif // QT_NO_HBOX + +#endif // TQHBOX_H diff --git a/src/widgets/qhbuttongroup.cpp b/src/widgets/qhbuttongroup.cpp new file mode 100644 index 000000000..08da1a99d --- /dev/null +++ b/src/widgets/qhbuttongroup.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Implementation of TQHButtonGroup class +** +** Created : 990602 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qhbuttongroup.h" +#ifndef QT_NO_HBUTTONGROUP + +/*! + \class TQHButtonGroup qhbuttongroup.h + \brief The TQHButtonGroup widget organizes TQButton widgets in a + group with one horizontal row. + + \ingroup organizers + \ingroup geomanagement + \ingroup appearance + + TQHButtonGroup is a convenience class that offers a thin layer on + top of TQButtonGroup. From a layout point of view it is effectively + a TQHBox that offers a frame with a title and is specifically + designed for buttons. From a functionality point of view it is a + TQButtonGroup. + + \img qbuttongroup-h.png TQButtonGroup + + \sa TQVButtonGroup +*/ + +/*! + Constructs a horizontal button group with no title. + + The \a parent and \a name arguments are passed to the TQWidget + constructor. +*/ +TQHButtonGroup::TQHButtonGroup( TQWidget *parent, const char *name ) + : TQButtonGroup( 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 TQWidget + constructor. +*/ + +TQHButtonGroup::TQHButtonGroup( const TQString &title, TQWidget *parent, + const char *name ) + : TQButtonGroup( 1, Vertical /* sic! */, title, parent, name ) +{ +} + +/*! + Destroys the horizontal button group, deleting its child widgets. +*/ +TQHButtonGroup::~TQHButtonGroup() +{ +} +#endif diff --git a/src/widgets/qhbuttongroup.h b/src/widgets/qhbuttongroup.h new file mode 100644 index 000000000..91616ed01 --- /dev/null +++ b/src/widgets/qhbuttongroup.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Definition of TQHButtonGroup class +** +** Created : 990602 +** +** Copyright (C) 1999-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQHBUTTONGROUP_H +#define TQHBUTTONGROUP_H + +#ifndef QT_H +#include "qbuttongroup.h" +#endif // QT_H + +#ifndef QT_NO_HBUTTONGROUP + +class Q_EXPORT TQHButtonGroup : public TQButtonGroup +{ + Q_OBJECT +public: + TQHButtonGroup( TQWidget* parent=0, const char* name=0 ); + TQHButtonGroup( const TQString &title, TQWidget* parent=0, const char* name=0 ); + ~TQHButtonGroup(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQHButtonGroup( const TQHButtonGroup & ); + TQHButtonGroup &operator=( const TQHButtonGroup & ); +#endif +}; + + +#endif // QT_NO_HBUTTONGROUP + +#endif // TQHBUTTONGROUP_H diff --git a/src/widgets/qheader.cpp b/src/widgets/qheader.cpp new file mode 100644 index 000000000..8e029ca7c --- /dev/null +++ b/src/widgets/qheader.cpp @@ -0,0 +1,2049 @@ +/**************************************************************************** +** +** Implementation of TQHeader 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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQHeaderData +{ +public: + TQHeaderData(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; + } + + + TQMemArray<TQCOORD> sizes; + int height; // we abuse the heights as widths for vertical layout + bool heightDirty; + TQMemArray<TQCOORD> positions; // sorted by index + TQPtrVector<TQString> labels; + TQPtrVector<TQIconSet> iconsets; + TQMemArray<int> i2s; + TQMemArray<int> s2i; + + TQBitArray clicks; + TQBitArray 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 TQHeader qheader.h + \brief The TQHeader 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 TQTable and TQListView for example. + + A header is composed of one or more \e sections, each of which can + display a text label and an \link TQIconSet 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 + TQHeader 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. + + TQHeader 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 TQListView TQTable +*/ + + + +/*! + Constructs a horizontal header called \a name, with parent \a + parent. +*/ + +TQHeader::TQHeader( TQWidget *parent, const char *name ) + : TQWidget( parent, name, WStaticContents ) +{ + orient = Horizontal; + init( 0 ); +} + +/*! + Constructs a horizontal header called \a name, with \a n sections + and parent \a parent. +*/ + +TQHeader::TQHeader( int n, TQWidget *parent, const char *name ) + : TQWidget( parent, name, WStaticContents ) +{ + orient = Horizontal; + init( n ); +} + +/*! + Destroys the header and all its sections. +*/ + +TQHeader::~TQHeader() +{ + delete d; + d = 0; +} + +/*! \reimp + */ + +void TQHeader::showEvent( TQShowEvent *e ) +{ + calculatePositions(); + TQWidget::showEvent( e ); +} + +/*! + \fn void TQHeader::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 TQHeader::clicked( int section ) + + If isClickEnabled() is TRUE, this signal is emitted when the user + clicks section \a section. + + \sa pressed(), released() +*/ + +/*! + \fn void TQHeader::pressed( int section ) + + This signal is emitted when the user presses section \a section + down. + + \sa released() +*/ + +/*! + \fn void TQHeader::released( int section ) + + This signal is emitted when section \a section is released. + + \sa pressed() +*/ + + +/*! + \fn void TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::count + \brief the number of sections in the header +*/ + +int TQHeader::count() const +{ + return d->count; +} + + +/*! + \property TQHeader::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 TQHeader::init( int n ) +{ + state = Idle; + cachedPos = 0; // unused + d = new TQHeaderData( 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( TQSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Fixed ) ); +} + +/*! + \property TQHeader::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 TQHeader::setOrientation( Orientation orientation ) +{ + if ( orient == orientation ) + return; + orient = orientation; + if ( orient == Horizontal ) + setSizePolicy( TQSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Fixed ) ); + else + setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Preferred ) ); + update(); + updateGeometry(); +} + + +/* + Paints a rectangle starting at \a p, with length \s. +*/ +void TQHeader::paintRect( int p, int s ) +{ + TQPainter paint( this ); + paint.setPen( TQPen( 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 TQHeader::markLine( int idx ) +{ + TQPainter paint( this ); + paint.setPen( TQPen( black, 1, DotLine ) ); + int MARKSIZE = style().pixelMetric( TQStyle::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 TQHeader::unMarkLine( int idx ) +{ + if ( idx < 0 ) + return; + int MARKSIZE = style().pixelMetric( TQStyle::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 TQHeader::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 TQHeader::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( TQStyle::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 TQHeader::handleAt(int p) +{ + int section = d->sectionAt( p ); + if ( section >= 0 ) { + int GripMargin = (bool)d->resize[ section ] ? + style().pixelMetric( TQStyle::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 TQHeader::moveCell( int fromIdx, int toIdx ) +{ + moveSection( mapToSection(fromIdx), toIdx ); +} + + + +/*! + Move and signal and repaint. + */ + +void TQHeader::handleColumnMove( int fromIdx, int toIdx ) +{ + int s = d->i2s[fromIdx]; + if ( fromIdx < toIdx ) + toIdx++; //Convert to + TQRect r = sRect( fromIdx ); + r |= sRect( toIdx ); + moveSection( s, toIdx ); + update( r ); + emit moved( fromIdx, toIdx ); + emit indexChange( s, fromIdx, toIdx ); +} + +/*! + \reimp +*/ +void TQHeader::keyPressEvent( TQKeyEvent *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 + TQRect r = sRect( d->focusIdx ); + d->focusIdx = (d->focusIdx + count() + dir) % count(); + r |= sRect( d->focusIdx ); + update( r ); + } + } else { + e->ignore(); + } +} + +/*! + \reimp +*/ +void TQHeader::keyReleaseEvent( TQKeyEvent *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 TQHeader::mousePressEvent( TQMouseEvent *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( TQStyle::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 TQHeader::mouseReleaseEvent( TQMouseEvent *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 TQHeader::mouseMoveEvent( TQMouseEvent *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 ( TQABS( 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( "TQHeader::mouseMoveEvent: (%s) unknown state", name() ); + break; + } +} + +/*! \reimp */ + +void TQHeader::mouseDoubleClickEvent( TQMouseEvent *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 TQHeader::handleColumnResize( int index, int c, bool final, bool recalcAll ) +{ + int section = d->i2s[index]; + int GripMargin = (bool)d->resize[ section ] ? + style().pixelMetric( TQStyle::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. +*/ + +TQRect TQHeader::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 TQRect( s, 0, width() - s + 10, height() ); + else + return TQRect( 0, s, width(), height() - s + 10 ); + } + if ( section < 0 ) + return rect(); // ### eeeeevil + + if ( reverse() ) + return TQRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(), + 0, d->sizes[section], height() ); + else if ( orient == Horizontal ) + return TQRect( d->positions[index]-offset(), 0, d->sizes[section], height() ); + else + return TQRect( 0, d->positions[index]-offset(), width(), d->sizes[section] ); +} + +/*! + Returns the rectangle covered by section \a section. +*/ + +TQRect TQHeader::sectionRect( int section ) const +{ + int index = mapToIndex( section ); + if ( section < 0 ) + return rect(); // ### eeeeevil + + if ( reverse() ) + return TQRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(), + 0, d->sizes[section], height() ); + else if ( orient == Horizontal ) + return TQRect( d->positions[index]-offset(), 0, d->sizes[section], height() ); + else + return TQRect( 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 TQHeader::setLabel( int section, const TQIconSet& iconset, + const TQString &s, int size ) +{ + if ( section < 0 || section >= count() ) + return; + d->iconsets.insert( section, new TQIconSet( 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 TQHeader::setLabel( int section, const TQString &s, int size ) +{ + if ( section < 0 || section >= count() ) + return; + if ( s.isNull() ) + d->labels.remove( section ); + else + d->labels.insert( section, new TQString( 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 TQString::null is returned. +*/ +TQString TQHeader::label( int section ) const +{ + if ( section < 0 || section >= count() ) + return TQString::null; + if ( d->labels[ section ] ) + return *( d->labels[ section ] ); + else if ( qt_qheader_label_return_null_strings ) + return TQString::null; + else + return TQString::number( section + 1 ); +} + +/*! + Returns the icon set for section \a section. If the section does + not exist, 0 is returned. +*/ + +TQIconSet *TQHeader::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 TQHeader::addLabel( const TQIconSet& iconset, const TQString &s, int size ) +{ + int n = count() + 1; + d->iconsets.resize( n + 1 ); + d->iconsets.insert( n - 1, new TQIconSet( iconset ) ); + return addLabel( s, size ); +} + +/*! + Removes section \a section. If the section does not exist, nothing + happens. +*/ +void TQHeader::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(); + } +} + +TQSize TQHeader::sectionSizeHint( int section, const TQFontMetrics& fm ) const +{ + int iw = 0; + int ih = 0; + if ( d->iconsets[section] != 0 ) { + TQSize isize = d->iconsets[section]->pixmap( TQIconSet::Small, + TQIconSet::Normal ).size(); + iw = isize.width() + 2; + ih = isize.height(); + } + + TQRect bound; + TQString *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)); + TQStringList list = TQStringList::split('\n', *label); + for (int i=0; i <(int)list.count(); ++i) { + int tmpw = fm.width(*(list.at(i))); + w = TQMAX(w, tmpw); + } + } else { + bound.setHeight(fm.height()); + w = fm.width(*label); + } + bound.setWidth( w ); + } + int arrowWidth = 0; + if ( d->sortSection == section ) + arrowWidth = ( ( orient == TQt::Horizontal ? height() : width() ) / 2 ) + 8; + int height = TQMAX( bound.height() + 2, ih ) + 4; + int width = bound.width() + style().pixelMetric( TQStyle::PM_HeaderMargin ) * 4 + + iw + arrowWidth; + return TQSize( 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 TQHeader::setSectionSizeAndHeight( int section, int size ) +{ + TQSize 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 TQHeader::addLabel( const TQString &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 TQString( 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 TQHeader::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 TQHeader::setIsATableHeader( bool b ) +{ + d->is_a_table_header = b; +} + +/*! \reimp */ +TQSize TQHeader::sizeHint() const +{ + int width; + int height; + + constPolish(); + TQFontMetrics 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 = TQMAX( d->height, h ); + } + d->heightDirty = FALSE; + } + + if ( orient == Horizontal ) { + height = fm.lineSpacing() + 6; + width = 0; + height = TQMAX( height, d->height ); + for ( int i = 0; i < count(); i++ ) + width += d->sizes[i]; + } else { + width = fm.width( ' ' ); + height = 0; + width = TQMAX( width, d->height ); + for ( int i = 0; i < count(); i++ ) + height += d->sizes[i]; + } + return (style().sizeFromContents(TQStyle::CT_Header, this, + TQSize(width, height)).expandedTo(TQApplication::globalStrut())); +} + +/*! + \property TQHeader::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 TQHeader::offset() const +{ + if ( reverse() ) + return d->lastPos - width() - offs; + return offs; +} + +void TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::paintSection( TQPainter *p, int index, const TQRect& fr ) +{ + int section = mapToSection( index ); + + if ( section < 0 ) { + style().drawPrimitive( TQStyle::PE_HeaderSection, p, fr, + colorGroup(), TQStyle::Style_Raised | + (isEnabled() ? TQStyle::Style_Enabled : 0) | + ( orient == Horizontal ? TQStyle::Style_Horizontal : 0 ), + TQStyleOption( this ) ); + return; + } + + if ( sectionSize( section ) <= 0 ) + return; + + TQStyle::SFlags flags = (orient == Horizontal ? TQStyle::Style_Horizontal : TQStyle::Style_Default); + //pass in some hint about the sort indicator if it is used + if(d->sortSection != section) + flags |= TQStyle::Style_Off; + else if(!d->sortDirection) + flags |= TQStyle::Style_Up; + if(isEnabled()) + flags |= TQStyle::Style_Enabled; + if(isClickEnabled(section)) { + if(section == d->sortSection) + flags |= TQStyle::Style_Sunken; //currently selected + if((state == Pressed || state == Moving) && index == handleIdx) + flags |= TQStyle::Style_Down; //currently pressed + + } + if(!(flags & TQStyle::Style_Down)) + flags |= TQStyle::Style_Raised; + p->setBrushOrigin( fr.topLeft() ); + if ( d->clicks[section] ) { + style().drawPrimitive( TQStyle::PE_HeaderSection, p, fr, + colorGroup(), flags, + TQStyleOption( this ) ); + } else { + p->save(); + p->setClipRect( fr ); // hack to keep styles working + if ( orientation() == Horizontal ) { + style().drawPrimitive( TQStyle::PE_HeaderSection, p, + TQRect(fr.x() - 2, fr.y() - 2, fr.width() + 4, fr.height() + 4), + colorGroup(), flags, + TQStyleOption( this ) ); + + p->setPen( colorGroup().color( TQColorGroup::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( TQColorGroup::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( TQColorGroup::Mid ) ); + p->drawLine( fr.x() + fr.width() - 2, fr.y(), + fr.x() + fr.width() - 2, fr.y() + fr.height() - 1 ); + } + } else { + style().drawPrimitive( TQStyle::PE_HeaderSection, p, + TQRect(fr.x() - 2, fr.y() - 2, fr.width() + 4, fr.height() + 4), + colorGroup(), flags, + TQStyleOption( this ) ); + + p->setPen( colorGroup().color( TQColorGroup::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( TQColorGroup::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( TQColorGroup::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 TQHeader::paintSectionLabel( TQPainter *p, int index, const TQRect& fr ) +{ + int section = mapToSection( index ); + if ( section < 0 ) + return; + + int dx = 0, dy = 0; + TQStyle::SFlags flags = TQStyle::Style_Default; + if ( index == handleIdx && ( state == Pressed || state == Moving ) ) { + dx = style().pixelMetric( TQStyle::PM_ButtonShiftHorizontal, this ); + dy = style().pixelMetric( TQStyle::PM_ButtonShiftVertical, this ); + flags |= TQStyle::Style_Sunken; + } + if ( isEnabled() ) + flags |= TQStyle::Style_Enabled; + + + TQRect r( fr.x() + style().pixelMetric( TQStyle::PM_HeaderMargin ) + dx, fr.y() + 2 + dy, + fr.width() - 6, fr.height() - 4 ); + + style().drawControl( TQStyle::CE_HeaderLabel, p, this, r, colorGroup(), flags, + TQStyleOption( section ) ); + + int arrowWidth = ( orient == TQt::Horizontal ? height() : width() ) / 2; + int arrowHeight = fr.height() - 6; + TQSize ssh = sectionSizeHint( section, p->fontMetrics() ); + int tw = ( orient == TQt::Horizontal ? ssh.width() : ssh.height() ); + int ew = 0; + + if ( style().styleHint( TQStyle::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; + } + TQStyle::SFlags flags = TQStyle::Style_Default; + if ( isEnabled() ) + flags |= TQStyle::Style_Enabled; + if ( d->sortDirection ) + flags |= TQStyle::Style_Down; + else + flags |= TQStyle::Style_Up; + TQRect 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( TQStyle::PE_HeaderArrow, p, + ar, colorGroup(), flags, TQStyleOption( this ) ); + } +} + + +/*! \reimp */ +void TQHeader::paintEvent( TQPaintEvent *e ) +{ + TQPainter 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-- ) { + TQRect 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++ ) { + TQRect 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 ) { + TQRect fr( r.x()+2, r.y()+2, r.width()-4, r.height()-4 ); + style().drawPrimitive( TQStyle::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 TQHeader::setSortIndicator( int section, bool ascending ) +{ + d->sortSection = section; + if ( section != -1 ) + oldHandleIdx = section; + d->sortDirection = ascending; + update(); + updateGeometry(); +} + +/*! + \fn void TQHeader::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 TQHeader::sortIndicatorSection() const +{ + return d->sortSection; +} + +/*! + Returns the implied sort order of the TQHeaders sort indicator. + + \sa setSortIndicator(), sortIndicatorSection() +*/ + +TQt::SortOrder TQHeader::sortIndicatorOrder() const +{ + return d->sortDirection ? Ascending : Descending; +} + +/*! + Resizes section \a section to \a s pixels wide (or high). +*/ + +void TQHeader::resizeSection( int section, int s ) +{ + setCellSize( section, s ); + update(); +} + +/*! + Returns the width (or height) of the \a section in pixels. +*/ + +int TQHeader::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 TQHeader::sectionPos( int section ) const +{ + if ( d->positionsDirty ) + ((TQHeader *)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 TQHeader::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 TQTable 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., TQTable does not call moveSection() + to move sections but handles move operations internally. + + \sa mapToIndex() +*/ + +int TQHeader::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 TQTable 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., TQTable does not call moveSection() + to move sections but handles move operations internally. + + \sa mapToSection() +*/ + +int TQHeader::mapToIndex( int section ) const +{ + return ( section >= 0 && section < count() ) ? d->s2i[ section ] : -1; +} + +/*! + Moves section \a section to index position \a toIndex. +*/ + +void TQHeader::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 TQHeader::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 TQHeader::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 TQHeader::isMovingEnabled() const +{ + return d->move; +} + +/*! \reimp */ + +void TQHeader::setUpdatesEnabled( bool enable ) +{ + if ( enable ) + calculatePositions(); + TQWidget::setUpdatesEnabled( enable ); +} + + +bool TQHeader::reverse () const +{ +#if 0 + return ( orient == TQt::Horizontal && TQApplication::reverseLayout() ); +#else + return FALSE; +#endif +} + +/*! \reimp */ +void TQHeader::resizeEvent( TQResizeEvent *e ) +{ + if ( e ) + TQWidget::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 TQHeader::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 TQHeader::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 TQHeader::headerWidth() const +{ + if ( d->pos_dirty ) { + ( (TQHeader*)this )->calculatePositions(); + d->pos_dirty = FALSE; + } + return d->lastPos; +} + +void TQHeader::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 TQHeader::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 TQHeader::setStretchEnabled( bool b, int section ) +{ + if ( b ) + d->fullSize = section; + else + d->fullSize = -2; + adjustHeaderSize(); +} + +bool TQHeader::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 TQHeader::isStretchEnabled( int section ) const +{ + return d->fullSize == section; +} + +/*! + \reimp +*/ +void TQHeader::fontChange( const TQFont &oldFont ) +{ + TQFontMetrics fm = fontMetrics(); + d->height = ( orient == Horizontal ) ? fm.lineSpacing() + 6 : fm.width( ' ' ); + TQWidget::fontChange( oldFont ); +} + +#endif // QT_NO_HEADER diff --git a/src/widgets/qheader.h b/src/widgets/qheader.h new file mode 100644 index 000000000..f88f7ba2d --- /dev/null +++ b/src/widgets/qheader.h @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Definition of TQHeader 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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQHEADER_H +#define TQHEADER_H + +#ifndef QT_H +#include "qwidget.h" +#include "qstring.h" +#include "qiconset.h" // conversion TQPixmap->TQIconset +#endif // QT_H + +#ifndef QT_NO_HEADER + +class TQShowEvent; +class TQHeaderData; +class TQTable; + +class Q_EXPORT TQHeader : public TQWidget +{ + friend class TQTable; + friend class TQTableHeader; + friend class TQListView; + + 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: + TQHeader( TQWidget* parent=0, const char* name=0 ); + TQHeader( int, TQWidget* parent=0, const char* name=0 ); + ~TQHeader(); + + int addLabel( const TQString &, int size = -1 ); + int addLabel( const TQIconSet&, const TQString &, int size = -1 ); + void removeLabel( int section ); + virtual void setLabel( int, const TQString &, int size = -1 ); + virtual void setLabel( int, const TQIconSet&, const TQString &, int size = -1 ); + TQString label( int section ) const; + TQIconSet* 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; + TQRect 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; + + TQSize 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( TQPaintEvent * ); + void showEvent( TQShowEvent *e ); + void resizeEvent( TQResizeEvent *e ); + TQRect sRect( int index ); + + virtual void paintSection( TQPainter *p, int index, const TQRect& fr); + virtual void paintSectionLabel( TQPainter* p, int index, const TQRect& fr ); + + void fontChange( const TQFont & ); + + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void mouseDoubleClickEvent( TQMouseEvent * ); + + void keyPressEvent( TQKeyEvent * ); + void keyReleaseEvent( TQKeyEvent * ); + +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 ); + TQSize sectionSizeHint( int section, const TQFontMetrics& 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; + TQCOORD clickPos; + bool trackingIsOn; + int oldHandleIdx; + int cachedPos; // not used + Orientation orient; + + TQHeaderData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQHeader( const TQHeader & ); + TQHeader &operator=( const TQHeader & ); +#endif +}; + + +inline TQHeader::Orientation TQHeader::orientation() const +{ + return orient; +} + +inline void TQHeader::setTracking( bool enable ) { trackingIsOn = enable; } +inline bool TQHeader::tracking() const { return trackingIsOn; } + +extern Q_EXPORT bool qt_qheader_label_return_null_strings; // needed for professional edition + +#endif // QT_NO_HEADER + +#endif // TQHEADER_H diff --git a/src/widgets/qhgroupbox.cpp b/src/widgets/qhgroupbox.cpp new file mode 100644 index 000000000..098a5eacb --- /dev/null +++ b/src/widgets/qhgroupbox.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Implementation of TQHGroupBox class +** +** Created : 990602 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qhgroupbox.h" +#ifndef QT_NO_HGROUPBOX + +/*! + \class TQHGroupBox qhgroupbox.h + + \brief The TQHGroupBox widget organizes widgets in a group with one + horizontal row. + + \ingroup organizers + \ingroup geomanagement + \ingroup appearance + + TQHGroupBox is a convenience class that offers a thin layer on top + of TQGroupBox. Think of it as a TQHBox that offers a frame with a + title. + + \img qgroupboxes.png Group Boxes + + \sa TQVGroupBox +*/ + +/*! + Constructs a horizontal group box with no title. + + The \a parent and \a name arguments are passed to the TQWidget + constructor. +*/ +TQHGroupBox::TQHGroupBox( TQWidget *parent, const char *name ) + : TQGroupBox( 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 TQWidget + constructor. +*/ + +TQHGroupBox::TQHGroupBox( const TQString &title, TQWidget *parent, + const char *name ) + : TQGroupBox( 1, Vertical /* sic! */, title, parent, name ) +{ +} + +/*! + Destroys the horizontal group box, deleting its child widgets. +*/ +TQHGroupBox::~TQHGroupBox() +{ +} +#endif diff --git a/src/widgets/qhgroupbox.h b/src/widgets/qhgroupbox.h new file mode 100644 index 000000000..3a4dedbc2 --- /dev/null +++ b/src/widgets/qhgroupbox.h @@ -0,0 +1,67 @@ +/********************************************************************** +** +** Definition of TQHGroupBox widget class +** +** Created : 990602 +** +** Copyright (C) 1999-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQHGROUPBOX_H +#define TQHGROUPBOX_H + +#ifndef QT_H +#include "qgroupbox.h" +#endif // QT_H + +#ifndef QT_NO_HGROUPBOX + +class Q_EXPORT TQHGroupBox : public TQGroupBox +{ + Q_OBJECT +public: + TQHGroupBox( TQWidget* parent=0, const char* name=0 ); + TQHGroupBox( const TQString &title, TQWidget* parent=0, const char* name=0 ); + ~TQHGroupBox(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQHGroupBox( const TQHGroupBox & ); + TQHGroupBox &operator=( const TQHGroupBox & ); +#endif +}; + +#endif // QT_NO_HGROUPBOX + +#endif // TQHGROUPBOX_H diff --git a/src/widgets/qlabel.cpp b/src/widgets/qlabel.cpp new file mode 100644 index 000000000..d9b400151 --- /dev/null +++ b/src/widgets/qlabel.cpp @@ -0,0 +1,1190 @@ +/********************************************************************** +** +** Implementation of TQLabel widget class +** +** Created : 941215 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQLabelPrivate +{ +public: + TQLabelPrivate() + :img(0), pix(0), valid_hints( -1 ) + {} + TQImage* img; // for scaled contents + TQPixmap* pix; // for scaled contents + TQSize sh; + TQSize msh; + int valid_hints; // stores the frameWidth() for the stored size hint, -1 otherwise +}; + + +/*! + \class TQLabel qlabel.h + \brief The TQLabel widget provides a text or image display. + + \ingroup basic + \ingroup text + \mainclass + + TQLabel 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 TQLabel can contain any of the following content types: + \table + \header \i Content \i Setting + \row \i Plain text + \i Pass a TQString to setText(). + \row \i Rich text + \i Pass a TQString that contains rich text to setText(). + \row \i A pixmap + \i Pass a TQPixmap to setPixmap(). + \row \i A movie + \i Pass a TQMovie 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 TQLabel can be tuned in several ways. All the + settings of TQFrame are available for specifying a widget frame. + The positioning of the content within the TQLabel 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 + TQLabel *label = new TQLabel( this ); + label->setFrameStyle( TQFrame::Panel | TQFrame::Sunken ); + label->setText( "first line\nsecond line" ); + label->setAlignment( AlignBottom | AlignRight ); + \endcode + + A TQLabel is often used as a label for an interactive widget. For + this use TQLabel provides a useful mechanism for adding an + accelerator key (see TQAccel) that will set the keyboard focus to + the other widget (called the TQLabel's "buddy"). For example: + \code + TQLineEdit* phoneEdit = new TQLineEdit( this, "phoneEdit" ); + TQLabel* phoneLabel = new TQLabel( phoneEdit, "&Phone:", this, "phoneLabel" ); + \endcode + + In this example, keyboard focus is transferred to the label's + buddy (the TQLineEdit) 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 TQLineEdit, TQTextEdit, TQPixmap, TQMovie, + \link guibooks.html#fowler GUI Design Handbook: Label\endlink +*/ + +/*! + \fn TQPicture * TQLabel::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 TQFrame constructor. + + \sa setAlignment(), setFrameStyle(), setIndent() +*/ + +TQLabel::TQLabel( TQWidget *parent, const char *name, WFlags f ) + : TQFrame( 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 TQFrame constructor. + + \sa setText(), setAlignment(), setFrameStyle(), setIndent() +*/ + +TQLabel::TQLabel( const TQString &text, TQWidget *parent, const char *name, + WFlags f ) + : TQFrame( 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 TQFrame constructor. + + \sa setText(), setBuddy(), setAlignment(), setFrameStyle(), + setIndent() +*/ +TQLabel::TQLabel( TQWidget *buddy, const TQString &text, + TQWidget *parent, const char *name, WFlags f ) + : TQFrame( parent, name, f | WMouseNoMask ) +{ + init(); +#ifndef QT_NO_ACCEL + setBuddy( buddy ); +#endif + setText( text ); +} + +/*! + Destroys the label. +*/ + +TQLabel::~TQLabel() +{ + clearContents(); + delete d; +} + + +void TQLabel::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 = TQt::AutoText; +#ifndef QT_NO_RICHTEXT + doc = 0; +#endif + + setSizePolicy( TQSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Preferred ) ); + d = new TQLabelPrivate; +} + + +/*! + \property TQLabel::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. TQLabel 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 TQTextEdit + in read-only mode instead. TQTextEdit will flicker less on resize + and can also provide a scrollbar when necessary. + + \sa text, setTextFormat(), setBuddy(), alignment +*/ + +void TQLabel::setText( const TQString &text ) +{ + if ( ltext == text ) + return; + TQSize osh = sizeHint(); +#ifndef QT_NO_RICHTEXT + bool hadRichtext = doc != 0; +#endif + clearContents(); + ltext = text; +#ifndef QT_NO_RICHTEXT + bool useRichText = (textformat == RichText || + ( ( textformat == AutoText ) && TQStyleSheet::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 = TQAccel::shortcutKey( ltext ); + if ( p ) { + if ( !accel ) + accel = new TQAccel( this, "accel label accel" ); + accel->connectItem( accel->insertItem( p ), + this, SLOT(acceleratorSlot()) ); + } + } +#endif +#ifndef QT_NO_RICHTEXT + if ( useRichText ) { + if ( !hadRichtext ) + align |= WordBreak; + TQString 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 TQSimpleRichText( t, font() ); + } +#endif + + updateLabel( osh ); +} + + +/*! + Clears any label contents. Equivalent to setText( "" ). +*/ + +void TQLabel::clear() +{ + setText( TQString::fromLatin1("") ); +} + +/*! + \property TQLabel::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 TQLabel::autoResize() is TRUE. The buddy accelerator, + if any, is disabled. +*/ +void TQLabel::setPixmap( const TQPixmap &pixmap ) +{ + TQSize osh = sizeHint(); + + if ( !lpixmap || lpixmap->serialNumber() != pixmap.serialNumber() ) { + clearContents(); + lpixmap = new TQPixmap( pixmap ); + } + + if ( lpixmap->depth() == 1 && !lpixmap->mask() ) + lpixmap->setMask( *((TQBitmap *)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 TQLabel::setPicture( const TQPicture &picture ) +{ + TQSize osh = sizeHint(); + clearContents(); + lpicture = new TQPicture( 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(), TQString::setNum(), setBuddy() +*/ + +void TQLabel::setNum( int num ) +{ + TQString 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(), TQString::setNum(), setBuddy() +*/ + +void TQLabel::setNum( double num ) +{ + TQString str; + str.setNum( num ); + setText( str ); +} + +/*! + \property TQLabel::alignment + \brief the alignment of the label's contents + + The alignment is a bitwise OR of \c TQt::AlignmentFlags and \c + TQt::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 TQLabel. + + 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 TQt::AlignmentFlags, alignment, setBuddy(), text +*/ + +void TQLabel::setAlignment( int alignment ) +{ + if ( alignment == align ) + return; + TQSize osh = sizeHint(); +#ifndef QT_NO_ACCEL + if ( lbuddy ) + align = alignment | ShowPrefix; + else +#endif + align = alignment; + +#ifndef QT_NO_RICHTEXT + TQString t = ltext; + if ( !t.isNull() ) { + ltext = TQString::null; + setText( t ); + } +#endif + + updateLabel( osh ); +} + + +/*! + \property TQLabel::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 TQLabel::setIndent( int indent ) +{ + extraMargin = indent; + updateLabel( TQSize( -1, -1 ) ); +} + + +/*! + \fn bool TQLabel::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 TQLabel widgets that are not managed by + a TQLayout (e.g., top-level widgets). + + Auto-resizing is disabled by default. + + \sa autoResize(), adjustSize(), sizeHint() +*/ + +void TQLabel::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. +*/ + +TQSize TQLabel::sizeForWidth( int w ) const +{ + TQRect br; + TQPixmap *pix = pixmap(); +#ifndef QT_NO_PICTURE + TQPicture *pic = picture(); +#else + const int pic = 0; +#endif +#ifndef QT_NO_MOVIE + TQMovie *mov = movie(); +#else + const int mov = 0; +#endif + int hextra = 2 * frameWidth(); + int vextra = hextra; + TQFontMetrics 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 = TQApplication::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 = TQRect( 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 TQSize( wid, hei ); +} + + +/*! + \reimp +*/ + +int TQLabel::heightForWidth( int w ) const +{ + if ( +#ifndef QT_NO_RICHTEXT + doc || +#endif + (align & WordBreak) ) + return sizeForWidth( w ).height(); + return TQWidget::heightForWidth( w ); +} + + + +/*!\reimp +*/ +TQSize TQLabel::sizeHint() const +{ + if ( d->valid_hints != frameWidth() ) + (void) TQLabel::minimumSizeHint(); + return d->sh; +} + +/*! + \reimp +*/ + +TQSize TQLabel::minimumSizeHint() const +{ + if ( d->valid_hints == frameWidth() ) + return d->msh; + + constPolish(); + d->valid_hints = frameWidth(); + d->sh = sizeForWidth( -1 ); + TQSize 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(TQWIDGETSIZE_MAX).height(); + if ( d->sh.height() < sz.height() ) + sz.rheight() = d->sh.height(); + } + if ( sizePolicy().horData() == TQSizePolicy::Ignored ) + sz.rwidth() = -1; + if ( sizePolicy().verData() == TQSizePolicy::Ignored ) + sz.rheight() = -1; + d->msh = sz; + return sz; +} + +/*! + \reimp +*/ +void TQLabel::resizeEvent( TQResizeEvent* e ) +{ + TQFrame::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 ); + TQRect 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 TQFrame? Reduce pixmap flicker + TQRegion reg = TQRect( TQPoint(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 & TQt::AlignVCenter) == TQt::AlignVCenter ) + y += cr.height()/2 - h/2; + else if ( (align & TQt::AlignBottom) == TQt::AlignBottom) + y += cr.height() - h; + if ( (align & TQt::AlignRight) == TQt::AlignRight ) + x += cr.width() - w; + else if ( (align & TQt::AlignHCenter) == TQt::AlignHCenter ) + x += cr.width()/2 - w/2; + if ( x > cr.x() ) + reg = reg.unite( TQRect( cr.x(), cr.y(), x - cr.x(), cr.height() ) ); + if ( y > cr.y() ) + reg = reg.unite( TQRect( cr.x(), cr.y(), cr.width(), y - cr.y() ) ); + + if ( x + w < cr.right() ) + reg = reg.unite( TQRect( x + w, cr.y(), cr.right() - x - w, cr.height() ) ); + if ( y + h < cr.bottom() ) + reg = reg.unite( TQRect( cr.x(), y + h, cr.width(), cr.bottom() - y - h ) ); + + erase( reg ); + } +} + + +/*! + Draws the label contents using the painter \a p. +*/ + +void TQLabel::drawContents( TQPainter *p ) +{ + TQRect cr = contentsRect(); + + TQPixmap *pix = pixmap(); +#ifndef QT_NO_PICTURE + TQPicture *pic = picture(); +#else + const int pic = 0; +#endif +#ifndef QT_NO_MOVIE + TQMovie *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 = TQApplication::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 + TQRect r = style().itemRect( p, cr, align, isEnabled(), &(mov->framePixmap()), + TQString::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(TQStyle::SH_EtchDisabledText, this)) { + TQColorGroup cg = colorGroup(); + cg.setColor( TQColorGroup::Text, cg.light() ); + doc->draw(p, cr.x()+1, cr.y()+yo+1, cr, cg, 0); + } + + // TQSimpleRichText always draws with TQColorGroup::Text as with + // background mode PaletteBase. TQLabel typically has + // background mode PaletteBackground, so we create a temporary + // color group with the text color adjusted. + TQColorGroup cg = colorGroup(); + if ( backgroundMode() != PaletteBase && isEnabled() ) + cg.setColor( TQColorGroup::Text, paletteForegroundColor() ); + + doc->draw(p, cr.x(), cr.y()+yo, cr, cg, 0); + } else +#endif +#ifndef QT_NO_PICTURE + if ( pic ) { + TQRect 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 TQImage( lpixmap->convertToImage() ); + + if ( !d->pix ) + d->pix = new TQPixmap; + 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(TQStyle::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 TQLabel::updateLabel( TQSize oldSizeHint ) +{ + d->valid_hints = -1; + TQSizePolicy 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 TQLabel::acceleratorSlot() +{ + if ( !lbuddy ) + return; + TQWidget * w = lbuddy; + while ( w->focusProxy() ) + w = w->focusProxy(); + if ( !w->hasFocus() && + w->isEnabled() && + w->isVisible() && + w->focusPolicy() != NoFocus ) { + TQFocusEvent::setReason( TQFocusEvent::Shortcut ); + w->setFocus(); + TQFocusEvent::resetReason(); + } +} +#endif + +/*! + \internal + + Internal slot, used to clean up if the buddy widget dies. +*/ +#ifndef QT_NO_ACCEL +void TQLabel::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 TQLabels 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 + TQLineEdit *nameEd = new TQLineEdit( this ); + TQLabel *nameLb = new TQLabel( "&Name:", this ); + nameLb->setBuddy( nameEd ); + TQLineEdit *phoneEd = new TQLineEdit( this ); + TQLabel *phoneLb = new TQLabel( "&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(), TQAccel, setAlignment() +*/ + +void TQLabel::setBuddy( TQWidget *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 && + TQStyleSheet::mightBeRichText(ltext) ) ) ) +#endif + { + int p = TQAccel::shortcutKey( ltext ); + if ( p ) { + if ( !accel ) + accel = new TQAccel( 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() +*/ + +TQWidget * TQLabel::buddy() const +{ + return lbuddy; +} +#endif //QT_NO_ACCEL + + +#ifndef QT_NO_MOVIE +void TQLabel::movieUpdated(const TQRect& rect) +{ + TQMovie *mov = movie(); + if ( mov && !mov->isNull() ) { + TQRect r = contentsRect(); + r = style().itemRect( 0, r, align, isEnabled(), &(mov->framePixmap()), + TQString::null ); + r.moveBy(rect.x(), rect.y()); + r.setWidth(TQMIN(r.width(), rect.width())); + r.setHeight(TQMIN(r.height(), rect.height())); + repaint( r, mov->framePixmap().mask() != 0 ); + } +} + +void TQLabel::movieResized( const TQSize& size ) +{ + d->valid_hints = -1; + if ( autoresize ) + adjustSize(); + movieUpdated( TQRect( TQPoint(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 TQLabel::setMovie( const TQMovie& movie ) +{ + TQSize osh = sizeHint(); + clearContents(); + + lmovie = new TQMovie( movie ); + lmovie->connectResize(this, SLOT(movieResized(const TQSize&))); + lmovie->connectUpdate(this, SLOT(movieUpdated(const TQRect&))); + + 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 TQLabel::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 = TQString::null; +#ifndef QT_NO_ACCEL + if ( accel ) + accel->clear(); +#endif +#ifndef QT_NO_MOVIE + if ( lmovie ) { + lmovie->disconnectResize(this, SLOT(movieResized(const TQSize&))); + lmovie->disconnectUpdate(this, SLOT(movieUpdated(const TQRect&))); + 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() +*/ + +TQMovie* TQLabel::movie() const +{ + return lmovie; +} + +#endif // QT_NO_MOVIE + +/*! + \property TQLabel::backgroundMode + \brief the label's background mode + + Get this property with backgroundMode(). + + \sa TQWidget::setBackgroundMode() +*/ + +/*! + \property TQLabel::textFormat + \brief the label's text format + + See the \c TQt::TextFormat enum for an explanation of the possible + options. + + The default format is \c AutoText. + + \sa text +*/ + +TQt::TextFormat TQLabel::textFormat() const +{ + return textformat; +} + +void TQLabel::setTextFormat( TQt::TextFormat format ) +{ + if ( format != textformat ) { + textformat = format; + TQString t = ltext; + if ( !t.isNull() ) { + ltext = TQString::null; + setText( t ); + } + } +} + +/*! + \reimp +*/ + +void TQLabel::fontChange( const TQFont & ) +{ + if ( !ltext.isEmpty() ) { +#ifndef QT_NO_RICHTEXT + if ( doc ) + doc->setDefaultFont( font() ); +#endif + updateLabel( TQSize( -1, -1 ) ); + } +} + +#ifndef QT_NO_IMAGE_SMOOTHSCALE +/*! + \property TQLabel::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 TQLabel::hasScaledContents() const +{ + return scaledcontents; +} + +void TQLabel::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 TQLabel to font \a f. +*/ + +void TQLabel::setFont( const TQFont &f ) +{ + TQFrame::setFont( f ); +} + +#endif // QT_NO_LABEL diff --git a/src/widgets/qlabel.h b/src/widgets/qlabel.h new file mode 100644 index 000000000..ae2c6baa6 --- /dev/null +++ b/src/widgets/qlabel.h @@ -0,0 +1,174 @@ +/********************************************************************** +** +** Definition of TQLabel widget class +** +** Created : 941215 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQLABEL_H +#define TQLABEL_H + +#ifndef QT_H +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_LABEL + +class TQSimpleRichText; +class TQLabelPrivate; + +class Q_EXPORT TQLabel : public TQFrame +{ + Q_OBJECT + Q_PROPERTY( TQString text READ text WRITE setText ) + Q_PROPERTY( TextFormat textFormat READ textFormat WRITE setTextFormat ) + Q_PROPERTY( TQPixmap 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: + TQLabel( TQWidget *parent, const char* name=0, WFlags f=0 ); + TQLabel( const TQString &text, TQWidget *parent, const char* name=0, + WFlags f=0 ); + TQLabel( TQWidget *buddy, const TQString &, + TQWidget *parent, const char* name=0, WFlags f=0 ); + ~TQLabel(); + + TQString text() const { return ltext; } + TQPixmap *pixmap() const { return lpixmap; } +#ifndef QT_NO_PICTURE + TQPicture *picture() const { return lpicture; } +#endif +#ifndef QT_NO_MOVIE + TQMovie *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 + TQSize sizeHint() const; + TQSize minimumSizeHint() const; +#ifndef QT_NO_ACCEL + virtual void setBuddy( TQWidget * ); + TQWidget *buddy() const; +#endif + int heightForWidth(int) const; + + void setFont( const TQFont &f ); + +public slots: + virtual void setText( const TQString &); + virtual void setPixmap( const TQPixmap & ); +#ifndef QT_NO_PICTURE + virtual void setPicture( const TQPicture & ); +#endif +#ifndef QT_NO_MOVIE + virtual void setMovie( const TQMovie & ); +#endif + virtual void setNum( int ); + virtual void setNum( double ); + void clear(); + +protected: + void drawContents( TQPainter * ); + void fontChange( const TQFont & ); + void resizeEvent( TQResizeEvent* ); + +private slots: +#ifndef QT_NO_ACCEL + void acceleratorSlot(); + void buddyDied(); +#endif +#ifndef QT_NO_MOVIE + void movieUpdated(const TQRect&); + void movieResized(const TQSize&); +#endif + +private: + void init(); + void clearContents(); + void updateLabel( TQSize oldSizeHint ); + TQSize sizeForWidth( int w ) const; + TQString ltext; + TQPixmap *lpixmap; +#ifndef QT_NO_PICTURE + TQPicture *lpicture; +#endif +#ifndef QT_NO_MOVIE + TQMovie * lmovie; +#endif +#ifndef QT_NO_ACCEL + TQWidget * lbuddy; +#endif + ushort align; + short extraMargin; + uint autoresize:1; + uint scaledcontents :1; + TextFormat textformat; +#ifndef QT_NO_RICHTEXT + TQSimpleRichText* doc; +#endif +#ifndef QT_NO_ACCEL + TQAccel * accel; +#endif + TQLabelPrivate* d; + + friend class TQTipLabel; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQLabel( const TQLabel & ); + TQLabel &operator=( const TQLabel & ); +#endif +}; + + +#endif // QT_NO_LABEL + +#endif // TQLABEL_H diff --git a/src/widgets/qlcdnumber.cpp b/src/widgets/qlcdnumber.cpp new file mode 100644 index 000000000..f9b956f79 --- /dev/null +++ b/src/widgets/qlcdnumber.cpp @@ -0,0 +1,1170 @@ +/**************************************************************************** +** +** Implementation of TQLCDNumber class +** +** Created : 940518 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qlcdnumber.h" +#ifndef QT_NO_LCDNUMBER +#include "qbitarray.h" +#include "qpainter.h" + + +/*! + \class TQLCDNumber qlcdnumber.h + + \brief The TQLCDNumber 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(). + + TQLCDNumber 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. TQLCDNumber substitutes spaces for + illegal characters. + + It is not possible to retrieve the contents of a TQLCDNumber + 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, TQLCDNumber is the very oldest part of TQt, 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 TQLabel, TQFrame +*/ + +/*! + \enum TQLCDNumber::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 TQLCDNumber::SegmentStyle + + This type determines the visual appearance of the TQLCDNumber + 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 TQLCDNumber::overflow() + + This signal is emitted whenever the TQLCDNumber is asked to display + a too-large number or a too-long string. + + It is never emitted by setNumDigits(). +*/ + + +static TQString int2string( int num, int base, int ndigits, bool *oflow ) +{ + TQString s; + bool negative; + if ( num < 0 ) { + negative = TRUE; + num = -num; + } else { + negative = FALSE; + } + switch( base ) { + case TQLCDNumber::HEX: + s.sprintf( "%*x", ndigits, num ); + break; + case TQLCDNumber::DEC: + s.sprintf( "%*i", ndigits, num ); + break; + case TQLCDNumber::OCT: + s.sprintf( "%*o", ndigits, num ); + break; + case TQLCDNumber::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 += TQString::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 TQString double2string( double num, int base, int ndigits, bool *oflow ) +{ + TQString s; + if ( base != TQLCDNumber::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 TQFrame + constructor. + + \sa setNumDigits(), setSmallDecimalPoint() +*/ + +TQLCDNumber::TQLCDNumber( TQWidget *parent, const char *name ) + : TQFrame( 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 TQFrame + constructor. + + \sa setNumDigits(), setSmallDecimalPoint() +*/ + +TQLCDNumber::TQLCDNumber( uint numDigits, TQWidget *parent, const char *name ) + : TQFrame( parent, name ) +{ + ndigits = numDigits; + init(); +} + +/*! + \internal +*/ + +void TQLCDNumber::init() +{ + setFrameStyle( TQFrame::Box | TQFrame::Raised ); + val = 0; + base = DEC; + smallPoint = FALSE; + setNumDigits( ndigits ); + setSegmentStyle( Outline ); + d = 0; + setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Minimum ) ); +} + +/*! + Destroys the LCD number. +*/ + +TQLCDNumber::~TQLCDNumber() +{ +} + + +/*! + \property TQLCDNumber::numDigits + \brief the current number of digits displayed + + Corresponds to the current number of digits. If \l + TQLCDNumber::smallDecimalPoint is FALSE, the decimal point occupies + one digit position. + + \sa numDigits, smallDecimalPoint +*/ + +void TQLCDNumber::setNumDigits( int numDigits ) +{ + if ( numDigits > 99 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "TQLCDNumber::setNumDigits: (%s) Max 99 digits allowed", + name( "unnamed" ) ); +#endif + numDigits = 99; + } + if (numDigits < 0 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "TQLCDNumber::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; + TQString 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 ); + TQBitArray 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 TQLCDNumber::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 TQLCDNumber::checkOverflow( double num ) const +{ + bool of; + double2string( num, base, ndigits, &of ); + return of; +} + + +/*! + \property TQLCDNumber::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() +*/ + +TQLCDNumber::Mode TQLCDNumber::mode() const +{ + return (TQLCDNumber::Mode) base; +} + +void TQLCDNumber::setMode( Mode m ) +{ + base = m; + + display( val ); +} + + +/*! + \property TQLCDNumber::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 TQLCDNumber::value() const +{ + return val; +} + +/*! + \overload + + Displays the number \a num. +*/ +void TQLCDNumber::display( double num ) +{ + val = num; + bool of; + TQString s = double2string( num, base, ndigits, &of ); + if ( of ) + emit overflow(); + else + internalSetString( s ); +} + +/*! + \property TQLCDNumber::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 TQLCDNumber::intValue() const +{ + return (int)(val < 0 ? val - 0.5 : val + 0.5); +} + + +/*! + \overload + + Displays the number \a num. +*/ +void TQLCDNumber::display( int num ) +{ + val = (double)num; + bool of; + TQString 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. TQLCDNumber substitutes spaces for + illegal characters. +*/ + +void TQLCDNumber::display( const TQString &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 TQLCDNumber::setHexMode() +{ + setMode( HEX ); +} + + +/*! + Calls setMode( DEC ). Provided for convenience (e.g. for + connecting buttons to it). + + \sa setMode(), setHexMode(), setOctMode(), setBinMode(), mode() +*/ + +void TQLCDNumber::setDecMode() +{ + setMode( DEC ); +} + + +/*! + Calls setMode( OCT ). Provided for convenience (e.g. for + connecting buttons to it). + + \sa setMode(), setHexMode(), setDecMode(), setBinMode(), mode() +*/ + +void TQLCDNumber::setOctMode() +{ + setMode( OCT ); +} + + +/*! + Calls setMode( BIN ). Provided for convenience (e.g. for + connecting buttons to it). + + \sa setMode(), setHexMode(), setDecMode(), setOctMode(), mode() +*/ + +void TQLCDNumber::setBinMode() +{ + setMode( BIN ); +} + + +/*! + \property TQLCDNumber::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 TQLCDNumber::setSmallDecimalPoint( bool b ) +{ + smallPoint = b; +} + + +/*! + Draws the LCD number using painter \a p. This function is called + from TQFrame::paintEvent(). +*/ + + +void TQLCDNumber::drawContents( TQPainter *p ) +{ + if ( smallPoint ) + drawString( digitStr, *p, &points, FALSE ); + else + drawString( digitStr, *p, 0, FALSE ); +} + + +/*! + \internal +*/ + +void TQLCDNumber::internalDisplay( const TQString & ) +{ + // Not used anymore +} + +void TQLCDNumber::internalSetString( const TQString& s ) +{ + TQString buffer; + int i; + int len = s.length(); + TQBitArray 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( TQColorGroup::Background ).pixmap() ) { + digitStr = buffer; + if ( smallPoint ) + points = newPoints; + repaint( contentsRect() ); + } else { + TQPainter p( this ); + if ( !smallPoint ) + drawString( buffer, p ); + else + drawString( buffer, p, &newPoints ); + } +} + +/*! + \internal +*/ + +void TQLCDNumber::drawString( const TQString &s, TQPainter &p, + TQBitArray *newPoints, bool newString ) +{ + TQPoint 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 = TQPoint( 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 TQLCDNumber::drawDigit( const TQPoint &pos, TQPainter &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( TQPointArray &a, const TQPoint &p ) +{ + uint n = a.size(); + a.resize( n + 1 ); + a.setPoint( n, p ); +} + +/*! + \internal +*/ + +void TQLCDNumber::drawSegment( const TQPoint &pos, char segmentNo, TQPainter &p, + int segLen, bool erase ) +{ + TQPoint pt = pos; + int width = segLen/5; + + const TQColorGroup & g = colorGroup(); + TQColor 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, TQPoint(pt.x() + (X),pt.y() + (Y))) +#define LIGHT +#define DARK + + if ( fill ) { + TQPointArray 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 += TQPoint(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 += TQPoint(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 += TQPoint(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 += TQPoint(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 += TQPoint(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 += TQPoint(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 += TQPoint(segLen + width/2 , segLen*2); + else + pt += TQPoint(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 += TQPoint(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 += TQPoint(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( "TQLCDNumber::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(TQPoint(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 += TQPoint(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 += TQPoint(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 += TQPoint(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 += TQPoint(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 += TQPoint(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 += TQPoint(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 += TQPoint(segLen + width/2 , segLen*2); + else + pt += TQPoint(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 += TQPoint(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 += TQPoint(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( "TQLCDNumber::drawSegment: (%s) Internal error." + " Illegal segment id: %d\n", + name( "unnamed" ), segmentNo ); +#endif + } + +#undef LINETO +#undef LIGHT +#undef DARK +} + + + +/*! + \property TQLCDNumber::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 + TQColorGroup::light() and TQColorGroup::dark() for shadow effects. +*/ +void TQLCDNumber::setSegmentStyle( SegmentStyle s ) +{ + fill = ( s == Flat || s == Filled ); + shadow = ( s == Outline || s == Filled ); + update(); +} + +TQLCDNumber::SegmentStyle TQLCDNumber::segmentStyle() const +{ + Q_ASSERT( fill || shadow ); + if ( !fill && shadow ) + return Outline; + if ( fill && shadow ) + return Filled; + return Flat; +} + + +/*!\reimp +*/ +TQSize TQLCDNumber::sizeHint() const +{ + return TQSize( 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 000000000..b473e5374 --- /dev/null +++ b/src/widgets/qlcdnumber.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Definition of TQLCDNumber class +** +** Created : 940518 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQLCDNUMBER_H +#define TQLCDNUMBER_H + +#ifndef QT_H +#include "qframe.h" +#include "qbitarray.h" +#endif // QT_H + +#ifndef QT_NO_LCDNUMBER + + +class TQLCDNumberPrivate; + +class Q_EXPORT TQLCDNumber : public TQFrame // 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: + TQLCDNumber( TQWidget* parent=0, const char* name=0 ); + TQLCDNumber( uint numDigits, TQWidget* parent=0, const char* name=0 ); + ~TQLCDNumber(); + + 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; + + TQSize sizeHint() const; + +public slots: + void display( const TQString &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( TQPainter * ); + +private: + void init(); + void internalDisplay( const TQString &); + void internalSetString( const TQString& s ); + void drawString( const TQString& s, TQPainter &, TQBitArray * = 0, + bool = TRUE ); + //void drawString( const TQString &, TQPainter &, TQBitArray * = 0 ) const; + void drawDigit( const TQPoint &, TQPainter &, int, char, + char = ' ' ); + void drawSegment( const TQPoint &, char, TQPainter &, int, bool = FALSE ); + + int ndigits; + double val; + uint base : 2; + uint smallPoint : 1; + uint fill : 1; + uint shadow : 1; + TQString digitStr; + TQBitArray points; + TQLCDNumberPrivate * d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQLCDNumber( const TQLCDNumber & ); + TQLCDNumber &operator=( const TQLCDNumber & ); +#endif +}; + +inline bool TQLCDNumber::smallDecimalPoint() const +{ return (bool)smallPoint; } + +inline int TQLCDNumber::numDigits() const +{ return ndigits; } + + +#endif // QT_NO_LCDNUMBER + +#endif // TQLCDNUMBER_H diff --git a/src/widgets/qlineedit.cpp b/src/widgets/qlineedit.cpp new file mode 100644 index 000000000..0546bb0d7 --- /dev/null +++ b/src/widgets/qlineedit.cpp @@ -0,0 +1,2925 @@ +/********************************************************************** +** +** Implementation of TQLineEdit widget class +** +** Created : 941011 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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" + TQString(TQKeySequence( TQt::CTRL | TQt::Key_ ## k )) +#else +#define ACCEL_KEY(k) "\t" + TQString("Ctrl+" #k) +#endif + +#define innerMargin 1 + +struct TQLineEditPrivate : public TQt +{ + TQLineEditPrivate( TQLineEdit *q ) + : q(q), cursor(0), cursorTimer(0), tripleClickTimer(0), frame(1), + cursorVisible(0), separator(0), readOnly(0), modified(0), + direction(TQChar::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 TQString&); + + TQLineEdit *q; + TQString text; + int cursor; + int cursorTimer; + TQPoint 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; + TQChar passwordChar; // obsolete + + void finishChange( int validateFromState = -1, bool setModified = TRUE ); + + const TQValidator* validator; + struct MaskInputData { + enum Casemode { NoCaseMode, Upper, Lower }; + TQChar maskChar; // either the separator char or the inputmask + bool separator; + Casemode caseMode; + }; + TQString inputMask; + TQChar 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, TQChar c ) + :type(type),c(c),pos(pos){} + uint type : 4; + TQChar c; + int pos; + }; + int undoState; + TQValueVector<Command> history; + void addCommand( const Command& cmd ); + void insert( const TQString& 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==TQChar::DirON?text.isRightToLeft():(direction==TQChar::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, TQTextItem::OnCharacters ); return pos >= selstart && pos < selend; } + + // masking + void parseInputMask( const TQString &maskFields ); + bool isValidInput( TQChar key, TQChar mask ) const; + TQString maskString( uint pos, const TQString &str, bool clear = FALSE ) const; + TQString clearString( uint pos, uint len ) const; + TQString stripString( const TQString &str ) const; + int findInMask( int pos, bool forward, bool findSeparator, TQChar searchChar = TQChar() ) 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 + TQTextLayout textLayout; + void updateTextLayout(); + void moveCursor( int pos, bool mark = FALSE ); + void setText( const TQString& txt ); + int xToPosInternal( int x, TQTextItem::CursorPosition ) const; + int xToPos( int x, TQTextItem::CursorPosition = TQTextItem::BetweenCharacters ) const; + inline int visualAlignment() const { return alignment ? alignment : int( isRightToLeft() ? AlignRight : AlignLeft ); } + TQRect cursorRect() const; + void updateMicroFocusHint(); + +#ifndef QT_NO_DRAGANDDROP + // drag and drop + TQPoint dndPos; + int dndTimer; + bool drag(); +#endif +}; + + +/*! + \class TQLineEdit + \brief The TQLineEdit 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 TQTextEdit 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, TQLineEdits 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 TQTextEdit TQLabel TQComboBox + \link guibooks.html#fowler GUI Design Handbook: Field, Entry\endlink +*/ + + +/*! + \fn void TQLineEdit::textChanged( const TQString& ) + + This signal is emitted whenever the text changes. The argument is + the new text. +*/ + +/*! + \fn void TQLineEdit::selectionChanged() + + This signal is emitted whenever the selection changes. + + \sa hasSelectedText(), selectedText() +*/ + +/*! + \fn void TQLineEdit::lostFocus() + + This signal is emitted when the line edit has lost focus. + + \sa hasFocus(), TQWidget::focusInEvent(), TQWidget::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 TQWidget constructor. + + \sa setText(), setMaxLength() +*/ + +TQLineEdit::TQLineEdit( TQWidget* parent, const char* name ) + : TQFrame( parent, name, WNoAutoErase ), d(new TQLineEditPrivate( this )) +{ + d->init( TQString::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 TQWidget + constructor. + + \sa text(), setMaxLength() +*/ + +TQLineEdit::TQLineEdit( const TQString& contents, TQWidget* parent, const char* name ) + : TQFrame( parent, name, WNoAutoErase ), d(new TQLineEditPrivate( 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 TQWidget + constructor. + + \sa setMask() text() +*/ +TQLineEdit::TQLineEdit( const TQString& contents, const TQString &inputMask, TQWidget* parent, const char* name ) + : TQFrame( parent, name, WNoAutoErase ), d(new TQLineEditPrivate( this )) +{ + d->parseInputMask( inputMask ); + if ( d->maskData ) { + TQString 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. +*/ + +TQLineEdit::~TQLineEdit() +{ + delete [] d->maskData; + delete d; +} + + +/*! + \property TQLineEdit::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() +*/ +TQString TQLineEdit::text() const +{ + TQString res = d->text; + if ( d->maskData ) + res = d->stripString( d->text ); + return ( res.isNull() ? TQString::fromLatin1("") : res ); +} + +void TQLineEdit::setText( const TQString& text) +{ + resetInputContext(); + d->setText( text ); + d->modified = FALSE; + d->finishChange( -1, FALSE ); +} + + +/*! + \property TQLineEdit::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 +*/ + +TQString TQLineEdit::displayText() const +{ + if ( d->echoMode == NoEcho ) + return TQString::fromLatin1(""); + TQString res = d->text; + if ( d->echoMode == Password ) + res.fill( passwordChar() ); + return ( res.isNull() ? TQString::fromLatin1("") : res ); +} + + +/*! + \property TQLineEdit::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 TQLineEdit::maxLength() const +{ + return d->maxLength; +} + +void TQLineEdit::setMaxLength( int maxLength ) +{ + if ( d->maskData ) + return; + d->maxLength = maxLength; + setText( d->text ); +} + + + +/*! + \property TQLineEdit::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 TQLineEdit::frame() const +{ + return frameShape() != NoFrame; +} + + +void TQLineEdit::setFrame( bool enable ) +{ + setFrameStyle( enable ? ( LineEditPanel | Sunken ) : NoFrame ); +} + + +/*! + \enum TQLineEdit::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 TQLineEdit::echoMode + \brief the line edit's echo mode + + The initial setting is \c Normal, but TQLineEdit 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() +*/ + +TQLineEdit::EchoMode TQLineEdit::echoMode() const +{ + return (EchoMode) d->echoMode; +} + +void TQLineEdit::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 TQValidator * TQLineEdit::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() TQIntValidator TQDoubleValidator TQRegExpValidator +*/ + +void TQLineEdit::setValidator( const TQValidator *v ) +{ + if ( d->validator ) + disconnect( (TQObject*)d->validator, SIGNAL( destroyed() ), + this, SLOT( clearValidator() ) ); + d->validator = v; + if ( d->validator ) + connect( (TQObject*)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. +*/ + +TQSize TQLineEdit::sizeHint() const +{ + constPolish(); + TQFontMetrics fm( font() ); + int h = TQMAX(fm.lineSpacing(), 14) + 2*innerMargin; + int w = fm.width( 'x' ) * 17; // "some" + int m = frameWidth() * 2; + return (style().sizeFromContents(TQStyle::CT_LineEdit, this, + TQSize( w + m, h + m ). + expandedTo(TQApplication::globalStrut()))); +} + + +/*! + Returns a minimum size for the line edit. + + The width returned is enough for at least one character. +*/ + +TQSize TQLineEdit::minimumSizeHint() const +{ + constPolish(); + TQFontMetrics fm = fontMetrics(); + int h = fm.height() + TQMAX( 2*innerMargin, fm.leading() ); + int w = fm.maxWidth(); + int m = frameWidth() * 2; + return TQSize( w + m, h + m ); +} + + +/*! + \property TQLineEdit::cursorPosition + \brief the current cursor position for this line edit + + Setting the cursor position causes a repaint when appropriate. +*/ + +int TQLineEdit::cursorPosition() const +{ + return d->cursor; +} + + +void TQLineEdit::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 TQLineEdit::validateAndSet( const TQString &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 = TQMIN( newMarkAnchor, newMarkDrag ); + d->selend = TQMAX( newMarkAnchor, newMarkDrag ); + d->updateMicroFocusHint(); + update(); + return TRUE; + } + return FALSE; +} + + +/*! + \property TQLineEdit::alignment + \brief the alignment of the line edit + + Possible Values are \c TQt::AlignAuto, \c TQt::AlignLeft, \c + TQt::AlignRight and \c TQt::AlignHCenter. + + Attempting to set the alignment to an illegal flag combination + does nothing. + + \sa TQt::AlignmentFlags +*/ + +int TQLineEdit::alignment() const +{ + return d->alignment; +} + +void TQLineEdit::setAlignment( int flag ) +{ + d->alignment = flag & 0x7; + update(); +} + + +/*! + \obsolete + \fn void TQLineEdit::cursorRight( bool, int ) + + Use cursorForward() instead. + + \sa cursorForward() +*/ + +/*! + \obsolete + \fn void TQLineEdit::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 TQLineEdit::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 TQLineEdit::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 TQLineEdit::cursorWordForward( bool mark ) +{ + d->moveCursor( d->textLayout.nextCursorPosition(d->cursor, TQTextLayout::SkipWords), mark ); +} + +/*! + Moves the cursor one word backward. If \a mark is TRUE, the word + is also selected. + + \sa cursorWordForward() +*/ + +void TQLineEdit::cursorWordBackward( bool mark ) +{ + d->moveCursor( d->textLayout.previousCursorPosition(d->cursor, TQTextLayout::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 TQLineEdit::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 TQLineEdit::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 TQLineEdit::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 TQLineEdit::end( bool mark ) +{ + d->moveCursor( d->text.length(), mark ); +} + + +/*! + \property TQLineEdit::modified + \brief whether the line edit's contents has been modified by the user + + The modified flag is never read by TQLineEdit; 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 TQLineEdit::isModified() const +{ + return d->modified; +} + +/*! + Resets the modified flag to FALSE. + + \sa isModified() +*/ +void TQLineEdit::clearModified() +{ + d->modified = FALSE; + d->history.clear(); + d->undoState = 0; +} + +/*! + \obsolete + \property TQLineEdit::edited + \brief whether the line edit has been edited. Use modified instead. +*/ +bool TQLineEdit::edited() const { return d->modified; } +void TQLineEdit::setEdited( bool on ) { d->modified = on; } + +/*! + \obsolete + \property TQLineEdit::hasMarkedText + \brief whether part of the text has been selected by the user. Use hasSelectedText instead. +*/ + +/*! + \property TQLineEdit::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 TQLineEdit::hasSelectedText() const +{ + return d->hasSelectedText(); +} + +/*! + \obsolete + \property TQLineEdit::markedText + \brief the text selected by the user. Use selectedText instead. +*/ + +/*! + \property TQLineEdit::selectedText + \brief the selected text + + If there is no selected text this property's value is + TQString::null. + + \sa hasSelectedText() +*/ + +TQString TQLineEdit::selectedText() const +{ + if ( d->hasSelectedText() ) + return d->text.mid( d->selstart, d->selend - d->selstart ); + return TQString::null; +} + +/*! + selectionStart() returns the index of the first selected character in the + line edit or -1 if no text is selected. + + \sa selectedText() +*/ + +int TQLineEdit::selectionStart() const +{ + return d->hasSelectedText() ? d->selstart : -1; +} + +/*! \obsolete use selectedText(), selectionStart() */ +bool TQLineEdit::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 TQLineEdit::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 = TQMIN( start + length, (int)d->text.length() ); + d->cursor = d->selend; + } + update(); +} + + +/*! + \property TQLineEdit::undoAvailable + \brief whether undo is available +*/ + +bool TQLineEdit::isUndoAvailable() const +{ + return d->isUndoAvailable(); +} + +/*! + \property TQLineEdit::redoAvailable + \brief whether redo is available +*/ + +bool TQLineEdit::isRedoAvailable() const +{ + return d->isRedoAvailable(); +} + +/*! + \property TQLineEdit::dragEnabled + \brief whether the lineedit starts a drag if the user presses and + moves the mouse on some selected text +*/ + +bool TQLineEdit::dragEnabled() const +{ + return d->dragEnabled; +} + +void TQLineEdit::setDragEnabled( bool b ) +{ + d->dragEnabled = b; +} + +/*! + \property TQLineEdit::acceptableInput + \brief whether the input satisfies the inputMask and the + validator. + + \sa setInputMask(), setValidator() +*/ +bool TQLineEdit::hasAcceptableInput() const +{ +#ifndef QT_NO_VALIDATOR + TQString text = d->text; + int cursor = d->cursor; + if ( d->validator && d->validator->validate( text, cursor ) != TQValidator::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 TQLineEdit::inputMask + \brief The validation input mask + + If no mask is set, inputMask() returns TQString::null. + + Sets the TQLineEdit's validation mask. Validators can be used + instead of, or in conjunction with masks; see setValidator(). + + Unset the mask and return to normal TQLineEdit 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 retquired. A-Z, a-z. + \row \i \c a \i ASCII alphabetic character permitted but not retquired. + \row \i \c N \i ASCII alphanumeric character retquired. A-Z, a-z, 0-9. + \row \i \c n \i ASCII alphanumeric character permitted but not retquired. + \row \i \c X \i Any character retquired. + \row \i \c x \i Any character permitted but not retquired. + \row \i \c 9 \i ASCII digit retquired. 0-9. + \row \i \c 0 \i ASCII digit permitted but not retquired. + \row \i \c D \i ASCII digit retquired. 1-9. + \row \i \c d \i ASCII digit permitted but not retquired (1-9). + \row \i \c # \i ASCII digit or plus/minus sign permitted but not retquired. + \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 +*/ +TQString TQLineEdit::inputMask() const +{ + return ( d->maskData ? d->inputMask + ';' + d->blank : TQString::null ); +} + +void TQLineEdit::setInputMask( const TQString &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 TQLineEdit::selectAll() +{ + d->selstart = d->selend = d->cursor = 0; + d->moveCursor( d->text.length(), TRUE ); +} + +/*! + Deselects any selected text. + + \sa setSelection() selectAll() +*/ + +void TQLineEdit::deselect() +{ + d->deselect(); + d->finishChange(); +} + + +/*! + This slot is equivalent to setValidator(0). +*/ + +void TQLineEdit::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 TQLineEdit::insert( const TQString &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 TQLineEdit::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 + TQLineEdit::undoAvailable available\endlink. Deselects any current + selection, and updates the selection start to the current cursor + position. +*/ +void TQLineEdit::undo() +{ + resetInputContext(); + d->undo(); + d->finishChange( -1, FALSE ); +} + +/*! + Redoes the last operation if redo is \link + TQLineEdit::redoAvailable available\endlink. +*/ +void TQLineEdit::redo() +{ + resetInputContext(); + d->redo(); + d->finishChange(); +} + + +/*! + \property TQLineEdit::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. + + TQLineEdit does not show a cursor in read-only mode. + + \sa setEnabled() +*/ + +bool TQLineEdit::isReadOnly() const +{ + return d->readOnly; +} + +void TQLineEdit::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 TQLineEdit::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 TQLineEdit::copy() const +{ + d->copy(); +} + +/*! + Inserts the clipboard's text at the cursor position, deleting any + selected text, providing the line edit is not \link + TQLineEdit::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 TQLineEdit::paste() +{ + insert( TQApplication::clipboard()->text( TQClipboard::Clipboard ) ); +} + +void TQLineEditPrivate::copy( bool clipboard ) const +{ + TQString t = q->selectedText(); + if ( !t.isEmpty() && echoMode == TQLineEdit::Normal ) { + q->disconnect( TQApplication::clipboard(), SIGNAL(selectionChanged()), q, 0); + TQApplication::clipboard()->setText( t, clipboard ? TQClipboard::Clipboard : TQClipboard::Selection ); + q->connect( TQApplication::clipboard(), SIGNAL(selectionChanged()), + q, SLOT(clipboardChanged()) ); + } +} + +#endif // !QT_NO_CLIPBOARD + +/*!\reimp +*/ + +void TQLineEdit::resizeEvent( TQResizeEvent *e ) +{ + TQFrame::resizeEvent( e ); +} + +/*! \reimp +*/ +bool TQLineEdit::event( TQEvent * e ) +{ + if ( e->type() == TQEvent::AccelOverride && !d->readOnly ) { + TQKeyEvent* ke = (TQKeyEvent*) 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() == TQEvent::Timer ) { + // should be timerEvent, is here for binary compatibility + int timerId = ((TQTimerEvent*)e)->timerId(); + if ( timerId == d->cursorTimer ) { + if(!hasSelectedText() || style().styleHint( TQStyle::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 TQWidget::event( e ); +} + +/*! \reimp +*/ +void TQLineEdit::mousePressEvent( TQMouseEvent* e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; + if ( e->button() == RightButton ) + return; + if ( d->tripleClickTimer && ( e->pos() - d->tripleClick ).manhattanLength() < + TQApplication::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( TQApplication::startDragTime() ); + } else +#endif + { + d->moveCursor( cursor, mark ); + } +} + +/*! \reimp +*/ +void TQLineEdit::mouseMoveEvent( TQMouseEvent * e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; +#ifndef QT_NO_CURSOR + if ( ( e->state() & MouseButtonMask ) == 0 ) { + if ( !d->readOnly && d->dragEnabled +#ifndef QT_NO_WHATSTHIS + && !TQWhatsThis::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() > TQApplication::startDragDistance() ) + d->drag(); + } else +#endif + { + d->moveCursor( d->xToPos( e->pos().x() ), TRUE ); + } + } +} + +/*! \reimp +*/ +void TQLineEdit::mouseReleaseEvent( TQMouseEvent* 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 (TQApplication::clipboard()->supportsSelection() ) { + if ( e->button() == LeftButton ) { + d->copy( FALSE ); + } else if ( !d->readOnly && e->button() == MidButton ) { + d->deselect(); + insert( TQApplication::clipboard()->text( TQClipboard::Selection ) ); + } + } +#endif +} + +/*! \reimp +*/ +void TQLineEdit::mouseDoubleClickEvent( TQMouseEvent* e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; + if ( e->button() == TQt::LeftButton ) { + deselect(); + d->cursor = d->xToPos( e->pos().x() ); + d->cursor = d->textLayout.previousCursorPosition( d->cursor, TQTextLayout::SkipWords ); + // ## text layout should support end of words. + int end = d->textLayout.nextCursorPosition( d->cursor, TQTextLayout::SkipWords ); + while ( end > d->cursor && d->text[end-1].isSpace() ) + --end; + d->moveCursor( end, TRUE ); + d->tripleClickTimer = startTimer( TQApplication::doubleClickInterval() ); + d->tripleClick = e->pos(); + } +} + +/*! + \fn void TQLineEdit::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 TQValidator::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 TQLineEdit::keyPressEvent( TQKeyEvent * e ) +{ + d->setCursorVisible( TRUE ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) { + const TQValidator * v = d->validator; + if ( hasAcceptableInput() ) { + emit returnPressed(); + } +#ifndef QT_NO_VALIDATOR + else if ( v && v->validate( d->text, d->cursor ) != TQValidator::Acceptable ) { + TQString vstr = d->text; + v->fixup( vstr ); + if ( vstr != d->text ) { + setText( vstr ); + if ( hasAcceptableInput() ) + emit returnPressed(); + } + } +#endif + e->ignore(); + return; + } + if ( !d->readOnly ) { + TQString 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() ) { + TQChar *c = (TQChar *)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) ? TQChar::DirL : TQChar::DirR; + d->updateTextLayout(); + update(); + } + + if ( unknown ) + e->ignore(); +} + + +/*! + This function is not intended as polymorphic usage. Just a shared code + fragment that calls TQWidget::sendMouseEventToInputContext() easily for this + class. + */ +bool TQLineEdit::sendMouseEventToInputContext( TQMouseEvent *e ) +{ +#ifndef QT_NO_IM + if ( d->composeMode() ) { + int cursor = d->xToPosInternal( e->pos().x(), TQTextItem::OnCharacters ); + int mousePos = cursor - d->imstart; + if ( mousePos >= 0 && mousePos < d->preeditLength() ) { + TQWidget::sendMouseEventToInputContext( mousePos, e->type(), + e->button(), e->state() ); + } else if ( e->type() != TQEvent::MouseMove ) { + // send button events on out of preedit + TQWidget::sendMouseEventToInputContext( -1, e->type(), + e->button(), e->state() ); + } + return TRUE; + } +#endif + return FALSE; +} + + +/*! \reimp + */ +void TQLineEdit::imStartEvent( TQIMEvent *e ) +{ + if ( d->readOnly ) { + e->ignore(); + return; + } + d->removeSelectedText(); + d->updateMicroFocusHint(); + d->imstart = d->imend = d->imselstart = d->imselend = d->cursor; +} + +/*! \reimp + */ +void TQLineEdit::imComposeEvent( TQIMEvent *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 TQLineEdit::imEndEvent( TQIMEvent *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 TQLineEdit::focusInEvent( TQFocusEvent* ) +{ + if ( TQFocusEvent::reason() == TQFocusEvent::Tab || + TQFocusEvent::reason() == TQFocusEvent::Backtab || + TQFocusEvent::reason() == TQFocusEvent::Shortcut ) + d->maskData ? d->moveCursor( d->nextMaskBlank( 0 ) ) : selectAll(); + if ( !d->cursorTimer ) { + int cft = TQApplication::cursorFlashTime(); + d->cursorTimer = cft ? startTimer( cft/2 ) : -1; + } + if( !hasSelectedText() || style().styleHint( TQStyle::SH_BlinkCursorWhenTextSelected ) ) + d->setCursorVisible( TRUE ); + if ( d->hasIMSelection() ) + d->cursor = d->imselstart; + d->updateMicroFocusHint(); +} + +/*!\reimp +*/ + +void TQLineEdit::focusOutEvent( TQFocusEvent* ) +{ + if ( TQFocusEvent::reason() != TQFocusEvent::ActiveWindow && + TQFocusEvent::reason() != TQFocusEvent::Popup ) + deselect(); + d->setCursorVisible( FALSE ); + if ( d->cursorTimer > 0 ) + killTimer( d->cursorTimer ); + d->cursorTimer = 0; + if (TQFocusEvent::reason() != TQFocusEvent::Popup) + emit lostFocus(); +} + +/*!\reimp +*/ +void TQLineEdit::drawContents( TQPainter *p ) +{ + const TQColorGroup& cg = colorGroup(); + TQRect cr = contentsRect(); + TQFontMetrics fm = fontMetrics(); + TQRect lineRect( cr.x() + innerMargin, cr.y() + (cr.height() - fm.height() + 1) / 2, + cr.width() - 2*innerMargin, fm.height() ); + TQBrush bg = TQBrush( paletteBackgroundColor() ); + if ( paletteBackgroundPixmap() ) + bg = TQBrush( cg.background(), *paletteBackgroundPixmap() ); + else if ( !isEnabled() ) + bg = cg.brush( TQColorGroup::Background ); + TQPoint brushOrigin = p->brushOrigin(); + p->save(); + p->setClipRegion( TQRegion(cr) - lineRect ); + p->setBrushOrigin(brushOrigin - backgroundOffset()); + p->fillRect( cr, bg ); + p->restore(); + TQSharedDoubleBuffer buffer( p, lineRect.x(), lineRect.y(), + lineRect.width(), lineRect.height(), + hasFocus() ? TQSharedDoubleBuffer::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; + TQTextItem 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 = TQMAX( 0, -fm.minLeftBearing() ); + int minRB = TQMAX( 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() retquires 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. + TQPoint topLeft = lineRect.topLeft() - TQPoint(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 |= TQt::Underline; + if ( font().strikeOut() ) + textflags |= TQt::StrikeOut; + if ( font().overline() ) + textflags |= TQt::Overline; + + for ( int i = 0; i < d->textLayout.numItems(); i++ ) { + TQTextItem 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 ) ) { + TQRect highlight = TQRect( TQPoint( tix + ti.cursorToX( TQMAX( d->selstart - first, 0 ) ), + lineRect.top() ), + TQPoint( tix + ti.cursorToX( TQMIN( d->selend - first, last - first + 1 ) ) - 1, + lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRegion( TQRegion( lineRect ) - highlight, TQPainter::CoordPainter ); + p->drawTextItem( topLeft, ti, textflags ); + p->setClipRect( lineRect & highlight, TQPainter::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 ) ) { + TQRect highlight = TQRect( TQPoint( tix + ti.cursorToX( TQMAX( d->imstart - first, 0 ) ), lineRect.top() ), + TQPoint( tix + ti.cursorToX( TQMIN( d->imend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRect( lineRect & highlight, TQPainter::CoordPainter ); + + int h1, s1, v1, h2, s2, v2; + cg.color( TQColorGroup::Base ).hsv( &h1, &s1, &v1 ); + cg.color( TQColorGroup::Background ).hsv( &h2, &s2, &v2 ); + TQColor 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 ) ) { + TQRect highlight = TQRect( TQPoint( tix + ti.cursorToX( TQMAX( d->imselstart - first, 0 ) ), lineRect.top() ), + TQPoint( tix + ti.cursorToX( TQMIN( d->imselend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRect( lineRect & highlight, TQPainter::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 ) ) { + TQRect highlight = TQRect( TQPoint( tix + ti.cursorToX( TQMAX( d->cursor - first, 0 ) ), lineRect.top() ), + TQPoint( tix + ti.cursorToX( TQMIN( d->cursor + 1 - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRect( lineRect & highlight, TQPainter::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() ) { + TQPoint from( topLeft.x() + cix, lineRect.top() ); + TQPoint to = from + TQPoint( 0, lineRect.height() ); + p->drawLine( from, to ); + if ( hasRightToLeft ) { + bool rtl = ci.isValid() ? ci.isRightToLeft() : TRUE; + to = from + TQPoint( (rtl ? -2 : 2), 2 ); + p->drawLine( from, to ); + from.ry() += 4; + p->drawLine( from, to ); + } + } + buffer.end(); +} + + +#ifndef QT_NO_DRAGANDDROP +/*!\reimp +*/ +void TQLineEdit::dragMoveEvent( TQDragMoveEvent *e ) +{ + if ( !d->readOnly && TQTextDrag::canDecode(e) ) { + e->acceptAction(); + d->cursor = d->xToPos( e->pos().x() ); + d->cursorVisible = TRUE; + update(); + } +} + +/*!\reimp */ +void TQLineEdit::dragEnterEvent( TQDragEnterEvent * e ) +{ + TQLineEdit::dragMoveEvent( e ); +} + +/*!\reimp */ +void TQLineEdit::dragLeaveEvent( TQDragLeaveEvent *) +{ + if ( d->cursorVisible ) { + d->cursorVisible = FALSE; + update(); + } +} + +/*!\reimp */ +void TQLineEdit::dropEvent( TQDropEvent* e ) +{ + TQString str; + // try text/plain + TQCString plain = "plain"; + bool decoded = TQTextDrag::decode(e, str, plain); + // otherwise we'll accept any kind of text (like text/uri-list) + if (! decoded) + decoded = TQTextDrag::decode(e, str); + + if ( decoded && !d->readOnly ) { + if ( e->source() == this && e->action() == TQDropEvent::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() == TQDropEvent::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 TQLineEditPrivate::drag() +{ + q->killTimer( dndTimer ); + dndTimer = 0; + TQTextDrag *tdo = new TQTextDrag( q->selectedText(), q ); + + TQGuardedPtr<TQLineEdit> gptr = q; + bool r = tdo->drag(); + if ( !gptr ) + return FALSE; + + // ### fix the check TQDragObject::target() != q in TQt4 (should not be needed) + if ( r && !readOnly && TQDragObject::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 TQLineEdit::contextMenuEvent( TQContextMenuEvent * e ) +{ +#ifndef QT_NO_POPUPMENU +#ifndef QT_NO_IM + if ( d->composeMode() ) + return; +#endif + d->separate(); + TQPopupMenu *menu = createPopupMenu(); + if (!menu) + return; + TQGuardedPtr<TQPopupMenu> popup = menu; + TQGuardedPtr<TQLineEdit> that = this; + TQPoint pos = e->reason() == TQContextMenuEvent::Mouse ? e->globalPos() : + mapToGlobal( TQPoint(e->pos().x(), 0) ) + TQPoint( width() / 2, height() / 2 ); + int r = popup->exec( pos ); + delete (TQPopupMenu*)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. +*/ + +TQPopupMenu *TQLineEdit::createPopupMenu() +{ +#ifndef QT_NO_POPUPMENU + TQPopupMenu *popup = new TQPopupMenu( 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 + TQInputContext *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 && !TQApplication::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 TQLineEdit::windowActivationChange( bool b ) +{ + //### remove me with WHighlightSelection attribute + if ( palette().active() != palette().inactive() ) + update(); + TQWidget::windowActivationChange( b ); +} + +/*! \reimp */ + +void TQLineEdit::setPalette( const TQPalette & p ) +{ + //### remove me with WHighlightSelection attribute + TQWidget::setPalette( p ); + update(); +} + +/*! + \obsolete + \fn void TQLineEdit::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 TQLineEdit::setFont( const TQFont & f ) +{ + TQWidget::setFont( f ); + d->updateTextLayout(); +} + +/*! \obsolete +*/ +int TQLineEdit::characterAt( int xpos, TQChar *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 TQLineEdit::setPasswordChar( TQChar c ) +{ + d->passwordChar = c; +} + +/*! + \internal + + Returns the password character. + + \sa setPasswordChar() +*/ +TQChar TQLineEdit::passwordChar() const +{ + return ( d->passwordChar.isNull() ? TQChar( style().styleHint( TQStyle::SH_LineEdit_PasswordCharacter, this ) ) : d->passwordChar ); +} + +void TQLineEdit::clipboardChanged() +{ +} + +void TQLineEditPrivate::init( const TQString& txt ) +{ +#ifndef QT_NO_CURSOR + q->setCursor( readOnly ? arrowCursor : ibeamCursor ); +#endif + q->setFocusPolicy( TQWidget::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( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed ) ); + q->setBackgroundMode( PaletteBase ); + q->setKeyCompression( TRUE ); + q->setMouseTracking( TRUE ); + q->setAcceptDrops( TRUE ); + q->setFrame( TRUE ); + text = txt; + updateTextLayout(); + cursor = text.length(); +} + +void TQLineEditPrivate::updateTextLayout() +{ + // replace all non-printable characters with spaces (to avoid + // drawing boxes when using fonts that don't have glyphs for such + // characters) + const TQString &displayText = q->displayText(); + TQString str(displayText.unicode(), displayText.length()); + TQChar* uc = (TQChar*)str.unicode(); + for (int i = 0; i < (int)str.length(); ++i) { + if (! uc[i].isPrint()) + uc[i] = TQChar(0x0020); + } + textLayout.setText( str, q->font() ); + textLayout.setDirection((TQChar::Direction)direction); + textLayout.beginLayout(TQTextLayout::SingleLine); + textLayout.beginLine( INT_MAX ); + while ( !textLayout.atEnd() ) + textLayout.addCurrentItem(); + ascent = 0; + textLayout.endLine(0, 0, TQt::AlignLeft|TQt::SingleLine, &ascent); +} + +int TQLineEditPrivate::xToPosInternal( int x, TQTextItem::CursorPosition betweenOrOn ) const +{ + x-= q->contentsRect().x() - hscroll + innerMargin; + for ( int i = 0; i < textLayout.numItems(); ++i ) { + TQTextItem ti = textLayout.itemAt( i ); + TQRect 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 TQLineEditPrivate::xToPos( int x, TQTextItem::CursorPosition betweenOrOn ) const +{ + int pos = xToPosInternal( x, betweenOrOn ); + return ( pos < 0 ) ? 0 : pos; +} + + +TQRect TQLineEditPrivate::cursorRect() const +{ + TQRect cr = q->contentsRect(); + int cix = cr.x() - hscroll + innerMargin; + TQTextItem 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 TQRect( cix-4, cr.y() + ( cr.height() - ch + 1) / 2, 8, ch + 1 ); +} + +void TQLineEditPrivate::updateMicroFocusHint() +{ + // To reduce redundant microfocus update notification, we remember + // the old rect and update the microfocus if actual update is + // retquired. The rect o is intentionally static because some + // notifyee retquires the microfocus information as global update + // rather than per notifyee update to place shared widget around + // microfocus. + static TQRect o; + if ( q->hasFocus() ) { + TQRect r = cursorRect(); + if ( o != r ) { + o = r; + q->setMicroFocusHint( r.x(), r.y(), r.width(), r.height() ); + } + } +} + +void TQLineEditPrivate::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 = TQMIN( anchor, pos ); + selend = TQMAX( anchor, pos ); + } else { + deselect(); + } + if ( fullUpdate ) { + cursor = pos; + q->update(); + } else { + setCursorVisible( FALSE ); + cursor = pos; + setCursorVisible( TRUE ); + } + updateMicroFocusHint(); + if ( mark && !q->style().styleHint( TQStyle::SH_BlinkCursorWhenTextSelected ) ) + setCursorVisible( FALSE ); + if ( mark || selDirty ) { + selDirty = FALSE; + emit q->selectionChanged(); + } +} + +void TQLineEditPrivate::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 ) { + TQString textCopy = text; + int cursorCopy = cursor; + validInput = ( validator->validate( textCopy, cursorCopy ) != TQValidator::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) + TQAccessible::updateAccessibility( q, 0, TQAccessible::ValueChanged ); +#endif + } + if ( selDirty ) { + selDirty = FALSE; + emit q->selectionChanged(); + } + if ( lineDirty || !setModified ) + q->update(); +} + +void TQLineEditPrivate::setText( const TQString& txt ) +{ + deselect(); + TQString 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 TQLineEditPrivate::setCursorVisible( bool visible ) +{ + if ( (bool)cursorVisible == visible ) + return; + if ( cursorTimer ) + cursorVisible = visible; + TQRect r = cursorRect(); + if ( maskData || !q->contentsRect().contains( r ) ) + q->update(); + else + q->update( r ); +} + +void TQLineEditPrivate::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 TQLineEditPrivate::insert( const TQString& s ) +{ + if ( maskData ) { + TQString 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 TQLineEditPrivate::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 TQLineEditPrivate::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 -= TQMIN( cursor, selend ) - selstart; + deselect(); + textDirty = TRUE; + } +} + +void TQLineEditPrivate::parseInputMask( const TQString &maskFields ) +{ + if ( maskFields.isEmpty() || maskFields.section( ';', 0, 0 ).isEmpty() ) { + if ( maskData ) { + delete [] maskData; + maskData = 0; + maxLength = 32767; + q->setText( TQString::null ); + } + return; + } + + inputMask = maskFields.section( ';', 0, 0 ); + blank = maskFields.section( ';', 1, 1 ).at(0); + if ( blank.isNull() ) + blank = ' '; + + // calculate maxLength / maskData length + maxLength = 0; + TQChar 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( TQString::null ); +} + + +/* checks if the key is valid compared to the inputMask */ +bool TQLineEditPrivate::isValidInput( TQChar key, TQChar 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. +*/ +TQString TQLineEditPrivate::maskString( uint pos, const TQString &str, bool clear) const +{ + if ( pos >= (uint)maxLength ) + return TQString::fromLatin1(""); + + TQString fill; + fill = clear ? clearString( 0, maxLength ) : text; + + uint strIndex = 0; + TQString s = TQString::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. +*/ +TQString TQLineEditPrivate::clearString( uint pos, uint len ) const +{ + if ( pos >= (uint)maxLength ) + return TQString::null; + + TQString s; + int end = TQMIN( (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 TQLineEdit when an inputMask is set, + separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1". +*/ +TQString TQLineEditPrivate::stripString( const TQString &str ) const +{ + if ( !maskData ) + return str; + + TQString s; + int end = TQMIN( 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 TQLineEditPrivate::findInMask( int pos, bool forward, bool findSeparator, TQChar 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 000000000..808c087b3 --- /dev/null +++ b/src/widgets/qlineedit.h @@ -0,0 +1,232 @@ +/********************************************************************** +** +** Definition of TQLineEdit widget class +** +** Created : 941011 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQLINEEDIT_H +#define TQLINEEDIT_H + +struct TQLineEditPrivate; + +class TQValidator; +class TQPopupMenu; + +#ifndef QT_H +#include "qframe.h" +#include "qstring.h" +#endif // QT_H + +#ifndef QT_NO_LINEEDIT + +class TQTextParagraph; +class TQTextCursor; + +class Q_EXPORT TQLineEdit : public TQFrame +{ + Q_OBJECT + Q_ENUMS( EchoMode ) + Q_PROPERTY( TQString 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( TQString 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( TQString markedText READ markedText DESIGNABLE false ) + Q_PROPERTY( TQString 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( TQString inputMask READ inputMask WRITE setInputMask ) + Q_PROPERTY( bool acceptableInput READ hasAcceptableInput ) + +public: + TQLineEdit( TQWidget* parent, const char* name=0 ); + TQLineEdit( const TQString &, TQWidget* parent, const char* name=0 ); + TQLineEdit( const TQString &, const TQString &, TQWidget* parent, const char* name=0 ); + ~TQLineEdit(); + + TQString text() const; + + TQString displayText() const; + + int maxLength() const; + + bool frame() const; + + enum EchoMode { Normal, NoEcho, Password }; + EchoMode echoMode() const; + + bool isReadOnly() const; + + const TQValidator * validator() const; + + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + + int cursorPosition() const; + bool validateAndSet( const TQString &, 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; + TQString selectedText() const; + int selectionStart() const; + + bool isUndoAvailable() const; + bool isRedoAvailable() const; + +#ifndef QT_NO_COMPAT + bool hasMarkedText() const { return hasSelectedText(); } + TQString markedText() const { return selectedText(); } +#endif + + bool dragEnabled() const; + + TQString inputMask() const; + void setInputMask( const TQString &inputMask ); + bool hasAcceptableInput() const; + +public slots: + virtual void setText( const TQString &); + virtual void selectAll(); + virtual void deselect(); + virtual void clearValidator(); + virtual void insert( const TQString &); + 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 TQValidator * ); + virtual void setFont( const TQFont & ); + virtual void setPalette( const TQPalette & ); + 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 TQString &); + void returnPressed(); + void lostFocus(); + void selectionChanged(); + +protected: + bool event( TQEvent * ); + void mousePressEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseDoubleClickEvent( TQMouseEvent * ); + void keyPressEvent( TQKeyEvent * ); + void imStartEvent( TQIMEvent * ); + void imComposeEvent( TQIMEvent * ); + void imEndEvent( TQIMEvent * ); + void focusInEvent( TQFocusEvent * ); + void focusOutEvent( TQFocusEvent * ); + void resizeEvent( TQResizeEvent * ); + void drawContents( TQPainter * ); +#ifndef QT_NO_DRAGANDDROP + void dragEnterEvent( TQDragEnterEvent * ); + void dragMoveEvent( TQDragMoveEvent *e ); + void dragLeaveEvent( TQDragLeaveEvent *e ); + void dropEvent( TQDropEvent * ); +#endif + void contextMenuEvent( TQContextMenuEvent * ); + bool sendMouseEventToInputContext( TQMouseEvent *e ); + virtual TQPopupMenu *createPopupMenu(); + void windowActivationChange( bool ); +#ifndef QT_NO_COMPAT + void repaintArea( int, int ) { update(); } +#endif + +private slots: + void clipboardChanged(); + +public: + void setPasswordChar( TQChar c ); // internal obsolete + TQChar passwordChar() const; // obsolete internal + int characterAt( int, TQChar* ) const; // obsolete + bool getSelection( int *, int * ); // obsolete + +private: + friend struct TQLineEditPrivate; + TQLineEditPrivate * d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQLineEdit( const TQLineEdit & ); + TQLineEdit &operator=( const TQLineEdit & ); +#endif +}; + + +#endif // QT_NO_LINEEDIT + +#endif // TQLINEEDIT_H diff --git a/src/widgets/qlistbox.cpp b/src/widgets/qlistbox.cpp new file mode 100644 index 000000000..d767db495 --- /dev/null +++ b/src/widgets/qlistbox.cpp @@ -0,0 +1,4693 @@ +/********************************************************************** +** +** Implementation of TQListBox widget class +** +** Created : 941121 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQListBoxPrivate +{ +public: + TQListBoxPrivate( TQListBox *lb ): + head( 0 ), last( 0 ), cache( 0 ), cacheIndex( -1 ), current( 0 ), + highlighted( 0 ), tmpCurrent( 0 ), columnPos( 1 ), rowPos( 1 ), rowPosCache( 0 ), + columnPosOne( 0 ), rowMode( TQListBox::FixedNumber ), + columnMode( TQListBox::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( TQListBox::Single ), + count( 0 ), + listBox( lb ), currInputString( TQString::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 TQString &text ); + ~TQListBoxPrivate(); + + TQListBoxItem * head, *last, *cache; + int cacheIndex; + TQListBoxItem * current, *highlighted, *tmpCurrent; + + TQMemArray<int> columnPos; + TQMemArray<int> rowPos; + int rowPosCache; + int columnPosOne; + + TQListBox::LayoutMode rowMode; + TQListBox::LayoutMode columnMode; + int numRows; + int numColumns; + + int currentRow; + int currentColumn; + int mousePressRow; + int mousePressColumn; + int mouseMoveRow; + int mouseMoveColumn; + bool mouseInternalPress; + + TQTimer * scrollTimer; + TQTimer * updateTimer; + TQTimer * visibleTimer; + TQTimer * resizeTimer; + + TQPoint scrollPos; + + TQListBox::SelectionMode selectionMode; + + int count; + + + TQListBox *listBox; + TQString currInputString; + TQTimer *inputTimer; + + TQListBoxItem *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; + + TQRect *rubber; + TQPtrDict<bool> selectable; + + struct SortableItem { + TQListBoxItem *item; + }; +}; + + +TQListBoxPrivate::~TQListBoxPrivate() +{ + Q_ASSERT( !head ); +} + + +/*! + \class TQListBoxItem qlistbox.h + \brief The TQListBoxItem 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 TQListBox you must + inherit this class and reimplement paint(), height() and width(). + + \sa TQListBox +*/ + +/*! + \fn bool TQListBox::dragSelect() const + \internal +*/ + +/*! + \fn void TQListBox::setDragSelect( bool ) + \internal +*/ + +/*! + \fn bool TQListBox::autoScroll() const + \internal +*/ + +/*! + \fn void TQListBox::setAutoScroll( bool ) + \internal +*/ + +/*! + \fn bool TQListBox::autoScrollBar() const + + \obsolete + + Returns TRUE if vScrollBarMode() is \c Auto; otherwise returns + FALSE. +*/ + +/*! + \fn void TQListBox::setAutoScrollBar( bool enable ) + + \obsolete + + If \a enable is TRUE sets setVScrollBarMode() to \c AlwaysOn; + otherwise sets setVScrollBarMode() to \c AlwaysOff. +*/ + +/*! + \fn bool TQListBox::scrollBar() const + + \obsolete + + Returns FALSE if vScrollBarMode() is \c AlwaysOff; otherwise + returns TRUE. +*/ + +/*! + \fn void TQListBox::setScrollBar( bool enable ) + + \obsolete + + If \a enable is TRUE sets setVScrollBarMode() to \c AlwaysOn; + otherwise sets setVScrollBarMode() to \c AlwaysOff. +*/ + +/*! + \fn bool TQListBox::autoBottomScrollBar() const + + \obsolete + + Returns TRUE if hScrollBarMode() is \c Auto; otherwise returns + FALSE. +*/ + +/*! + \fn void TQListBox::setAutoBottomScrollBar( bool enable ) + + \obsolete + + If \a enable is TRUE sets setHScrollBarMode() to \c AlwaysOn; + otherwise sets setHScrollBarMode() to \c AlwaysOff. +*/ + +/*! + \fn bool TQListBox::bottomScrollBar() const + + \obsolete + + Returns FALSE if vScrollBarMode() is \c AlwaysOff; otherwise + returns TRUE. +*/ + +/*! + \fn void TQListBox::setBottomScrollBar( bool enable ) + + \obsolete + + If \a enable is TRUE sets setHScrollBarMode() to \c AlwaysOn; + otherwise sets setHScrollBarMode() to \c AlwaysOff. +*/ + +/*! + \fn bool TQListBox::smoothScrolling() const + \internal +*/ + +/*! + \fn void TQListBox::setSmoothScrolling( bool ) + \internal +*/ + +/*! + \fn bool TQListBox::autoUpdate() const + \internal +*/ + +/*! + \fn void TQListBox::setAutoUpdate( bool ) + \internal +*/ + +/*! + \fn void TQListBox::setFixedVisibleLines( int lines ) + \internal +*/ + +/*! + \obsolete + \fn int TQListBox::cellHeight( int i ) const + Returns the item height of item \a i. + \sa itemHeight() +*/ + +/*! + \obsolete + \overload int TQListBox::cellHeight() const + Returns the item height of the first item, item 0. + \sa itemHeight() +*/ + +/*! + \obsolete + \fn int TQListBox::cellWidth() const + Returns the maximum item width. + \sa maxItemWidth() +*/ + +/*! + \overload int TQListBox::cellWidth(int i) const + \internal +*/ + +/*! + \obsolete + \fn int TQListBox::numCols() const + Returns the number of columns. + \sa numColumns() +*/ + +/*! + \fn void TQListBox::updateCellWidth() + \internal +*/ + +/*! + \obsolete + \fn int TQListBox::totalWidth() const + Returns contentsWidth(). +*/ + +/*! + \obsolete + \fn int TQListBox::totalHeight() const + Returns contentsHeight(). +*/ + +/*! + \obsolete + \fn int TQListBox::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. +*/ + +TQListBoxItem::TQListBoxItem( TQListBox* 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. +*/ + +TQListBoxItem::TQListBoxItem( TQListBox* listbox, TQListBoxItem *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. +*/ + +TQListBoxItem::~TQListBoxItem() +{ + 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 TQListBoxItem::setCustomHighlighting( bool b ) +{ + custom_highlight = b; +} + +/*! + \fn void TQListBoxItem::paint( TQPainter *p ) + + Implement this function to draw your item. The painter, \a p, is + already open for painting. + + \sa height(), width() +*/ + +/*! + \fn int TQListBoxItem::width( const TQListBox* 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{TQApplication::globalStrut()}'s width. + + \sa paint(), height() +*/ +int TQListBoxItem::width(const TQListBox*) const +{ + return TQApplication::globalStrut().width(); +} + +/*! + \fn int TQListBoxItem::height( const TQListBox* 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{TQApplication::globalStrut()}'s height. + + \sa paint(), width() +*/ +int TQListBoxItem::height(const TQListBox*) const +{ + return TQApplication::globalStrut().height(); +} + + +/*! + Returns the text of the item. This text is also used for sorting. + + \sa setText() +*/ +TQString TQListBoxItem::text() const +{ + return txt; +} + +/*! + Returns the pixmap associated with the item, or 0 if there isn't + one. + + The default implementation returns 0. +*/ +const TQPixmap *TQListBoxItem::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 TQListBoxItem::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 TQListBoxItem::isSelectable() const +{ + if ( !listBox() ) + return TRUE; + bool *sel = listBox()->d->selectable.find( ( (TQListBoxItem*)this ) ); + if ( !sel ) + return TRUE; + else + return *sel; +} + +/*! + \fn void TQListBoxItem::setText( const TQString &text ) + + Sets the text of the TQListBoxItem to \a text. This \a text is also + used for sorting. The text is not shown unless explicitly drawn in + paint(). + + \sa text() +*/ + + +/*! + \class TQListBoxText qlistbox.h + \brief The TQListBoxText 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 + TQListBoxItem. + + \sa TQListBox, TQListBoxItem +*/ + + +/*! + Constructs a list box item in list box \a listbox showing the text + \a text. +*/ +TQListBoxText::TQListBoxText( TQListBox *listbox, const TQString &text ) + :TQListBoxItem( listbox ) +{ + setText( text ); +} + +/*! + Constructs a list box item showing the text \a text. +*/ + +TQListBoxText::TQListBoxText( const TQString &text ) + :TQListBoxItem() +{ + 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. +*/ + +TQListBoxText::TQListBoxText( TQListBox* listbox, const TQString &text, TQListBoxItem *after ) + : TQListBoxItem( listbox, after ) +{ + setText( text ); +} + +/*! + Destroys the item. +*/ + +TQListBoxText::~TQListBoxText() +{ +} + +/*! + Draws the text using \a painter. +*/ + +void TQListBoxText::paint( TQPainter *painter ) +{ + int itemHeight = height( listBox() ); + TQFontMetrics 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 TQListBoxText::height( const TQListBox* lb ) const +{ + int h = lb ? lb->fontMetrics().lineSpacing() + 2 : 0; + return TQMAX( h, TQApplication::globalStrut().height() ); +} + +/*! + Returns the width of this line in list box \a lb. + + \sa paint(), height() +*/ + +int TQListBoxText::width( const TQListBox* lb ) const +{ + int w = lb ? lb->fontMetrics().width( text() ) + 6 : 0; + return TQMAX( w, TQApplication::globalStrut().width() ); +} + +int TQListBoxText::RTTI = 1; + +/*! + \fn int TQListBoxText::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 TQListBoxText::rtti() const +{ + return RTTI; +} + +/*! + \class TQListBoxPixmap qlistbox.h + \brief The TQListBoxPixmap 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 TQListBox, TQListBoxItem +*/ + + +/*! + Constructs a new list box item in list box \a listbox showing the + pixmap \a pixmap. +*/ + +TQListBoxPixmap::TQListBoxPixmap( TQListBox* listbox, const TQPixmap &pixmap ) + : TQListBoxItem( listbox ) +{ + pm = pixmap; +} + +/*! + Constructs a new list box item showing the pixmap \a pixmap. +*/ + +TQListBoxPixmap::TQListBoxPixmap( const TQPixmap &pixmap ) + : TQListBoxItem() +{ + 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. +*/ + +TQListBoxPixmap::TQListBoxPixmap( TQListBox* listbox, const TQPixmap &pixmap, TQListBoxItem *after ) + : TQListBoxItem( listbox, after ) +{ + pm = pixmap; +} + + +/*! + Destroys the item. +*/ + +TQListBoxPixmap::~TQListBoxPixmap() +{ +} + + +/*! + Constructs a new list box item in list box \a listbox showing the + pixmap \a pix and the text \a text. +*/ +TQListBoxPixmap::TQListBoxPixmap( TQListBox* listbox, const TQPixmap &pix, const TQString& text) + : TQListBoxItem( listbox ) +{ + pm = pix; + setText( text ); +} + +/*! + Constructs a new list box item showing the pixmap \a pix and the + text to \a text. +*/ +TQListBoxPixmap::TQListBoxPixmap( const TQPixmap & pix, const TQString& text) + : TQListBoxItem() +{ + 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. +*/ +TQListBoxPixmap::TQListBoxPixmap( TQListBox* listbox, const TQPixmap & pix, const TQString& text, + TQListBoxItem *after ) + : TQListBoxItem( listbox, after ) +{ + pm = pix; + setText( text ); +} + +/*! + \fn const TQPixmap *TQListBoxPixmap::pixmap() const + + Returns the pixmap associated with the item. +*/ + + +/*! + Draws the pixmap using \a painter. +*/ + +void TQListBoxPixmap::paint( TQPainter *painter ) +{ + int itemHeight = height( listBox() ); + int yPos; + + const TQPixmap *pm = pixmap(); + if ( pm && ! pm->isNull() ) { + yPos = ( itemHeight - pm->height() ) / 2; + painter->drawPixmap( 3, yPos, *pm); + } + + if ( !text().isEmpty() ) { + TQFontMetrics 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 TQListBoxPixmap::height( const TQListBox* lb ) const +{ + int h; + if ( text().isEmpty() ) + h = pm.height(); + else + h = TQMAX( pm.height(), lb->fontMetrics().lineSpacing() + 2 ); + return TQMAX( h, TQApplication::globalStrut().height() ); +} + +/*! + Returns the width of the pixmap plus some margin in list box \a lb. + + \sa paint(), height() +*/ + +int TQListBoxPixmap::width( const TQListBox* lb ) const +{ + if ( text().isEmpty() ) + return TQMAX( pm.width() + 6, TQApplication::globalStrut().width() ); + return TQMAX( pm.width() + lb->fontMetrics().width( text() ) + 6, + TQApplication::globalStrut().width() ); +} + +int TQListBoxPixmap::RTTI = 2; + +/*! + \fn int TQListBoxPixmap::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 TQListBoxPixmap::rtti() const +{ + return RTTI; +} + +/*! + \class TQListBox qlistbox.h + \brief The TQListBox 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. + + TQListBox 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 TQListBox may become slow with huge lists. (See + TQListView and TQTable for possible alternatives.) + + There are a variety of selection modes described in the + TQListBox::SelectionMode documentation. The default is \c Single + selection mode, but you can change it using setSelectionMode(). + (setMultiSelection() is still provided for compatibility with TQt + 1.x. We recommend using setSelectionMode() in all code.) + + Because TQListBox 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 retquired. 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 TQListBox 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 TQListBox's items are TQListBoxItem objects. TQListBox provides + methods to insert new items as strings, as pixmaps, and as + TQListBoxItem * (insertItem() with various arguments), and to + replace an existing item with a new string, pixmap or TQListBoxItem + (changeItem() with various arguments). You can also remove items + singly with removeItem() or clear() the entire list box. Note that + if you create a TQListBoxItem yourself and insert it, TQListBox + takes ownership of the item. + + You can also create a TQListBoxItem, such as TQListBoxText or + TQListBoxPixmap, 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; TQListBox will add + scroll bars if necessary. TQListBox can display a single-column + (the common case) or multiple-columns, and offers both single and + multiple selection. TQListBox does not support multiple-column + items (but TQListView and TQTable do), or tree hierarchies (but + TQListView does). + + The list box items can be accessed both as TQListBoxItem objects + (recommended) and using integer indexes (the original TQListBox + 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 TQListBox contains a TQListBoxItem. 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 TQListBox::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 + tquite inefficient. It's preferable to insert the items normally + and call sort() afterwards, or to insert a sorted TQStringList(). + + 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 TQListBoxItem. + + \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 TQListView TQComboBox TQButtonGroup + \link guibooks.html#fowler GUI Design Handbook: List Box (two + sections)\endlink +*/ + +/*! + \enum TQListBox::SelectionMode + + This enumerated type is used by TQListBox 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 TQListBox::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 TQListBox::LayoutMode + + This enum type is used to specify how TQListBox 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 retquired by the + column mode. (Or as many columns as retquired 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 TQListBox::onItem( TQListBoxItem *i ) + + This signal is emitted when the user moves the mouse cursor onto + an item, similar to the TQWidget::enterEvent() function. \a i is + the TQListBoxItem 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 TQListBox::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 TQListBoxItem children is redrawn. This may be + unsuitable for custom TQListBoxItem classes, in which case \c + WStaticContents and \c WNoAutoErase should be cleared + immediately after construction. + + \sa TQWidget::clearWFlags() TQt::WidgetFlags +*/ + +TQListBox::TQListBox( TQWidget *parent, const char *name, WFlags f ) + : TQScrollView( parent, name, f | WStaticContents | WNoAutoErase ) +{ + d = new TQListBoxPrivate( this ); + d->updateTimer = new TQTimer( this, "listbox update timer" ); + d->visibleTimer = new TQTimer( this, "listbox visible timer" ); + d->inputTimer = new TQTimer( this, "listbox input timer" ); + d->resizeTimer = new TQTimer( 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 ); +} + + +TQListBox * TQListBox::changedListBox = 0; + +/*! + Destroys the list box. Deletes all list box items. +*/ + +TQListBox::~TQListBox() +{ + if ( changedListBox == this ) + changedListBox = 0; + clear(); + delete d; + d = 0; +} + +/*! + \fn void TQListBox::pressed( TQListBoxItem *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 TQListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \overload void TQListBox::pressed( TQListBoxItem *item, const TQPoint &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 (TQMouseEvent::globalPos()). + + Note that you must not delete any TQListBoxItem objects in slots + connected to this signal. + + \sa mouseButtonPressed() rightButtonPressed() clicked() +*/ + +/*! + \fn void TQListBox::clicked( TQListBoxItem *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 TQListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \overload void TQListBox::clicked( TQListBoxItem *item, const TQPoint &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 (TQMouseEvent::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 TQListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void TQListBox::mouseButtonClicked (int button, TQListBoxItem * item, const TQPoint & 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 (TQMouseEvent::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 TQListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void TQListBox::mouseButtonPressed (int button, TQListBoxItem * item, const TQPoint & 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 (TQMouseEvent::globalPos()). + + Note that you must not delete any TQListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void TQListBox::doubleClicked( TQListBoxItem *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 TQListBox::returnPressed( TQListBoxItem * ) + + This signal is emitted when Enter or Return is pressed. The + argument is currentItem(). +*/ + +/*! + \fn void TQListBox::rightButtonClicked( TQListBoxItem *, const TQPoint& ) + + 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 TQListBoxItem (may be 0) and the point + in global coordinates. +*/ + + +/*! + \fn void TQListBox::rightButtonPressed (TQListBoxItem *, const TQPoint & ) + + This signal is emitted when the right button is pressed. The + arguments are the relevant TQListBoxItem (may be 0) and the point + in global coordinates. +*/ + +/*! + \fn void TQListBox::contextMenuRequested( TQListBoxItem *item, const TQPoint & 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 TQListBox::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, TQListBox tries to emit + just one selectionChanged() signal so the signal can be connected + to computationally expensive slots. + + \sa selected() currentItem() +*/ + +/*! + \overload void TQListBox::selectionChanged( TQListBoxItem *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 TQListBox::currentChanged( TQListBoxItem *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 TQListBox::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 TQListBox::highlighted( TQListBoxItem * ) + + 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 TQListBox::highlighted( const TQString & ) + + 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 TQListBox::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 TQListBox::selected( TQListBoxItem * ) + + 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 TQListBox::selected( const TQString &) + + 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 TQListBox::setFont( const TQFont &font ) +{ + TQScrollView::setFont( font ); + triggerUpdate( TRUE ); +} + + +/*! + \property TQListBox::count + \brief the number of items in the list box +*/ + +uint TQListBox::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 TQString, + 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 TQStrings. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStringList() +*/ + +void TQListBox::insertStrList( const TQStrList *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 TQListBox::insertStringList( const TQStringList & list, int index ) +{ + if ( index < 0 ) + index = count(); + for ( TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) + insertItem( new TQListBoxText(*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 TQString, + 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 TQStrings. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStringList() +*/ + +void TQListBox::insertStrList( const TQStrList & list, int index ) +{ + TQStrListIterator it( list ); + const char* txt; + if ( index < 0 ) + index = count(); + while ( (txt=it.current()) ) { + ++it; + insertItem( new TQListBoxText(TQString::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 TQString, + 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 TQStrings. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStringList() +*/ + +void TQListBox::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 TQListBoxText( TQString::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 TQListBox::insertItem( const TQListBoxItem *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; + } + + TQListBoxItem * item = (TQListBoxItem *)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 { + TQListBoxItem * 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 TQListBox::insertItem( const TQListBoxItem *lbi, const TQListBoxItem *after ) +{ +#if defined ( QT_CHECK_NULL ) + Q_ASSERT( lbi != 0 ); +#else + if ( !lbi ) + return; +#endif + + TQListBoxItem * item = (TQListBoxItem*)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 { + TQListBoxItem * i = (TQListBoxItem*) 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 = (TQListBoxItem*) 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 TQListBox::insertItem( const TQString &text, int index ) +{ + insertItem( new TQListBoxText(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 TQListBox::insertItem( const TQPixmap &pixmap, int index ) +{ + insertItem( new TQListBoxPixmap(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 TQListBox::insertItem( const TQPixmap &pixmap, const TQString &text, int index ) +{ + insertItem( new TQListBoxPixmap(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 TQListBox::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 TQListBox::clear() +{ + setContentsPos( 0, 0 ); + bool blocked = signalsBlocked(); + blockSignals( TRUE ); + d->clearing = TRUE; + d->current = 0; + d->tmpCurrent = 0; + TQListBoxItem * i = d->head; + d->head = 0; + while ( i ) { + TQListBoxItem * 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 TQString::null if there + is no text at that position. + + \sa pixmap() +*/ + +TQString TQListBox::text( int index ) const +{ + TQListBoxItem * i = item( index ); + if ( i ) + return i->text(); + return TQString::null; +} + + +/*! + Returns a pointer to the pixmap at position \a index, or 0 if + there is no pixmap there. + + \sa text() +*/ + +const TQPixmap *TQListBox::pixmap( int index ) const +{ + TQListBoxItem * 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 TQListBox::changeItem( const TQString &text, int index ) +{ + if( index >= 0 && index < (int)count() ) + changeItem( new TQListBoxText(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 TQListBox::changeItem( const TQPixmap &pixmap, int index ) +{ + if( index >= 0 && index < (int)count() ) + changeItem( new TQListBoxPixmap(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 TQListBox::changeItem( const TQPixmap &pixmap, const TQString &text, int index ) +{ + if( index >= 0 && index < (int)count() ) + changeItem( new TQListBoxPixmap(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 TQListBox::changeItem( const TQListBoxItem *lbi, int index ) +{ + if ( !lbi || index < 0 || index >= (int)count() ) + return; + + removeItem( index ); + insertItem( lbi, index ); + setCurrentItem( index ); +} + + +/*! + \property TQListBox::numItemsVisible + \brief the number of visible items. + + Both partially and entirely visible items are counted. +*/ + +int TQListBox::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 TQListBox::currentItem() const +{ + if ( !d->current || !d->head ) + return -1; + + return index( d->current ); +} + + +/*! + \property TQListBox::currentText + \brief the text of the current item. + + This is equivalent to text(currentItem()). +*/ + + +/*! + \property TQListBox::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 TQListBox::setCurrentItem( int index ) +{ + setCurrentItem( item( index ) ); +} + + +/*! + \overload + + Sets the current item to the TQListBoxItem \a i. +*/ +void TQListBox::setCurrentItem( TQListBoxItem * i ) +{ + if ( !i || d->current == i || numRows() == 0 ) + return; + + TQRect mfrect = itemRect( i ); + if ( mfrect.isValid() ) + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); + + TQListBoxItem * 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) + TQAccessible::updateAccessibility( viewport(), ind+1, TQAccessible::StateChanged ); +#endif + } + if ( changed ) { + emit selectionChanged(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( viewport(), 0, TQAccessible::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 ); + + TQString 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) + TQAccessible::updateAccessibility( viewport(), ind+1, TQAccessible::Focus ); +#endif +} + + +/*! + Returns a pointer to the item at position \a index, or 0 if \a + index is out of bounds. + + \sa index() +*/ + +TQListBoxItem *TQListBox::item( int index ) const +{ + if ( index < 0 || index > d->count -1 ) + return 0; + + TQListBoxItem * 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 TQListBox::index( const TQListBoxItem * lbi ) const +{ + if ( !lbi ) + return -1; + TQListBoxItem * i_n = d->head; + int c_n = 0; + if ( d->cache ) { + i_n = d->cache; + c_n = d->cacheIndex; + } + TQListBoxItem* 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 TQListBox::itemVisible( int index ) +{ + TQListBoxItem * i = item( index ); + return i ? itemVisible( i ) : FALSE; +} + + +/*! + \overload + + Returns TRUE if \a item is at least partly visible; otherwise + returns FALSE. +*/ + +bool TQListBox::itemVisible( const TQListBoxItem * 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 TQListBox::mousePressEvent( TQMouseEvent *e ) +{ + mousePressEventEx( e ); +} + +void TQListBox::mousePressEventEx( TQMouseEvent *e ) +{ + d->mouseInternalPress = TRUE; + TQListBoxItem * 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() & TQMouseEvent::ShiftButton) && + !(e->state() & TQMouseEvent::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; + TQListBoxItem *oldCurrent = item( currentItem() ); + bool down = index( oldCurrent ) < index( i ); + + TQListBoxItem *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 TQRect( 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 TQListBox::mouseReleaseEvent( TQMouseEvent *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(); + } + + TQListBoxItem * 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 TQListBox::mouseDoubleClickEvent( TQMouseEvent *e ) +{ + bool ok = TRUE; + TQListBoxItem *i = itemAt( e->pos() ); + if ( !i || selectionMode() == NoSelection ) + ok = FALSE; + + d->ignoreMoves = TRUE; + + if ( d->current && ok ) { + TQListBoxItem * i = d->current; + TQString tmp = d->current->text(); + emit selected( currentItem() ); + emit selected( i ); + if ( !tmp.isNull() ) + emit selected( tmp ); + emit doubleClicked( i ); + } +} + + +/*! \reimp */ + +void TQListBox::mouseMoveEvent( TQMouseEvent *e ) +{ + TQListBoxItem * i = itemAt( e->pos() ); + if ( i != d->highlighted ) { + if ( i ) { + emit onItem( i ); + } else { + emit onViewport(); + } + d->highlighted = i; + } + + if ( d->rubber ) { + TQRect 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 ( !TQRect( 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 = TQPoint( dx, dy ); + + if ( ( dx || dy ) && !d->scrollTimer && e->state() == LeftButton && e->button() != LeftButton ) { + // start autoscrolling if necessary + d->scrollTimer = new TQTimer( this ); + connect( d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll()) ); + d->scrollTimer->start( 100, FALSE ); + doAutoScroll(); + } else if ( !d->scrollTimer ) { + // or just select the retquired bits + updateSelection(); + } +} + + + +void TQListBox::updateSelection() +{ + if ( d->mouseMoveColumn >= 0 && d->mouseMoveRow >= 0 && + d->mousePressColumn >= 0 && d->mousePressRow >= 0 ) { + TQListBoxItem * 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) + TQAccessible::updateAccessibility( viewport(), ind+1, TQAccessible::StateChanged ); + TQAccessible::updateAccessibility( viewport(), 0, TQAccessible::Selection ); + TQAccessible::updateAccessibility( viewport(), ind+1, TQAccessible::SelectionAdd ); +#endif + } + triggerUpdate( FALSE ); + } else { + int c = TQMIN( d->mouseMoveColumn, d->mousePressColumn ); + int r = TQMIN( d->mouseMoveRow, d->mousePressRow ); + int c2 = TQMAX( d->mouseMoveColumn, d->mousePressColumn ); + int r2 = TQMAX( d->mouseMoveRow, d->mousePressRow ); + bool changed = FALSE; + while( c <= c2 ) { + TQListBoxItem * 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) + TQAccessible::updateAccessibility( viewport(), ind+1, TQAccessible::StateChanged ); + TQAccessible::updateAccessibility( viewport(), ind+1, d->select ? TQAccessible::SelectionAdd : TQAccessible::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) + TQAccessible::updateAccessibility( viewport(), 0, TQAccessible::Selection ); +#endif + triggerUpdate( FALSE ); + } + } + if ( i ) + setCurrentItem( i ); + } + } +} + +void TQListBox::repaintSelection() +{ + if ( d->numColumns == 1 ) { + for ( uint i = topItem(); itemVisible( i ) && i < count(); ++i ) { + TQListBoxItem *it = item(i); + if ( !it ) + break; + if ( it->isSelected() ) + updateItem( it ); + } + } else { + for ( uint i = 0; i < count(); ++i ) { + TQListBoxItem *it = item(i); + if ( !it ) + break; + if ( it->isSelected() ) + updateItem( it ); + } + } +} + +/*! \reimp +*/ + +void TQListBox::contentsContextMenuEvent( TQContextMenuEvent *e ) +{ + if ( !receivers( SIGNAL(contextMenuRequested(TQListBoxItem*,const TQPoint&)) ) ) { + e->ignore(); + return; + } + if ( e->reason() == TQContextMenuEvent::Keyboard ) { + TQListBoxItem *i = item( currentItem() ); + if ( i ) { + TQRect r = itemRect( i ); + emit contextMenuRequested( i, mapToGlobal( r.topLeft() + TQPoint( width() / 2, r.height() / 2 ) ) ); + } + } else { + TQListBoxItem * i = itemAt( contentsToViewport( e->pos() ) ); + emit contextMenuRequested( i, e->globalPos() ); + } +} + +/*!\reimp +*/ +void TQListBox::keyPressEvent( TQKeyEvent *e ) +{ + if ( ( e->key() == TQt::Key_Tab || e->key() == TQt::Key_Backtab ) + && e->state() & TQt::ControlButton ) + e->ignore(); + + if ( count() == 0 ) { + e->ignore(); + return; + } + + TQGuardedPtr<TQListBox> selfCheck = this; + + TQListBoxItem *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 = TQString::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 = TQString::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 = TQString::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 { + TQApplication::sendEvent( horizontalScrollBar(), e ); + } + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = d->current; + } + break; + case Key_Right: + { + d->currInputString = TQString::null; + if ( currentColumn() < numColumns()-1 ) { + int row = currentRow(); + int i = currentItem(); + TQListBoxItem *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 { + TQApplication::sendEvent( horizontalScrollBar(), e ); + } + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = d->current; + } + break; + case Key_Next: + { + d->currInputString = TQString::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 = TQString::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 = TQString::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 = TQString::null; + if ( currentItem() >= 0 && selectionMode() != NoSelection ) { + TQString 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 = TQString::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 = TQString::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() == TQListBox::Extended ) { + bool changed = FALSE; + bool block = signalsBlocked(); + blockSignals( TRUE ); + selectAll( FALSE ); + blockSignals( block ); + TQListBoxItem *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 = TQString::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 TQListBox::focusInEvent( TQFocusEvent* ) +{ + d->mousePressRow = -1; + d->mousePressColumn = -1; + d->inMenuMode = FALSE; + if ( TQFocusEvent::reason() != TQFocusEvent::Mouse && !d->current && d->head ) { + d->current = d->head; + TQListBoxItem *i = d->current; + TQString 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( TQStyle::SH_ItemView_ChangeHighlightOnFocus, this ) ) + repaintSelection(); + + if ( d->current ) { + updateItem( currentItem() ); + TQRect mfrect = itemRect( d->current ); + if ( mfrect.isValid() ) + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); + } +} + + +/*!\reimp +*/ +void TQListBox::focusOutEvent( TQFocusEvent* ) +{ + if (style().styleHint( TQStyle::SH_ItemView_ChangeHighlightOnFocus, this )) { + d->inMenuMode = + TQFocusEvent::reason() == TQFocusEvent::Popup || + (qApp->focusWidget() && qApp->focusWidget()->inherits("TQMenuBar")); + if ( !d->inMenuMode ) + repaintSelection(); + } + + if ( d->current ) + updateItem( currentItem() ); +} + +/*!\reimp +*/ +bool TQListBox::eventFilter( TQObject *o, TQEvent *e ) +{ + //### remove in 4.0 + return TQScrollView::eventFilter( o, e ); +} + +/*! + Repaints the item at position \a index in the list. +*/ + +void TQListBox::updateItem( int index ) +{ + if ( index >= 0 ) + updateItem( item( index ) ); +} + + +/*! + \overload + + Repaints the TQListBoxItem \a i. +*/ + +void TQListBox::updateItem( TQListBoxItem * i ) +{ + if ( !i ) + return; + i->dirty = TRUE; + d->updateTimer->start( 0, TRUE ); +} + + +/*! + \property TQListBox::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 TQListBox::setSelectionMode( SelectionMode mode ) +{ + if ( d->selectionMode == mode ) + return; + + if ( ( selectionMode() == Multi || selectionMode() == Extended ) + && ( mode == TQListBox::Single || mode == TQListBox::NoSelection ) ){ + clearSelection(); + if ( ( mode == TQListBox::Single ) && currentItem() ) + setSelected( currentItem(), TRUE ); + } + + d->selectionMode = mode; + triggerUpdate( TRUE ); +} + + +TQListBox::SelectionMode TQListBox::selectionMode() const +{ + return d->selectionMode; +} + + +/*! + \obsolete + \property TQListBox::multiSelection + \brief whether or not the list box is in Multi selection mode + + Consider using the \l TQListBox::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 TQListBox::isMultiSelection() const +{ + return selectionMode() == Multi || selectionMode() == Extended; +} + +void TQListBox::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 TQListBox::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 ); + TQAccessible::updateAccessibility( viewport(), 0, TQAccessible::Selection ); + TQAccessible::updateAccessibility( viewport(), ind+1, TQAccessible::StateChanged ); + TQAccessible::updateAccessibility( viewport(), ind+1, d->current->s ? TQAccessible::SelectionAdd : TQAccessible::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 TQListBox::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 TQListBox::setSelected( TQListBoxItem * 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 ) { + TQListBoxItem *o = d->current; + if ( d->current && d->current->s ) + d->current->s = FALSE; + d->current = item; +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( viewport(), ind+1, TQAccessible::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) + TQAccessible::updateAccessibility( viewport(), ind+1, TQAccessible::StateChanged ); +#endif + } + emit selectionChanged(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( viewport(), 0, TQAccessible::Selection ); + if ( d->selectionMode != Single ) + TQAccessible::updateAccessibility( viewport(), ind+1, select ? TQAccessible::SelectionAdd : TQAccessible::SelectionRemove ); +#endif + + if ( emitHighlighted ) { + TQString 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 TQListBox::isSelected( int i ) const +{ + if ( selectionMode() == Single && i != currentItem() ) + return FALSE; + + TQListBoxItem * 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 TQListBox::isSelected( const TQListBoxItem * 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() +*/ + +TQListBoxItem* TQListBox::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 TQListBox::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 TQListBox::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 ) { + TQListBoxItem * i = d->current; + setSelected( i, select ); + } +} + +/*! + Inverts the selection. Only works in \c Multi and \c Extended + selection mode. +*/ + +void TQListBox::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 TQListBox::emitChangedSignal( bool ) +{ +} + + +/*! \reimp */ + +TQSize TQListBox::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 = TQMIN( 200, d->columnPos[i] + + 2 * style().pixelMetric( TQStyle::PM_DefaultFrameWidth ) ); + x = TQMAX( 40, x ); + + i = 0; + while( i < 10 && + i < (int)d->rowPos.size()-1 && + d->rowPos[i] < 200 ) + i++; + int y; + y = TQMIN( 200, d->rowPos[i] + + 2 * style().pixelMetric( TQStyle::PM_DefaultFrameWidth ) ); + y = TQMAX( 40, y ); + + TQSize s( x, y ); + setCachedSizeHint( s ); + return s; +} + +/*! + \reimp +*/ + +TQSize TQListBox::minimumSizeHint() const +{ + return TQScrollView::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 TQListBox::triggerUpdate( bool doLayout ) +{ + if ( doLayout ) + d->layoutDirty = d->mustPaintAll = TRUE; + d->updateTimer->start( 0, TRUE ); +} + + +void TQListBox::setColumnMode( LayoutMode mode ) +{ + if ( mode == Variable ) + return; + d->rowModeWins = FALSE; + d->columnMode = mode; + triggerUpdate( TRUE ); +} + + +void TQListBox::setColumnMode( int columns ) +{ + if ( columns < 1 ) + columns = 1; + d->columnMode = FixedNumber; + d->numColumns = columns; + d->rowModeWins = FALSE; + triggerUpdate( TRUE ); +} + +void TQListBox::setRowMode( LayoutMode mode ) +{ + if ( mode == Variable ) + return; + d->rowModeWins = TRUE; + d->rowMode = mode; + triggerUpdate( TRUE ); +} + + +void TQListBox::setRowMode( int rows ) +{ + if ( rows < 1 ) + rows = 1; + d->rowMode = FixedNumber; + d->numRows = rows; + d->rowModeWins = TRUE; + triggerUpdate( TRUE ); +} + +/*! + \property TQListBox::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 +*/ + + +TQListBox::LayoutMode TQListBox::columnMode() const +{ + if ( d->rowModeWins ) + return Variable; + else + return d->columnMode; +} + + +/*! + \property TQListBox::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 +*/ + + +TQListBox::LayoutMode TQListBox::rowMode() const +{ + if ( d->rowModeWins ) + return d->rowMode; + else + return Variable; +} + + +/*! + \property TQListBox::numColumns + \brief the number of columns in the list box + + This is normally 1, but can be different if \l + TQListBox::columnMode or \l TQListBox::rowMode has been set. + + \sa columnMode rowMode numRows +*/ + +int TQListBox::numColumns() const +{ + if ( count() == 0 ) + return 0; + if ( !d->rowModeWins && d->columnMode == FixedNumber ) + return d->numColumns; + doLayout(); + return d->columnPos.size()-1; +} + + +/*! + \property TQListBox::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 TQListBox::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 TQListBox::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; + TQListBoxItem * 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; + TQListBoxItem * 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]; + TQSize s( viewportSize( w, h ) ); + w = TQMAX( 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; + ((TQListBox *)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 TQListBox::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; + TQListBoxItem * 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 TQListBox::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 TQListBox::currentColumn() const +{ + if ( !d->current ) + return -1; + if ( d->currentColumn < 0 ) + d->layoutDirty = TRUE; + if ( d->layoutDirty ) + doLayout(); + return d->currentColumn; +} + + +void TQListBox::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 TQListBox::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. +*/ + +TQListBoxItem * TQListBox::itemAt( const TQPoint& p ) const +{ + if ( d->layoutDirty ) + doLayout(); + TQPoint 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 ); + + TQListBoxItem *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 TQListBox::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 TQListBox::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 == TQPoint( 0, 0 ) ) { + delete d->scrollTimer; + d->scrollTimer = 0; + } +} + + +/*! + \property TQListBox::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 TQListBox::topItem() const +{ + doLayout(); + + // move rightwards to the best column + int col = columnAt( contentsX() ); + int row = rowAt( contentsY() ); + return col * numRows() + row; +} + + +/*! + \property TQListBox::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 TQListBox::variableHeight() const +{ + return d->variableHeight; +} + + +void TQListBox::setVariableHeight( bool enable ) +{ + if ( (bool)d->variableHeight == enable ) + return; + + d->variableHeight = enable; + triggerUpdate( TRUE ); +} + + +/*! + \property TQListBox::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 TQListBox::variableWidth() const +{ + return d->variableWidth; +} + + +void TQListBox::setVariableWidth( bool enable ) +{ + if ( (bool)d->variableWidth == enable ) + return; + + d->variableWidth = enable; + triggerUpdate( TRUE ); +} + + +/*! + Repaints only what really needs to be repainted. +*/ +void TQListBox::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; + } + + TQRegion 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++; + TQListBoxItem * 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( TQRect( 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 TQListBox::viewportPaintEvent( TQPaintEvent * e ) +{ + doLayout(); + TQWidget* vp = viewport(); + TQPainter p( vp ); + TQRegion 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; + + TQListBoxItem * i = item( col*numRows() + row ); + + const TQColorGroup & 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]; + TQRect itemRect( d->columnPos[col]-x, d->rowPos[row]-y, cw, ch ); + TQRegion tempRegion( itemRect ); + TQRegion 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 == TQRegion( 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 TQListBox::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 TQListBox::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 TQListBox::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[ TQMAX( l, d->rowPosCache - 10 ) ] <= y + && y <= d->rowPos[ TQMIN( r, d->rowPosCache + 10 ) ] ) { + l = TQMAX( l, d->rowPosCache - 10 ); + r = TQMIN( 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. +*/ + +TQRect TQListBox::itemRect( TQListBoxItem *item ) const +{ + if ( d->resizeTimer->isActive() ) + return TQRect( 0, 0, -1, -1 ); + if ( !item ) + return TQRect( 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(); + + TQRect r( x, y, d->columnPos[ col + 1 ] - d->columnPos[ col ], + d->rowPos[ row + 1 ] - d->rowPos[ row ] ); + if ( r.intersects( TQRect( 0, 0, visibleWidth(), visibleHeight() ) ) ) + return r; + return TQRect( 0, 0, -1, -1 ); +} + + +#ifndef QT_NO_COMPAT + +/*! + \obsolete + + Using this method is tquite 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 TQListBox::inSort( const TQListBoxItem * lbi ) +{ + qObsolete( "TQListBox", "inSort", "insertItem" ); + if ( !lbi ) + return -1; + + TQListBoxItem * 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 tquite 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 TQListBox::inSort( const TQString& text ) +{ + qObsolete( "TQListBox", "inSort", "insertItem" ); + return inSort( new TQListBoxText(text) ); +} + +#endif + + +/*! \reimp */ + +void TQListBox::resizeEvent( TQResizeEvent *e ) +{ + d->layoutDirty = ( d->layoutDirty || + rowMode() == FitToHeight || + columnMode() == FitToWidth ); + + if ( !d->layoutDirty && columnMode() == FixedNumber && + d->numColumns == 1) { + int w = d->columnPosOne; + TQSize s( viewportSize( w, contentsHeight() ) ); + w = TQMAX( w, s.width() ); + d->columnPos[1] = TQMAX( 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(); + TQScrollView::resizeEvent( e ); + if ( currentItemVisible ) + ensureCurrentVisible(); + if ( d->current ) + viewport()->repaint( itemRect( d->current ), FALSE ); + } else if ( ( d->columnMode == FitToWidth || d->rowMode == FitToHeight ) && !(isVisible()) ) { + TQScrollView::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() ) ); + TQScrollView::resizeEvent( e ); + } else { + TQScrollView::resizeEvent( e ); + } +} + +/*! + \internal +*/ + +void TQListBox::adjustItems() +{ + triggerUpdate( TRUE ); + ensureCurrentVisible(); +} + + +/*! + Provided for compatibility with the old TQListBox. We recommend + using TQListBoxItem::paint() instead. + + Repaints the cell at \a row, \a col using painter \a p. +*/ + +void TQListBox::paintCell( TQPainter * p, int row, int col ) +{ + bool drawActiveSelection = hasFocus() || d->inMenuMode || + !style().styleHint( TQStyle::SH_ItemView_ChangeHighlightOnFocus, this ); + const TQColorGroup &g = ( drawActiveSelection ? colorGroup() : palette().inactive() ); + + int cw = d->columnPos[col+1] - d->columnPos[col]; + int ch = d->rowPos[row+1] - d->rowPos[row]; + TQListBoxItem * i = item( col*numRows()+row ); + p->save(); + if ( i->s ) { + if ( i->custom_highlight ) { + p->fillRect( 0, 0, cw, ch, + g.brush( TQPalette::backgroundRoleFromMode( viewport()->backgroundMode() ) ) ); + p->setPen( g.highlightedText() ); + p->setBackgroundColor( g.highlight() ); + } + else if ( numColumns() == 1 ) { + p->fillRect( 0, 0, cw, ch, g.brush( TQColorGroup::Highlight ) ); + p->setPen( g.highlightedText() ); + p->setBackgroundColor( g.highlight() ); + } else { + int iw = i->width( this ); + p->fillRect( 0, 0, iw, ch, g.brush( TQColorGroup::Highlight ) ); + p->fillRect( iw, 0, cw - iw + 1, ch, + g.brush( TQPalette::backgroundRoleFromMode( viewport()->backgroundMode() ) ) ); + p->setPen( g.highlightedText() ); + p->setBackgroundColor( g.highlight() ); + } + } else { + p->fillRect( 0, 0, cw, ch, + g.brush( TQPalette::backgroundRoleFromMode( viewport()->backgroundMode() ) ) ); + } + + i->paint( p ); + + if ( d->current == i && hasFocus() && !i->custom_highlight ) { + if ( numColumns() > 1 ) + cw = i->width( this ); + + style().drawPrimitive( TQStyle::PE_FocusRect, p, TQRect( 0, 0, cw, ch ), g, + TQStyle::Style_FocusAtBorder, + TQStyleOption(i->isSelected() ? g.highlight() : g.base() ) ); + } + + p->restore(); +} + +/*! + Returns the width of the widest item in the list box. +*/ + +long TQListBox::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 TQListBox::showEvent( TQShowEvent * ) +{ + 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 TQListBox::itemYPos( int index, int *yPos ) const +{ + qObsolete( "TQListBox", "itemYPos" ); + TQListBoxItem* i = item(index); + if ( !i ) + return FALSE; + if ( yPos ) + *yPos = i->y; + return TRUE; +} + +#endif + +/*! + \fn bool TQListBoxItem::isSelected() const + + Returns TRUE if the item is selected; otherwise returns FALSE. + + \sa TQListBox::isSelected(), isCurrent() +*/ + +/*! + \fn bool TQListBoxItem::selected() const + \obsolete +*/ + +/*! + Returns TRUE if the item is the current item; otherwise returns + FALSE. + + \sa TQListBox::currentItem(), TQListBox::item(), isSelected() +*/ +bool TQListBoxItem::isCurrent() const +{ + return listBox() && listBox()->hasFocus() && + listBox()->item( listBox()->currentItem() ) == this; +} +/*! + \fn bool TQListBoxItem::current() const + \obsolete +*/ + +/*! + \fn void TQListBox::centerCurrentItem() + \obsolete + + This function does exactly the same as ensureCurrentVisible() + + \sa TQListBox::ensureCurrentVisible() +*/ + +/*! + Returns a pointer to the list box containing this item. +*/ + +TQListBox * TQListBoxItem::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 TQListBoxItem::~TQListBoxItem() + calls it. The normal way to delete an item is with \c delete. + + \sa TQListBox::insertItem() +*/ +void TQListBox::takeItem( const TQListBoxItem * 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; + TQListBoxItem *i = d->current; + TQString 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(); + ((TQListBoxItem *)item)->lbox = 0; + triggerUpdate( TRUE ); +} + +/*! + \internal + Finds the next item after start beginning with \a text. +*/ + +int TQListBoxPrivate::findItemByName( int start, const TQString &text ) +{ + if ( start < 0 || (uint)start >= listBox->count() ) + start = 0; + TQString match = text.lower(); + if ( match.length() < 1 ) + return start; + TQString 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 TQListBox::clearInputString() +{ + d->currInputString = TQString::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 TQt::StringComparisonMode +*/ + +TQListBoxItem *TQListBox::findItem( const TQString &text, ComparisonFlags compare ) const +{ + if ( text.isEmpty() ) + return 0; + + if ( compare == CaseSensitive || compare == 0 ) + compare |= ExactMatch; + + TQString itmtxt; + TQString comtxt = text; + if ( ! (compare & CaseSensitive ) ) + comtxt = text.lower(); + + TQListBoxItem *item; + if ( d->current ) + item = d->current; + else + item = d->head; + + TQListBoxItem *beginsWithItem = 0; + TQListBoxItem *endsWithItem = 0; + TQListBoxItem *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 TQListBox::drawRubber() +{ + if ( !d->rubber ) + return; + if ( !d->rubber->width() && !d->rubber->height() ) + return; + TQPainter p( viewport() ); + p.setRasterOp( NotROP ); + style().drawPrimitive( TQStyle::PE_RubberBand, &p, d->rubber->normalize(), + colorGroup() ); + p.end(); +} + +/*! + \internal +*/ + +void TQListBox::doRubberSelection( const TQRect &old, const TQRect &rubber ) +{ + TQListBoxItem *i = d->head; + TQRect ir, pr; + bool changed = FALSE; + for ( ; i; i = i->n ) { + ir = itemRect( i ); + if ( ir == TQRect( 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) + TQAccessible::updateAccessibility( viewport(), 0, TQAccessible::Selection ); +#endif + } + viewport()->repaint( pr, TRUE ); +} + + +/*! + Returns TRUE if the user is selecting items using a rubber band + rectangle; otherwise returns FALSE. +*/ + +bool TQListBox::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() +*/ + +TQListBoxItem *TQListBoxItem::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() +*/ + +TQListBoxItem *TQListBoxItem::prev() const +{ + return p; +} + +/*! + Returns the first item in this list box. If the list box is empty, + returns 0. +*/ + +TQListBoxItem *TQListBox::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; + + TQListBoxPrivate::SortableItem *i1 = (TQListBoxPrivate::SortableItem *)n1; + TQListBoxPrivate::SortableItem *i2 = (TQListBoxPrivate::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 (TQListBoxItem::text()) of the items + is used. +*/ + +void TQListBox::sort( bool ascending ) +{ + if ( count() == 0 ) + return; + + d->cache = 0; + + TQListBoxPrivate::SortableItem *items = new TQListBoxPrivate::SortableItem[ count() ]; + + TQListBoxItem *item = d->head; + int i = 0; + for ( ; item; item = item->n ) + items[ i++ ].item = item; + + qsort( items, count(), sizeof( TQListBoxPrivate::SortableItem ), cmpListBoxItems ); + + TQListBoxItem *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 TQListBox::handleItemChange( TQListBoxItem *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 TQListBox::selectRange( TQListBoxItem *from, TQListBoxItem *to, bool invert, bool includeFirst, bool clearSel ) +{ + if ( !from || !to ) + return; + if ( from == to && !includeFirst ) + return; + TQListBoxItem *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) + TQAccessible::updateAccessibility( viewport(), 0, TQAccessible::Selection ); +#endif + } +} + +/*! \reimp */ +void TQListBox::windowActivationChange( bool oldActive ) +{ + if ( oldActive && d->scrollTimer ) + d->scrollTimer->stop(); + if ( palette().active() != palette().inactive() ) + viewport()->update(); + TQScrollView::windowActivationChange( oldActive ); +} + +int TQListBoxItem::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 TQListBoxItem::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 000000000..2f40a5579 --- /dev/null +++ b/src/widgets/qlistbox.h @@ -0,0 +1,435 @@ +/********************************************************************** +** +** Definition of TQListBox widget class +** +** Created : 941121 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQLISTBOX_H +#define TQLISTBOX_H + +#ifndef QT_H +#include "qscrollview.h" +#include "qpixmap.h" +#endif // QT_H + +#ifndef QT_NO_LISTBOX + + +class TQListBoxPrivate; +class TQListBoxItem; +class TQString; +class TQStrList; +class TQStringList; + + +class Q_EXPORT TQListBox : public TQScrollView +{ + friend class TQListBoxItem; + friend class TQListBoxPrivate; + + 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( TQString 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: + TQListBox( TQWidget* parent=0, const char* name=0, WFlags f=0 ); + ~TQListBox(); + + virtual void setFont( const TQFont & ); + + uint count() const; + + void insertStringList( const TQStringList&, int index=-1 ); + void insertStrList( const TQStrList *, int index=-1 ); + void insertStrList( const TQStrList &, int index=-1 ); + void insertStrList( const char **, + int numStrings=-1, int index=-1 ); + + void insertItem( const TQListBoxItem *, int index=-1 ); + void insertItem( const TQListBoxItem *, const TQListBoxItem *after ); + void insertItem( const TQString &text, int index=-1 ); + void insertItem( const TQPixmap &pixmap, int index=-1 ); + void insertItem( const TQPixmap &pixmap, const TQString &text, int index=-1 ); + + void removeItem( int index ); + + TQString text( int index ) const; + const TQPixmap *pixmap( int index ) const; + + void changeItem( const TQListBoxItem *, int index ); + void changeItem( const TQString &text, int index ); + void changeItem( const TQPixmap &pixmap, int index ); + void changeItem( const TQPixmap &pixmap, const TQString &text, int index ); + + void takeItem( const TQListBoxItem * ); + + int numItemsVisible() const; + + int currentItem() const; + TQString currentText() const { return text(currentItem()); } + virtual void setCurrentItem( int index ); + virtual void setCurrentItem( TQListBoxItem * ); + 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( TQListBoxItem *, bool ); + void setSelected( int, bool ); + bool isSelected( int ) const; + bool isSelected( const TQListBoxItem * ) const; + TQListBoxItem* selectedItem() const; + + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + + TQListBoxItem *item( int index ) const; + int index( const TQListBoxItem * ) const; + TQListBoxItem *findItem( const TQString &text, ComparisonFlags compare = BeginsWith ) const; + + void triggerUpdate( bool doLayout ); + + bool itemVisible( int index ); + bool itemVisible( const TQListBoxItem * ); + + 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( TQPaintEvent * ); + +#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 TQListBoxItem * ); + int inSort( const TQString& 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; + TQListBoxItem * itemAt( const TQPoint & ) const; + + TQRect itemRect( TQListBoxItem *item ) const; + + TQListBoxItem *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 TQString &); + void selected( const TQString &); + void highlighted( TQListBoxItem * ); + void selected( TQListBoxItem * ); + + void selectionChanged(); + void selectionChanged( TQListBoxItem * ); + void currentChanged( TQListBoxItem * ); + void clicked( TQListBoxItem * ); + void clicked( TQListBoxItem *, const TQPoint & ); + void pressed( TQListBoxItem * ); + void pressed( TQListBoxItem *, const TQPoint & ); + + void doubleClicked( TQListBoxItem * ); + void returnPressed( TQListBoxItem * ); + void rightButtonClicked( TQListBoxItem *, const TQPoint & ); + void rightButtonPressed( TQListBoxItem *, const TQPoint & ); + void mouseButtonPressed( int, TQListBoxItem*, const TQPoint& ); + void mouseButtonClicked( int, TQListBoxItem*, const TQPoint& ); + + void contextMenuRequested( TQListBoxItem *, const TQPoint & ); + + void onItem( TQListBoxItem *item ); + void onViewport(); + +protected: + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseDoubleClickEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void contentsContextMenuEvent( TQContextMenuEvent * ); + + void keyPressEvent( TQKeyEvent *e ); + void focusInEvent( TQFocusEvent *e ); + void focusOutEvent( TQFocusEvent *e ); + void resizeEvent( TQResizeEvent * ); + void showEvent( TQShowEvent * ); + + bool eventFilter( TQObject *o, TQEvent *e ); + + void updateItem( int index ); + void updateItem( TQListBoxItem * ); + +#ifndef QT_NO_COMPAT + void updateCellWidth() { } + int totalWidth() const { return contentsWidth(); } + int totalHeight() const { return contentsHeight(); } +#endif + + virtual void paintCell( TQPainter *, 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(TQPoint(0,yPos)) ); } +#endif + +protected slots: + void clearInputString(); + +private slots: + void refreshSlot(); + void doAutoScroll(); + void adjustItems(); + +private: + void mousePressEventEx( TQMouseEvent * ); + void tryGeometry( int, int ) const; + int currentRow() const; + int currentColumn() const; + void updateSelection(); + void repaintSelection(); + void drawRubber(); + void doRubberSelection( const TQRect &old, const TQRect &rubber ); + void handleItemChange( TQListBoxItem *old, bool shift, bool control ); + void selectRange( TQListBoxItem *from, TQListBoxItem *to, bool invert, bool includeFirst, bool clearSel = FALSE ); + + void emitChangedSignal( bool ); + + int columnAt( int ) const; + int rowAt( int ) const; + + TQListBoxPrivate * d; + + static TQListBox * changedListBox; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQListBox( const TQListBox & ); + TQListBox &operator=( const TQListBox & ); +#endif +}; + + +class Q_EXPORT TQListBoxItem +{ +public: + TQListBoxItem( TQListBox* listbox = 0 ); + TQListBoxItem( TQListBox* listbox, TQListBoxItem *after ); + virtual ~TQListBoxItem(); + + virtual TQString text() const; + virtual const TQPixmap *pixmap() const; + + virtual int height( const TQListBox * ) const; + virtual int width( const TQListBox * ) const; + + bool isSelected() const { return s; } + bool isCurrent() const; + +#ifndef QT_NO_COMPAT + bool selected() const { return isSelected(); } + bool current() const { return isCurrent(); } +#endif + + TQListBox *listBox() const; + + void setSelectable( bool b ); + bool isSelectable() const; + + TQListBoxItem *next() const; + TQListBoxItem *prev() const; + + virtual int rtti() const; + static int RTTI; + +protected: + virtual void paint( TQPainter * ) = 0; + virtual void setText( const TQString &text ) { txt = text; } + void setCustomHighlighting( bool ); + +private: + TQString txt; + uint s:1; + uint dirty:1; + uint custom_highlight : 1; + int x, y; + TQListBoxItem * p, * n; + TQListBox* lbox; + friend class TQListBox; + friend class TQListBoxPrivate; + friend class TQComboBox; + friend class TQComboBoxPopupItem; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQListBoxItem( const TQListBoxItem & ); + TQListBoxItem &operator=( const TQListBoxItem & ); +#endif +}; + + +class Q_EXPORT TQListBoxText : public TQListBoxItem +{ +public: + TQListBoxText( TQListBox* listbox, const TQString & text=TQString::null ); + TQListBoxText( const TQString & text=TQString::null ); + TQListBoxText( TQListBox* listbox, const TQString & text, TQListBoxItem *after ); + ~TQListBoxText(); + + int height( const TQListBox * ) const; + int width( const TQListBox * ) const; + + int rtti() const; + static int RTTI; + +protected: + void paint( TQPainter * ); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQListBoxText( const TQListBoxText & ); + TQListBoxText &operator=( const TQListBoxText & ); +#endif +}; + + +class Q_EXPORT TQListBoxPixmap : public TQListBoxItem +{ +public: + TQListBoxPixmap( TQListBox* listbox, const TQPixmap & ); + TQListBoxPixmap( const TQPixmap & ); + TQListBoxPixmap( TQListBox* listbox, const TQPixmap & pix, TQListBoxItem *after ); + TQListBoxPixmap( TQListBox* listbox, const TQPixmap &, const TQString& ); + TQListBoxPixmap( const TQPixmap &, const TQString& ); + TQListBoxPixmap( TQListBox* listbox, const TQPixmap & pix, const TQString&, TQListBoxItem *after ); + ~TQListBoxPixmap(); + + const TQPixmap *pixmap() const { return ± } + + int height( const TQListBox * ) const; + int width( const TQListBox * ) const; + + int rtti() const; + static int RTTI; + +protected: + void paint( TQPainter * ); + +private: + TQPixmap pm; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQListBoxPixmap( const TQListBoxPixmap & ); + TQListBoxPixmap &operator=( const TQListBoxPixmap & ); +#endif +}; + + +#endif // QT_NO_LISTBOX + +#endif // TQLISTBOX_H diff --git a/src/widgets/qlistview.cpp b/src/widgets/qlistview.cpp new file mode 100644 index 000000000..f537cc3b8 --- /dev/null +++ b/src/widgets/qlistview.cpp @@ -0,0 +1,8174 @@ +/**************************************************************************** +** +** Implementation of TQListView widget class +** +** Created : 970809 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQCleanupHandler<TQBitmap> qlv_cleanup_bitmap; + +struct TQListViewItemIteratorPrivate +{ + TQListViewItemIteratorPrivate( uint f ) : flags( f ) + { + // nothing yet + } + + uint flags; +}; + +static TQPtrDict<TQListViewItemIteratorPrivate> *qt_iteratorprivate_dict = 0; + +struct TQListViewPrivate +{ + // classes that are here to avoid polluting the global name space + + // the magical hidden mother of all items + class Root: public TQListViewItem { + public: + Root( TQListView * parent ); + + void setHeight( int ); + void invalidateHeight(); + void setup(); + TQListView * theListView() const; + + TQListView * lv; + }; + + // for the stack used in drawContentsOffset() + class Pending { + public: + Pending( int level, int ypos, TQListViewItem * 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 + TQListViewItem * 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; + TQListViewItem * i; + }; + + // for sorting + class SortableItem { + public: + /* + We could be smarter and keep a pointer to the TQListView + 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. + */ + TQListViewItem * 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; } + TQString text, tmpText; + TQPixmap * pm; + ItemColumnInfo * next; + uint truncated : 1; + uint dirty : 1; + uint allow_rename : 1; + int width; + }; + + class ViewColumnInfo { + public: + ViewColumnInfo(): align(TQt::AlignAuto), sortable(TRUE), next( 0 ) {} + ~ViewColumnInfo() { delete next; } + int align; + bool sortable; + ViewColumnInfo * next; + }; + + // private variables used in TQListView + ViewColumnInfo * vci; + TQHeader * h; + Root * r; + uint rootIsExpandable : 1; + int margin; + + TQListViewItem * focusItem, *highlighted, *oldFocusItem; + + TQTimer * timer; + TQTimer * dirtyItemTimer; + TQTimer * visibleTimer; + int levelWidth; + + // the list of drawables, and the range drawables covers entirely + // (it may also include a few items above topPixel) + TQPtrList<DrawableItem> * drawables; + int topPixel; + int bottomPixel; + + TQPtrDict<void> * dirtyItems; + + TQListView::SelectionMode selectionMode; + + // Per-column structure for information not in the TQHeader + struct Column { + TQListView::WidthMode wmode; + }; + TQPtrVector<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 + TQString currentPrefix; + TQTime currentPrefixTime; + + // holds a list of iterators + TQPtrList<TQListViewItemIterator> *iterators; + TQListViewItem *pressedItem, *selectAnchor; + + TQTimer *scrollTimer; + TQTimer *renameTimer; + TQTimer *autoopenTimer; + + // sort column and order #### may need to move to TQHeader [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; + + TQListView::RenameAction defRenameAction; + + TQListViewItem *startDragItem; + TQPoint dragStartPos; + TQListViewToolTip *toolTip; + int pressedColumn; + TQListView::ResizeMode resizeMode; +}; + +#ifndef QT_NO_TOOLTIP +class TQListViewToolTip : public TQToolTip +{ +public: + TQListViewToolTip( TQWidget *parent, TQListView *lv ); + + void maybeTip( const TQPoint &pos ); + +private: + TQListView *view; + +}; + +TQListViewToolTip::TQListViewToolTip( TQWidget *parent, TQListView *lv ) + : TQToolTip( parent ), view( lv ) +{ +} + +void TQListViewToolTip::maybeTip( const TQPoint &pos ) +{ + if ( !parentWidget() || !view || !view->showToolTips() ) + return; + + TQListViewItem *item = view->itemAt( pos ); + TQPoint contentsPos = view->viewportToContents( pos ); + if ( !item || !item->columns ) + return; + int col = view->header()->sectionAt( contentsPos.x() ); + TQListViewPrivate::ItemColumnInfo *ci = (TQListViewPrivate::ItemColumnInfo*)item->columns; + for ( int i = 0; ci && ( i < col ); ++i ) + ci = ci->next; + + if ( !ci || !ci->truncated ) + return; + + TQRect r = view->itemRect( item ); + int headerPos = view->header()->sectionPos( col ); + r.setLeft( headerPos ); + r.setRight( headerPos + view->header()->sectionSize( col ) ); + tip( r, TQStyleSheet::escape(item->text( col ) ) ); +} +#endif + +// these should probably be in TQListViewPrivate, 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 TQPoint activatedP; + +#if defined(QT_ACCESSIBILITY_SUPPORT) +static int indexOfItem( TQListViewItem *item ) +{ + if ( !TQAccessible::isActive() ) + return 0; + + static TQListViewItem *lastItem = 0; + static int lastIndex = 0; + + if ( !item || !item->listView() ) + return 0; + + if ( item == lastItem ) + return lastIndex; + + lastItem = item; + int index = 1; + + TQListViewItemIterator 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 TQString qEllipsisText( const TQString &org, const TQFontMetrics &fm, int width, int align ) +{ + int ellWidth = fm.width( "..." ); + TQString text = TQString::fromLatin1(""); + int i = 0; + int len = org.length(); + int offset = (align & TQt::AlignRight) ? (len-1) - i : i; + while ( i < len && fm.width( text + org[ offset ] ) + ellWidth < width ) { + if ( align & TQt::AlignRight ) + text.prepend( org[ offset ] ); + else + text += org[ offset ]; + offset = (align & TQt::AlignRight) ? (len-1) - ++i : ++i; + } + if ( text.isEmpty() ) + text = ( align & TQt::AlignRight ) ? org.right( 1 ) : text = org.left( 1 ); + if ( align & TQt::AlignRight ) + text.prepend( "..." ); + else + text += "..."; + return text; +} + +/*! + \class TQListViewItem + \brief The TQListViewItem class implements a list view item. + + \ingroup advanced + + A list view item is a multi-column object capable of displaying + itself in a TQListView. + + The easiest way to use TQListViewItem is to construct one with a + few constant strings, and either a TQListView or another + TQListViewItem as parent. + \code + (void) new TQListViewItem( listView, "Column 1", "Column 2" ); + (void) new TQListViewItem( 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, TQListView sorts its + items; this can be switched off with TQListView::setSorting(-1). + + The parent must be another TQListViewItem or a TQListView. If the + parent is a TQListView, the item becomes a top-level item within + that TQListView. If the parent is another TQListViewItem, 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 TQListViewItem in a TQListView using + TQListView::selectedItem(), TQListView::currentItem(), + TQListView::firstChild(), TQListView::lastItem() and + TQListView::findItem(). + + If you call \c delete on a list view item, it will be deleted as + expected, and as usual for \l{TQObject}s, if it has any child items + (to any depth), all these will be deleted too. + + \l{TQCheckListItem}s are list view items that have a checkbox or + radio button and can be used in place of plain TQListViewItems. + + 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 + TQListViewItem * 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 + TQListViewItemIterator it( listview ); + while ( it.current() ) { + TQListViewItem *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; TQListView will + always call it before it needs to show an item. + + Many programs will need to reimplement TQListViewItem. 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, TQFileDialog + 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 tquickly: 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 TQCheckListItem TQListView +*/ + +/*! + \fn int TQCheckListItem::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 TQListView \a + parent. +*/ + +TQListViewItem::TQListViewItem( TQListView * 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. +*/ + +TQListViewItem::TQListViewItem( TQListViewItem * 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 TQListView the item will be a top-level item. +*/ + +TQListViewItem::TQListViewItem( TQListView * parent, TQListViewItem * 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. +*/ + +TQListViewItem::TQListViewItem( TQListViewItem * parent, TQListViewItem * after ) +{ + init(); + parent->insertItem( this ); + moveToJustAfter( after ); +} + + + +/*! + Constructs a new top-level list view item in the TQListView \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() +*/ + +TQListViewItem::TQListViewItem( TQListView * parent, + TQString label1, + TQString label2, + TQString label3, + TQString label4, + TQString label5, + TQString label6, + TQString label7, + TQString 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 TQListViewItem \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() +*/ + +TQListViewItem::TQListViewItem( TQListViewItem * parent, + TQString label1, + TQString label2, + TQString label3, + TQString label4, + TQString label5, + TQString label6, + TQString label7, + TQString 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 TQListView \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 TQListViewItem::key() + unless the list view's sorting is disabled using + TQListView::setSorting(-1). + + \sa setText() +*/ + +TQListViewItem::TQListViewItem( TQListView * parent, TQListViewItem * after, + TQString label1, + TQString label2, + TQString label3, + TQString label4, + TQString label5, + TQString label6, + TQString label7, + TQString 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 TQListViewItem \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 TQListViewItem::key() + unless the list view's sorting is disabled using + TQListView::setSorting(-1). + + \sa setText() +*/ + +TQListViewItem::TQListViewItem( TQListViewItem * parent, TQListViewItem * after, + TQString label1, + TQString label2, + TQString label3, + TQString label4, + TQString label5, + TQString label6, + TQString label7, + TQString 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 TQListViewItem::sort() +{ + if ( !listView() ) + return; + lsc = Unsorted; + enforceSortOrder(); + listView()->triggerUpdate(); +} + +int TQListViewItem::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 TQListViewItem::rtti() const +{ + return RTTI; +} + +/* + Performs the initializations that's common to the constructors. +*/ + +void TQListViewItem::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. + TQListViewItemIterator. +*/ + +void TQListViewItem::setVisible( bool b ) +{ + if ( b == (bool)visible ) + return; + TQListView *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 ( TQListViewItem *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 TQListViewItem::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 TQListViewItem::setRenameEnabled( int col, bool b ) +{ + TQListViewPrivate::ItemColumnInfo * l = (TQListViewPrivate::ItemColumnInfo*)columns; + if ( !l ) { + l = new TQListViewPrivate::ItemColumnInfo; + columns = (void*)l; + } + for( int c = 0; c < col; c++ ) { + if ( !l->next ) + l->next = new TQListViewPrivate::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 TQListViewItem::renameEnabled( int col ) const +{ + TQListViewPrivate::ItemColumnInfo * l = (TQListViewPrivate::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 TQListViewItem::setEnabled( bool b ) +{ + if ( (bool)enabled == b ) + return; + enabled = b; + if ( !enabled ) + selected = FALSE; + TQListView *lv = listView(); + if ( lv ) { + lv->triggerUpdate(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( lv->viewport(), indexOfItem( this ), TQAccessible::StateChanged ); +#endif + } +} + +/*! + Returns TRUE if this item is enabled; otherwise returns FALSE. + + \sa setEnabled() +*/ + +bool TQListViewItem::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 TQListViewItem::startRename( int col ) +{ + if ( !renameEnabled( col ) ) + return; + if ( renameBox ) + cancelRename( col ); + TQListView *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 == TQListView::Reject ) + lv->currentItem()->cancelRename( lv->currentItem()->renameCol ); + else + lv->currentItem()->okRename( lv->currentItem()->renameCol ); + } + + if ( this != lv->currentItem() ) + lv->setCurrentItem( this ); + + TQRect r = lv->itemRect( this ); + r = TQRect( lv->viewportToContents( r.topLeft() ), r.size() ); + r.setLeft( lv->header()->sectionPos( col ) ); + r.setWidth(TQMIN(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 TQLineEdit( lv->viewport(), "qt_renamebox" ); + renameBox->setFrameStyle( TQFrame::Box | TQFrame::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 TQListViewItem::removeRenameBox() +{ + // Sanity, it should be checked by the functions calling this first anyway + TQListView *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 TQListViewItem::okRename( int col ) +{ + TQListView *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 TQListViewItem::cancelRename( int ) +{ + TQListView *lv = listView(); + if ( !lv || !renameBox ) + return; + removeRenameBox(); +} + +/*! + Destroys the item, deleting all its children and freeing up all + allocated resources. +*/ + +TQListViewItem::~TQListViewItem() +{ + if ( renameBox ) { + delete renameBox; + renameBox = 0; + } + + TQListView *lv = listView(); + + if ( lv ) { + if ( lv->d->oldFocusItem == this ) + lv->d->oldFocusItem = 0; + if ( lv->d->iterators ) { + TQListViewItemIterator *i = lv->d->iterators->first(); + while ( i ) { + if ( i->current() == this ) + i->currentRemoved(); + i = lv->d->iterators->next(); + } + } + } + + if ( parentItem ) + parentItem->takeItem( this ); + TQListViewItem * i = childItem; + childItem = 0; + while ( i ) { + i->parentItem = 0; + TQListViewItem * n = i->siblingItem; + delete i; + i = n; + } + delete (TQListViewPrivate::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 TQListViewItem::setMultiLinesEnabled( bool b ) +{ + mlenabled = b; +} + +/*! + Returns TRUE if the item can display multiple lines of text in its + columns; otherwise returns FALSE. +*/ + +bool TQListViewItem::multiLinesEnabled() const +{ + return mlenabled; +} + +/*! + If \a allow is TRUE, the list view starts a drag (see + TQListView::dragObject()) when the user presses and moves the mouse + on this item. +*/ + + +void TQListViewItem::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 TQListViewItem::setDropEnabled( bool allow ) +{ + allow_drop = (uint)allow; +} + +/*! + Returns TRUE if this item can be dragged; otherwise returns FALSE. + + \sa setDragEnabled() +*/ + +bool TQListViewItem::dragEnabled() const +{ + return (bool)allow_drag; +} + +/*! + Returns TRUE if this item accepts drops; otherwise returns FALSE. + + \sa setDropEnabled(), acceptDrop() +*/ + +bool TQListViewItem::dropEnabled() const +{ + return (bool)allow_drop; +} + +/*! + Returns TRUE if the item can accept drops of type TQMimeSource \a + mime; otherwise returns FALSE. + + The default implementation does nothing and returns FALSE. A + subclass must reimplement this to accept drops. +*/ + +bool TQListViewItem::acceptDrop( const TQMimeSource * ) 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 TQListViewItem::dropped( TQDropEvent *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 TQListViewItem::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 TQListViewItem::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 TQListViewItem::insertItem( TQListViewItem * newChild ) +{ + TQListView *lv = listView(); + if ( lv && lv->currentItem() && lv->currentItem()->renameBox ) { + if ( lv->d->defRenameAction == TQListView::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 TQListViewItem::removeItem( TQListViewItem * ) + \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 + TQListViewItem::~TQListViewItem() 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 TQListViewItem::insertItem() +*/ + +void TQListViewItem::takeItem( TQListViewItem * item ) +{ + if ( !item ) + return; + + TQListView *lv = listView(); + if ( lv && lv->currentItem() && lv->currentItem()->renameBox ) { + if ( lv->d->defRenameAction == TQListView::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 ) { + TQListViewItemIterator *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 TQListViewItem * 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 TQListViewItem *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--; + + TQListViewItem ** 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) + TQAccessible::updateAccessibility( lv->viewport(), 0, TQAccessible::Focus ); +#endif + } +} + + +/*! + \fn TQString TQListViewItem::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() +*/ + +TQString TQListViewItem::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 + TQString::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( TQListViewItem *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() TQString::localeAwareCompare() TQString::compare() +*/ + +int TQListViewItem::compare( TQListViewItem *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. (TQListView and + TQListViewItem 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 TQListViewItem::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() + TQListViewPrivate::SortableItem * siblings + = new TQListViewPrivate::SortableItem[nChildren]; + TQListViewItem * 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 TQListViewItem::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 TQListViewItem::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 TQListViewItem::setOpen( bool o ) +{ + if ( o == (bool)open || !enabled ) + return; + open = o; + + // If no children to show simply emit signals and return + if ( !nChildren ) { + TQListView *lv = listView(); + if ( lv && this != lv->d->r ) { + if ( o ) + emit lv->expanded( this ); + else + emit lv->collapsed( this ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( lv->viewport(), indexOfItem( this ), TQAccessible::StateChanged ); +#endif + } + return; + } + invalidateHeight(); + + if ( !configured ) { + TQListViewItem * l = this; + TQPtrStack<TQListViewItem> s; + while( l ) { + if ( l->open && l->childItem ) { + s.push( l->childItem ); + } else if ( l->childItem ) { + // first invisible child is unconfigured + TQListViewItem * 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(); + } + } + + TQListView *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) + TQAccessible::updateAccessibility( lv->viewport(), indexOfItem( this ), TQAccessible::StateChanged ); +#endif + } +} + + +/*! + This virtual function is called before the first time TQListView + 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 TQListViewItem::setup() +{ + widthChanged(); + TQListView *lv = listView(); + + int ph = 0; + int h = 0; + if ( lv ) { + for ( uint i = 0; i < lv->d->column.size(); ++i ) { + if ( pixmap( i ) ) + ph = TQMAX( ph, pixmap( i )->height() ); + } + + if ( mlenabled ) { + h = ph; + for ( int c = 0; c < lv->columns(); ++c ) { + int lines = text( c ).contains( TQChar('\n') ) + 1; + int tmph = lv->d->fontMetricsHeight + + lv->fontMetrics().lineSpacing() * ( lines - 1 ); + h = TQMAX( h, tmph ); + } + h += 2*lv->itemMargin(); + } else { + h = TQMAX( lv->d->fontMetricsHeight, ph ) + 2*lv->itemMargin(); + } + } + + h = TQMAX( h, TQApplication::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 TQListViewItem::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 TQListViewItem::activatedPos( TQPoint &pos ) +{ + if ( activatedByClick ) + pos = activatedP; + return activatedByClick; +} + + +/*! + \fn bool TQListViewItem::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 TQListViewItem::setSelectable( bool enable ) +{ + selectable = enable; +} + + +/*! + \fn bool TQListViewItem::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 + TQListView::setRootIsDecorated() is set to TRUE. + + \sa setSelectable() +*/ + +void TQListViewItem::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 TQListViewItem::enforceSortOrder() const +{ + TQListView *lv = listView(); + if ( !lv || lv && (lv->d->clearing || lv->d->sortcolumn == Unsorted) ) + return; + if ( parentItem && + (parentItem->lsc != lsc || parentItem->lso != lso) ) + ((TQListViewItem *)this)->sortChildItems( (int)parentItem->lsc, + (bool)parentItem->lso ); + else if ( !parentItem && + ( (int)lsc != lv->d->sortcolumn || (bool)lso != lv->d->ascending ) ) + ((TQListViewItem *)this)->sortChildItems( lv->d->sortcolumn, lv->d->ascending ); +} + + +/*! + \fn bool TQListViewItem::isSelected() const + + Returns TRUE if this item is selected; otherwise returns FALSE. + + \sa setSelected() TQListView::setSelected() TQListView::selectionChanged() +*/ + + +/*! + If \a s is TRUE this item is selected; otherwise it is deselected. + + This function does not maintain any invariants or repaint anything + -- TQListView::setSelected() does that. + + \sa height() totalHeight() +*/ + +void TQListViewItem::setSelected( bool s ) +{ + bool old = selected; + + TQListView *lv = listView(); + if ( lv && lv->selectionMode() != TQListView::NoSelection) { + if ( s && isSelectable() ) + selected = TRUE; + else + selected = FALSE; + +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( old != (bool)selected ) { + int ind = indexOfItem( this ); + TQAccessible::updateAccessibility( lv->viewport(), ind, TQAccessible::StateChanged ); + TQAccessible::updateAccessibility( lv->viewport(), ind, selected ? TQAccessible::SelectionAdd : TQAccessible::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 TQListViewItem::totalHeight() const +{ + if ( !visible ) + return 0; + if ( maybeTotalHeight >= 0 ) + return maybeTotalHeight; + TQListViewItem * that = (TQListViewItem *)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; + + TQListViewItem * child = that->childItem; + while ( child != 0 ) { + that->maybeTotalHeight += child->totalHeight(); + child = child->siblingItem; + } + return that->maybeTotalHeight; +} + + +/*! + Returns the text in column \a column, or TQString::null if there is + no text in that column. + + \sa key() paintCell() +*/ + +TQString TQListViewItem::text( int column ) const +{ + TQListViewPrivate::ItemColumnInfo * l + = (TQListViewPrivate::ItemColumnInfo*) columns; + + while( column && l ) { + l = l->next; + column--; + } + + return l ? l->text : TQString::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 TQListViewItem::setText( int column, const TQString &text ) +{ + if ( column < 0 ) + return; + + TQListViewPrivate::ItemColumnInfo * l + = (TQListViewPrivate::ItemColumnInfo*) columns; + if ( !l ) { + l = new TQListViewPrivate::ItemColumnInfo; + columns = (void*)l; + } + for( int c = 0; c < column; c++ ) { + if ( !l->next ) + l->next = new TQListViewPrivate::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( TQChar( '\n' ) ) + 1; + if ( !text.isEmpty() ) + newLc = text.contains( TQChar( '\n' ) ) + 1; + } + + l->dirty = TRUE; + l->text = text; + if ( column == (int)lsc ) + lsc = Unsorted; + + if ( mlenabled && oldLc != newLc ) + setup(); + else + widthChanged( column ); + + TQListView * lv = listView(); + if ( lv ) { + lv->d->useDoubleBuffer = TRUE; + lv->triggerUpdate(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( lv->isVisible() ) + TQAccessible::updateAccessibility( lv->viewport(), indexOfItem(this), TQAccessible::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 TQListViewItem::setPixmap( int column, const TQPixmap & pm ) +{ + if ( column < 0 ) + return; + + int oldW = 0; + int oldH = 0; + if ( pixmap( column ) ) { + oldW = pixmap( column )->width(); + oldH = pixmap( column )->height(); + } + + TQListViewPrivate::ItemColumnInfo * l + = (TQListViewPrivate::ItemColumnInfo*) columns; + if ( !l ) { + l = new TQListViewPrivate::ItemColumnInfo; + columns = (void*)l; + } + + for( int c = 0; c < column; c++ ) { + if ( !l->next ) + l->next = new TQListViewPrivate::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 TQPixmap( 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(); + } + TQListView *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 TQPixmap * TQListViewItem::pixmap( int column ) const +{ + TQListViewPrivate::ItemColumnInfo * l + = (TQListViewPrivate::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 TQPainter 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 TQListView::itemMargin() for readability + spacing on the left and right sides of data such as text, and + should honor isSelected() and TQListView::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(), TQListView::drawContentsOffset() +*/ + +void TQListViewItem::paintCell( TQPainter * p, const TQColorGroup & cg, + int column, int width, int align ) +{ + // Change width() if you change this. + + if ( !p ) + return; + + TQListView *lv = listView(); + if ( !lv ) + return; + TQFontMetrics 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 ) ); + } + } + + TQString t = text( column ); + + if ( columns ) { + TQListViewPrivate::ItemColumnInfo *ci = 0; + // try until we have a column info.... + while ( !ci ) { + ci = (TQListViewPrivate::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 + TQStringList list = TQStringList::split( TQChar('\n'), t, TRUE ); + for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + TQString 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( TQString("\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 TQPixmap * icon = pixmap( column ); + + const BackgroundMode bgmode = lv->viewport()->backgroundMode(); + const TQColorGroup::ColorRole crole = TQPalette::backgroundRoleFromMode( bgmode ); + if ( cg.brush( crole ) != lv->colorGroup().brush( crole ) ) + p->fillRect( 0, 0, width, height(), cg.brush( crole ) ); + else + lv->paintEmptyArea( p, TQRect( 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( TQColorGroup::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 = TQApplication::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 = TQMAX( textheight, TQApplication::globalStrut().height() ); + if ( textheight % 2 > 0 ) + textheight++; + if ( textheight < height() ) { + int w = lv->treeStepSize() / 2; + lv->style().drawComplexControl( TQStyle::CC_ListView, p, lv, + TQRect( 0, textheight, w + 1, height() - textheight + 1 ), cg, + lv->isEnabled() ? TQStyle::Style_Enabled : TQStyle::Style_Default, + TQStyle::SC_ListViewExpand, + (uint)TQStyle::SC_All, TQStyleOption( this ) ); + } + } +} + +/*! + Returns the number of pixels of width retquired 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 TQListView::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() TQListView::setColumnWidthMode() + TQListView::itemMargin() +*/ +int TQListViewItem::width( const TQFontMetrics& fm, + const TQListView* 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 TQPixmap * pm = pixmap( c ); + if ( pm ) + w += pm->width() + lv->itemMargin(); // ### correct margin stuff? + return TQMAX( w, TQApplication::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() TQListView::setAllColumnsShowFocus() +*/ + +void TQListViewItem::paintFocus( TQPainter *p, const TQColorGroup &cg, + const TQRect & r ) +{ + TQListView *lv = listView(); + if ( lv ) + lv->style().drawPrimitive( TQStyle::PE_FocusRect, p, r, cg, + (isSelected() ? + TQStyle::Style_FocusAtBorder : + TQStyle::Style_Default), + TQStyleOption(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(), TQListView::drawContentsOffset() +*/ + +void TQListViewItem::paintBranches( TQPainter * p, const TQColorGroup & cg, + int w, int y, int h ) +{ + TQListView *lv = listView(); + if ( lv ) + lv->paintEmptyArea( p, TQRect( 0, 0, w, h ) ); + if ( !visible || !lv ) + return; + lv->style().drawComplexControl( TQStyle::CC_ListView, p, lv, + TQRect( 0, y, w, h ), + cg, + lv->isEnabled() ? TQStyle::Style_Enabled : + TQStyle::Style_Default, + (TQStyle::SC_ListViewBranch | + TQStyle::SC_ListViewExpand), + TQStyle::SC_None, TQStyleOption(this) ); +} + + +TQListViewPrivate::Root::Root( TQListView * parent ) + : TQListViewItem( parent ) +{ + lv = parent; + setHeight( 0 ); + setOpen( TRUE ); +} + + +void TQListViewPrivate::Root::setHeight( int ) +{ + TQListViewItem::setHeight( 0 ); +} + + +void TQListViewPrivate::Root::invalidateHeight() +{ + TQListViewItem::invalidateHeight(); + lv->triggerUpdate(); +} + + +TQListView * TQListViewPrivate::Root::theListView() const +{ + return lv; +} + + +void TQListViewPrivate::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 TQListViewItem::ignoreDoubleClick() +{ + TQListView *lv = listView(); + if ( lv ) + lv->d->ignoreDoubleClick = TRUE; +} + + + +/*! + \fn void TQListView::onItem( TQListViewItem *i ) + + This signal is emitted when the user moves the mouse cursor onto + item \a i, similar to the TQWidget::enterEvent() function. +*/ + +// ### bug here too? see qiconview.cppp onItem/onViewport + +/*! + \fn void TQListView::onViewport() + + This signal is emitted when the user moves the mouse cursor from + an item to an empty part of the list view. +*/ + +/*! + \enum TQListView::SelectionMode + + This enumerated type is used by TQListView 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 TQListView::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 TQListView::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 TQListView + \brief The TQListView 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 TQListView, add some + column headers using addColumn() and create one or more + TQListViewItem or TQCheckListItem objects with the TQListView as + parent: + + \quotefile xml/tagreader-with-features/structureparser.h + \skipto TQListView * table + \printline + \quotefile xml/tagreader-with-features/structureparser.cpp + \skipto addColumn + \printline addColumn + \printline + \skipto new TQListViewItem( table + \printline + + Further nodes can be added to the list view object (the root of the + tree) or as child nodes to TQListViewItems: + + \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 TQScrollView functions: + \link TQScrollView::contentsMousePressEvent() contentsMousePressEvent\endlink, + \link TQScrollView::contentsMouseReleaseEvent() contentsMouseReleaseEvent\endlink, + \link TQScrollView::contentsMouseDoubleClickEvent() contentsMouseDoubleClickEvent\endlink, + \link TQScrollView::contentsMouseMoveEvent() contentsMouseMoveEvent\endlink, + \link TQScrollView::contentsDragEnterEvent() contentsDragEnterEvent\endlink, + \link TQScrollView::contentsDragMoveEvent() contentsDragMoveEvent\endlink, + \link TQScrollView::contentsDragLeaveEvent() contentsDragLeaveEvent\endlink, + \link TQScrollView::contentsDropEvent() contentsDropEvent\endlink, and + \link TQScrollView::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 + TQListViewItem::itemBelow(); over a list view's top-level items + using TQListViewItem::firstChild() and + TQListViewItem::nextSibling(); or every item using a + TQListViewItemIterator. See + the TQListViewItem documentation for examples of traversal. + + An item can be moved amongst its siblings using + TQListViewItem::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 + TQListView::SelectionMode documentation. The default is \c Single + selection, which you can change using setSelectionMode(). + + Because TQListView 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()). + + TQListView 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(TQListViewItem*) (applicable only to a + \c Single selection list view), and currentChanged(TQListViewItem*). + The second group consists of doubleClicked(TQListViewItem*), + returnPressed(TQListViewItem*), + rightButtonClicked(TQListViewItem*, const TQPoint&, 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 TQTimer::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, TQListView 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 TQListView::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 TQListViewItem TQCheckListItem +*/ + +/*! + \fn void TQListView::itemRenamed( TQListViewItem * 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 TQListViewItem::setRenameEnabled() +*/ + +/*! + \fn void TQListView::itemRenamed( TQListViewItem * item, int col, const TQString &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 TQListViewItem::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 TQListViewItem children is redrawn. This may be + unsuitable for custom TQListViewItem classes, in which case \c + WStaticContents and \c WNoAutoErase should be cleared. + + \sa TQWidget::clearWFlags() TQt::WidgetFlags +*/ +TQListView::TQListView( TQWidget * parent, const char *name, WFlags f ) + : TQScrollView( parent, name, f | WStaticContents | WNoAutoErase ) +{ + init(); +} + +void TQListView::init() +{ + d = new TQListViewPrivate; + d->vci = 0; + d->timer = new TQTimer( this ); + d->levelWidth = 20; + d->r = 0; + d->rootIsExpandable = 0; + d->h = new TQHeader( this, "list view header" ); + d->h->installEventFilter( this ); + d->focusItem = 0; + d->oldFocusItem = 0; + d->drawables = 0; + d->dirtyItems = 0; + d->dirtyItemTimer = new TQTimer( this ); + d->visibleTimer = new TQTimer( this ); + d->renameTimer = new TQTimer( this ); + d->autoopenTimer = new TQTimer( this ); + d->margin = 1; + d->selectionMode = TQListView::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 TQListViewToolTip( 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 + TQListViewPrivate::Root * r = new TQListViewPrivate::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 TQListView::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 TQHeader::setSortIndicator() +*/ + +void TQListView::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 TQListView::showSortIndicator() const +{ + return d->sortIndicator; +} + +/*! + \property TQListView::showToolTips + \brief whether this list view should show tooltips for truncated column texts + + The default is TRUE. +*/ + +void TQListView::setShowToolTips( bool b ) +{ + d->toolTips = b; +} + +bool TQListView::showToolTips() const +{ + return d->toolTips; +} + +/*! + \property TQListView::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 TQHeader, header() +*/ + +void TQListView::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 ); +} + +TQListView::ResizeMode TQListView::resizeMode() const +{ + return d->resizeMode; +} + +/*! + Destroys the list view, deleting all its items, and frees up all + allocated resources. +*/ + +TQListView::~TQListView() +{ + if ( d->iterators ) { + TQListViewItemIterator *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 TQListViewItem::paintCell() and + TQListViewItem::paintBranches() as necessary for all list view + items that retquire 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 TQListView::drawContentsOffset( TQPainter * p, int ox, int oy, + int cx, int cy, int cw, int ch ) +{ + if ( columns() == 0 ) { + paintEmptyArea( p, TQRect( 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 ) { + TQRect br( cx - ox, cy - oy, cw, ch ); + TQPtrDictIterator<void> it( *(d->dirtyItems) ); + TQListViewItem * i; + while( (i = (TQListViewItem *)(it.currentKey())) != 0 ) { + ++it; + TQRect 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() ); + + TQPtrListIterator<TQListViewPrivate::DrawableItem> it( *(d->drawables) ); + + TQRect r; + int fx = -1, x, fc = 0, lc = 0; + int tx = -1; + TQListViewPrivate::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( TQStyle::SH_ItemView_ChangeHighlightOnFocus, this ) || + ( currentItem() && currentItem()->renameBox && currentItem()->renameBox->hasFocus() ); + const TQColorGroup &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 ) { + TQRect a( 0, 0, r.width(), current->i->height() ); + TQSharedDoubleBuffer buffer( p, a, TQSharedDoubleBuffer::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 ); + TQRect 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 = TQMIN( current->l * treeStepSize(), d->h->cellSize( cell ) ); + r.setLeft( r.left() + offsetx ); + current->i->paintFocus( p, colorGroup(), r ); + } else { + int xdepth = TQMIN( treeStepSize() * ( current->i->depth() + ( rootIsDecorated() ? 1 : 0) ) + + itemMargin(), d->h->cellSize( cell ) ); + xdepth += d->h->cellPos( cell ); + TQRect r1( r ); + r1.setRight( d->h->cellPos( cell ) - 1 ); + TQRect 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 = TQMAX( rtop, cy ); + int crbottom = TQMIN( rbottom, cy+ch ); + int crleft = TQMAX( rleft, cx ); + int crright = TQMIN( 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, TQRect( 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, TQRect( 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 TQListView::paintEmptyArea( TQPainter * p, const TQRect & rect ) +{ + TQStyleOption opt( d->sortcolumn, 0 ); // ### hack; in 3.1, add a property in TQListView and TQHeader + TQStyle::SFlags how = TQStyle::Style_Default; + if ( isEnabled() ) + how |= TQStyle::Style_Enabled; + + style().drawComplexControl( TQStyle::CC_ListView, + p, this, rect, colorGroup(), + how, TQStyle::SC_ListView, TQStyle::SC_None, + opt ); +} + + +/* + Rebuilds the list of drawable TQListViewItems. This function is + const so that const functions can call it without retquiring + d->drawables to be mutable. +*/ + +void TQListView::buildDrawableList() const +{ + d->r->enforceSortOrder(); + + TQPtrStack<TQListViewPrivate::Pending> stack; + stack.push( new TQListViewPrivate::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 = ((TQListView *)this)->visibleHeight(); + d->topPixel = cy + ch; // one below bottom + d->bottomPixel = cy - 1; // one above top + + TQListViewPrivate::Pending * cur; + + // used to work around lack of support for mutable + TQPtrList<TQListViewPrivate::DrawableItem> * dl; + + dl = new TQPtrList<TQListViewPrivate::DrawableItem>; + dl->setAutoDelete( TRUE ); + if ( d->drawables ) + delete ((TQListView *)this)->d->drawables; + ((TQListView *)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 TQListViewPrivate::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 TQListViewPrivate::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(); + + TQListViewItem * 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 TQListViewPrivate::Pending( cur->l + 1, + y, c ) ); + } + + delete cur; + } +} + +/*! + \property TQListView::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 TQListView::treeStepSize() const +{ + return d->levelWidth; +} + +void TQListView::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 + TQListViewItem::takeItem(\a i) and need to reinsert \a i elsewhere. + + \sa TQListViewItem::takeItem() takeItem() +*/ + +void TQListView::insertItem( TQListViewItem * 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 TQListView::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 ) { + TQListViewItemIterator *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; + + TQListViewItem *c = (TQListViewItem *)d->r->firstChild(); + TQListViewItem *n; + while( c ) { + n = (TQListViewItem *)c->nextSibling(); + delete c; + c = n; + } + resizeContents( d->h->sizeHint().width(), contentsHeight() ); + delete d->r; + d->r = 0; + TQListViewPrivate::Root * r = new TQListViewPrivate::Root( this ); + r->is_root = TRUE; + d->r = r; + d->r->setSelectable( FALSE ); + blockSignals( block ); + triggerUpdate(); + d->clearing = FALSE; +} + +/*! + \reimp +*/ + +void TQListView::setContentsPos( int x, int y ) +{ + updateGeometries(); + TQScrollView::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 TQListView::addColumn( const TQString &label, int width ) +{ + int c = d->h->addLabel( label, width ); + d->column.resize( c+1 ); + d->column.insert( c, new TQListViewPrivate::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 TQListView::addColumn( const TQIconSet& iconset, const TQString &label, int width ) +{ + int c = d->h->addLabel( iconset, label, width ); + d->column.resize( c+1 ); + d->column.insert( c, new TQListViewPrivate::Column ); + d->column[c]->wmode = width >=0 ? Manual : Maximum; + updateGeometries(); + updateGeometry(); + return c; +} + +/*! + \property TQListView::columns + \brief the number of columns in this list view + + \sa addColumn(), removeColumn() +*/ + +int TQListView::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 TQListView::removeColumn( int index ) +{ + if ( index < 0 || index > (int)d->column.count() - 1 ) + return; + + if ( d->vci ) { + TQListViewPrivate::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; + } + } + + TQListViewItemIterator it( this ); + for ( ; it.current(); ++it ) { + TQListViewPrivate::ItemColumnInfo *ci = (TQListViewPrivate::ItemColumnInfo*)it.current()->columns; + if ( ci ) { + TQListViewPrivate::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 ) { + TQListViewPrivate::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 TQListView::setColumnText( int column, const TQString &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 TQListView::setColumnText( int column, const TQIconSet& iconset, const TQString &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 TQListView::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() +*/ + +TQString TQListView::columnText( int c ) const +{ + return d->h->label(c); +} + +/*! + Returns the width of column \a c. + + \sa setColumnWidth() +*/ + +int TQListView::columnWidth( int c ) const +{ + int actual = d->h->mapToActual( c ); + return d->h->cellSize( actual ); +} + + +/*! + \enum TQListView::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 TQListViewItem::width() +*/ + +void TQListView::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() +*/ + +TQListView::WidthMode TQListView::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 TQListViewItem::paintCell() for each item in + the list view. For horizontally aligned text with TQt::AlignLeft or + TQt::AlignHCenter the ellipsis (...) will be to the right, for + TQt::AlignRight the ellipsis will be to the left. + + \sa TQt::AlignmentFlags +*/ + +void TQListView::setColumnAlignment( int column, int align ) +{ + if ( column < 0 ) + return; + if ( !d->vci ) + d->vci = new TQListViewPrivate::ViewColumnInfo; + TQListViewPrivate::ViewColumnInfo * l = d->vci; + while( column ) { + if ( !l->next ) + l->next = new TQListViewPrivate::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 TQt::AlignmentFlags +*/ + +int TQListView::columnAlignment( int column ) const +{ + if ( column < 0 || !d->vci ) + return AlignAuto; + TQListViewPrivate::ViewColumnInfo * l = d->vci; + while( column ) { + if ( !l->next ) + l->next = new TQListViewPrivate::ViewColumnInfo; + l = l->next; + column--; + } + return l ? l->align : AlignAuto; +} + + + +/*! + \reimp + */ +void TQListView::show() +{ + // Reimplemented to setx the correct background mode and viewed + // area size. + if ( !isVisible() ) { + reconfigureItems(); + updateGeometries(); + } + TQScrollView::show(); +} + + +/*! + Updates the sizes of the viewport, header, scroll bars and so on. + + \warning Don't call this directly; call triggerUpdate() instead. +*/ + +void TQListView::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 TQListView::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 - TQListView::d->h->width() ); +#if 0 + if ( TQApplication::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 { + TQSize 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 TQListView::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, TQRect( 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 ) { + TQRect r = itemRect( currentItem() ); + r = TQRect( 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 TQListView::updateDirtyItems() +{ + if ( d->timer->isActive() || !d->dirtyItems ) + return; + TQRect ir; + TQPtrDictIterator<void> it( *(d->dirtyItems) ); + TQListViewItem * i; + while( (i = (TQListViewItem *)(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 TQListView::makeVisible() +{ + if ( d->focusItem ) + ensureItemVisible( d->focusItem ); +} + + +/*! + Ensures that the header is correctly sized and positioned when the + resize event \a e occurs. +*/ + +void TQListView::resizeEvent( TQResizeEvent *e ) +{ + TQScrollView::resizeEvent( e ); + d->fullRepaintOnComlumnChange = TRUE; + d->h->resize( visibleWidth(), d->h->height() ); +} + +/*! \reimp */ + +void TQListView::viewportResizeEvent( TQResizeEvent *e ) +{ + TQScrollView::viewportResizeEvent( e ); + d->h->resize( visibleWidth(), d->h->height() ); + if ( resizeMode() != NoColumn && currentItem() && currentItem()->renameBox ) { + TQRect r = itemRect( currentItem() ); + r = TQRect( 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 TQListView::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 TQListView::eventFilter( TQObject * o, TQEvent * e ) +{ + if ( o == d->h && + e->type() >= TQEvent::MouseButtonPress && + e->type() <= TQEvent::MouseMove ) { + TQMouseEvent * me = (TQMouseEvent *)e; + TQMouseEvent me2( me->type(), + TQPoint( me->pos().x(), + me->pos().y() - d->h->height() ), + me->button(), me->state() ); + switch( me2.type() ) { + case TQEvent::MouseButtonDblClick: + if ( me2.button() == RightButton ) + return TRUE; + break; + case TQEvent::MouseMove: + if ( me2.state() & RightButton ) { + viewportMouseMoveEvent( &me2 ); + return TRUE; + } + break; + default: + break; + } + } else if ( o == viewport() ) { + TQFocusEvent * fe = (TQFocusEvent *)e; + + switch( e->type() ) { + case TQEvent::FocusIn: + focusInEvent( fe ); + return TRUE; + case TQEvent::FocusOut: + focusOutEvent( fe ); + return TRUE; + default: + // nothing + break; + } + } else if ( ::qt_cast<TQLineEdit*>(o) ) { + if ( currentItem() && currentItem()->renameBox ) { + if ( e->type() == TQEvent::KeyPress ) { + TQKeyEvent *ke = (TQKeyEvent*)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() == TQEvent::FocusOut ) { + if ( ( (TQFocusEvent*)e )->reason() != TQFocusEvent::Popup ) { + TQCustomEvent *e = new TQCustomEvent( 9999 ); + TQApplication::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 TQScrollView::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 + TQListViewItem::takeItem() +*/ + +TQListView * TQListViewItem::listView() const +{ + const TQListViewItem* c = this; + while ( c && !c->is_root ) + c = c->parentItem; + if ( !c ) + return 0; + return ((TQListViewPrivate::Root*)c)->theListView(); +} + + +/*! + Returns the depth of this item. +*/ +int TQListViewItem::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() TQListView::itemRect() +*/ + +TQListViewItem * TQListViewItem::itemAbove() +{ + if ( !parentItem ) + return 0; + + TQListViewItem * 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() TQListView::itemRect() +*/ + +TQListViewItem * TQListViewItem::itemBelow() +{ + TQListViewItem * 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 TQListViewItem::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. + TQListView and TQListViewItem try to postpone or avoid sorting to + the greatest degree possible, in order to keep the user interface + snappy. + + \sa nextSibling() sortChildItems() +*/ + +TQListViewItem* TQListViewItem::firstChild() const +{ + enforceSortOrder(); + return childItem; +} + + +/*! + Returns the parent of this item, or 0 if this item has no parent. + + \sa firstChild(), nextSibling() +*/ + +TQListViewItem* TQListViewItem::parent() const +{ + if ( !parentItem || parentItem->is_root ) return 0; + return parentItem; +} + + +/*! + \fn TQListViewItem* TQListViewItem::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. + TQListView and TQListViewItem try to postpone or avoid sorting to + the greatest degree possible, in order to keep the user interface + snappy. + + \sa firstChild() sortChildItems() +*/ + +/*! + \fn int TQListViewItem::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 TQListViewItem::height() const +{ + TQListViewItem * that = (TQListViewItem *)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 TQListViewItem::widthChanged( int c ) const +{ + TQListView *lv = listView(); + if ( lv ) + lv->widthChanged( this, c ); +} + +/*! + \fn void TQListView::dropped ( TQDropEvent * 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 TQListView::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 TQListViewItem objects in slots + connected to this signal. + + \sa setSelected() TQListViewItem::setSelected() +*/ + + +/*! + \fn void TQListView::pressed( TQListViewItem *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 TQListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void TQListView::pressed( TQListViewItem *item, const TQPoint &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 TQListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void TQListView::clicked( TQListViewItem *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 TQListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void TQListView::mouseButtonClicked(int button, TQListViewItem * item, const TQPoint & 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 TQListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void TQListView::mouseButtonPressed(int button, TQListViewItem * item, const TQPoint & 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 TQListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void TQListView::clicked( TQListViewItem *item, const TQPoint &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 TQListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void TQListView::selectionChanged( TQListViewItem * ) + + \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 TQListViewItem objects in slots + connected to this signal. + + \sa setSelected() TQListViewItem::setSelected() currentChanged() +*/ + + +/*! + \fn void TQListView::currentChanged( TQListViewItem * ) + + 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 TQListViewItem objects in slots + connected to this signal. + + \sa setCurrentItem() currentItem() +*/ + + +/*! + \fn void TQListView::expanded( TQListViewItem *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 TQListView::collapsed( TQListViewItem *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 TQListView::contentsMousePressEvent( TQMouseEvent * e ) +{ + contentsMousePressEventEx( e ); +} + +void TQListView::contentsMousePressEventEx( TQMouseEvent * 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(); + TQPoint vp = contentsToViewport( e->pos() ); + + d->ignoreDoubleClick = FALSE; + d->buttonDown = TRUE; + + TQListViewItem * i = itemAt( vp ); + d->pressedEmptyArea = e->y() > contentsHeight(); + if ( i && !i->isEnabled() ) + return; + if ( d->startEdit && ( i != currentItem() || (i && !i->isSelected()) ) ) + d->startEdit = FALSE; + TQListViewItem *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 ) ); + TQPtrListIterator<TQListViewPrivate::DrawableItem> it( *(d->drawables) ); + while( it.current() && it.current()->i != i ) + ++it; + + if ( it.current() ) { + x1 -= treeStepSize() * (it.current()->l - 1); + TQStyle::SubControl ctrl = + style().querySubControl( TQStyle::CC_ListView, + this, TQPoint(x1, e->pos().y()), + TQStyleOption(i) ); + if( ctrl == TQStyle::SC_ListViewExpand && + e->type() == style().styleHint(TQStyle::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; + TQListViewItem *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; + TQPoint 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) + TQAccessible::updateAccessibility( viewport(), 0, TQAccessible::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 TQListView::contentsContextMenuEvent( TQContextMenuEvent *e ) +{ + if ( !receivers( SIGNAL(contextMenuRequested(TQListViewItem*,const TQPoint&,int)) ) ) { + e->ignore(); + return; + } + if ( e->reason() == TQContextMenuEvent::Keyboard ) { + TQListViewItem *item = currentItem(); + if ( item ) { + TQRect r = itemRect( item ); + TQPoint p = r.topLeft(); + if ( allColumnsShowFocus() ) + p += TQPoint( width() / 2, ( r.height() / 2 ) ); + else + p += TQPoint( columnWidth( 0 ) / 2, ( r.height() / 2 ) ); + p.rx() = TQMAX( 0, p.x() ); + p.rx() = TQMIN( visibleWidth(), p.x() ); + emit contextMenuRequested( item, viewport()->mapToGlobal( p ), -1 ); + } + } else { + TQPoint vp = contentsToViewport( e->pos() ); + TQListViewItem * 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 TQListView::contentsMouseReleaseEvent( TQMouseEvent * e ) +{ + contentsMouseReleaseEventEx( e ); +} + +void TQListView::contentsMouseReleaseEventEx( TQMouseEvent * 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) + TQAccessible::updateAccessibility( viewport(), 0, TQAccessible::Selection ); +#endif + } + + TQPoint vp = contentsToViewport(e->pos()); + TQListViewItem *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(TQStyle::SH_ListViewExpand_SelectMouseType, this)) { + TQPtrListIterator<TQListViewPrivate::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)); + TQStyle::SubControl ctrl = style().querySubControl( TQStyle::CC_ListView, + this, TQPoint(x1, e->pos().y()), + TQStyleOption(i) ); + if( ctrl == TQStyle::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; + TQListViewItem *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 ) { + TQRect r = itemRect( currentItem() ); + r = TQRect( 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( TQApplication::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 TQListView::contentsMouseDoubleClickEvent( TQMouseEvent * 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; + } + + TQPoint vp = contentsToViewport(e->pos()); + + TQListViewItem * 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 TQListView::contentsMouseMoveEvent( TQMouseEvent * e ) +{ + if ( !e ) + return; + + bool needAutoScroll = FALSE; + + TQPoint vp = contentsToViewport(e->pos()); + + TQListViewItem * 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() > TQApplication::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 TQTimer( 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 TQListView::doAutoScroll() +{ + doAutoScroll( TQPoint() ); +} + +/* + 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 TQCursor::pos, otherwise it uses cursorPos +*/ +void TQListView::doAutoScroll( const TQPoint &cursorPos ) +{ + TQPoint pos = cursorPos.isNull() ? viewport()->mapFromGlobal( TQCursor::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(); + + TQListViewItem *c = d->focusItem, *old = 0; + TQListViewItem *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 + TQListViewItem * 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 TQListView::focusInEvent( TQFocusEvent* ) +{ + d->inMenuMode = FALSE; + if ( d->focusItem ) { + repaintItem( d->focusItem ); + } else if ( firstChild() && TQFocusEvent::reason() != TQFocusEvent::Mouse ) { + d->focusItem = firstChild(); + emit currentChanged( d->focusItem ); + repaintItem( d->focusItem ); + } + if ( TQFocusEvent::reason() == TQFocusEvent::Mouse ) { + d->ignoreEditAfterFocus = TRUE; + d->startEdit = FALSE; + } + if ( style().styleHint( TQStyle::SH_ItemView_ChangeHighlightOnFocus, this ) ) { + bool db = d->useDoubleBuffer; + d->useDoubleBuffer = TRUE; + viewport()->repaint( FALSE ); + d->useDoubleBuffer = db; + } + + TQRect 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 TQListView::focusOutEvent( TQFocusEvent* ) +{ + if ( TQFocusEvent::reason() == TQFocusEvent::Popup && d->buttonDown ) + d->buttonDown = FALSE; + if ( style().styleHint( TQStyle::SH_ItemView_ChangeHighlightOnFocus, this ) ) { + d->inMenuMode = + TQFocusEvent::reason() == TQFocusEvent::Popup + || (qApp->focusWidget() && qApp->focusWidget()->inherits("TQMenuBar")); + if ( !d->inMenuMode ) { + bool db = d->useDoubleBuffer; + d->useDoubleBuffer = TRUE; + viewport()->repaint( FALSE ); + d->useDoubleBuffer = db; + } + } + + if ( d->focusItem ) + repaintItem( d->focusItem ); +} + + +/*! + \reimp +*/ + +void TQListView::keyPressEvent( TQKeyEvent * e ) +{ + if (currentItem() && currentItem()->renameBox) + return; + if (!firstChild()) { + e->ignore(); + return; // subclass bug + } + + TQListViewItem* oldCurrent = currentItem(); + if ( !oldCurrent ) { + setCurrentItem( firstChild() ); + if ( d->selectionMode == Single ) + setSelected( firstChild(), TRUE ); + return; + } + + TQListViewItem * i = currentItem(); + TQListViewItem *old = i; + + TQRect r( itemRect( i ) ); + TQListViewItem * 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. TQDialog. + 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( TQPoint( 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( TQPoint( 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) { + TQListViewItem *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 TQDialog + 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; + TQString input( d->currentPrefix ); + TQListViewItem * keyItem = i; + TQTime now( TQTime::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; + } + } + TQString keyItemKey; + TQString prefix; + while( keyItem ) { + keyItemKey = TQString::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 = TQString::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 + TQListViewItem *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() +*/ + +TQListViewItem * TQListView::itemAt( const TQPoint & viewPos ) const +{ + if ( viewPos.x() > contentsWidth() - contentsX() ) + return 0; + + if ( !d->drawables || d->drawables->isEmpty() ) + buildDrawableList(); + + TQListViewPrivate::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(); + + TQListViewItem *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 TQListViewItem::itemPos(). + + \sa itemAt() itemRect() +*/ + +int TQListView::itemPos( const TQListViewItem * item ) +{ + return item ? item->itemPos() : 0; +} + + +/*! \obsolete + \property TQListView::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 TQListView::setMultiSelection( bool enable ) +{ + if ( !enable ) + d->selectionMode = TQListView::Single; + else if ( d->selectionMode != Multi && d->selectionMode != Extended ) + d->selectionMode = TQListView::Multi; +} + +bool TQListView::isMultiSelection() const +{ + return d->selectionMode == TQListView::Extended || d->selectionMode == TQListView::Multi; +} + +/*! + \property TQListView::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 TQListView::setSelectionMode( SelectionMode mode ) +{ + if ( d->selectionMode == mode ) + return; + + if ( ( d->selectionMode == Multi || d->selectionMode == Extended ) && + ( mode == TQListView::Single || mode == TQListView::NoSelection ) ){ + clearSelection(); + if ( ( mode == TQListView::Single ) && currentItem() ) + currentItem()->selected = TRUE; + } + + d->selectionMode = mode; +} + +TQListView::SelectionMode TQListView::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 TQListViewItem::setSelected(), this function + updates the list view as necessary and emits the + selectionChanged() signals. + + \sa isSelected() setMultiSelection() isMultiSelection() + setCurrentItem(), setSelectionAnchor() +*/ + +void TQListView::setSelected( TQListViewItem * item, bool selected ) +{ + if ( !item || item->isSelected() == selected || + !item->isSelectable() || selectionMode() == NoSelection ) + return; + + bool emitHighlighted = FALSE; + if ( selectionMode() == Single && d->focusItem != item ) { + TQListViewItem *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 TQListView::setSelectionAnchor( TQListViewItem *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 TQListView::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 TQListView::selectAll( bool select ) +{ + if ( d->selectionMode == Multi || d->selectionMode == Extended ) { + bool b = signalsBlocked(); + blockSignals( TRUE ); + bool anything = FALSE; + TQListViewItemIterator it( this ); + while ( it.current() ) { + TQListViewItem *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 ) { + TQListViewItem * i = d->focusItem; + setSelected( i, select ); + } +} + +/*! + Inverts the selection. Only works in \c Multi and \c Extended + selection modes. +*/ + +void TQListView::invertSelection() +{ + if ( d->selectionMode == Single || + d->selectionMode == NoSelection ) + return; + + bool b = signalsBlocked(); + blockSignals( TRUE ); + TQListViewItemIterator 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 TQListViewItem::isSelected() +*/ + +bool TQListView::isSelected( const TQListViewItem * 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() +*/ + +TQListViewItem * TQListView::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 TQListView::setCurrentItem( TQListViewItem * 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 ); + } + + TQListViewItem * prev = d->focusItem; + d->focusItem = i; + + TQRect 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) + TQAccessible::updateAccessibility( viewport(), indexOfItem( i ), TQAccessible::Focus ); +#endif + } +} + + +/*! + Returns the current item, or 0 if there isn't one. + + \sa setCurrentItem() +*/ + +TQListViewItem * TQListView::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 TQListViewItem::height(), rather than + TQListViewItem::totalHeight()). If you want the rectangle to + include children you can use something like this: + + \code + TQRect r( listView->itemRect( item ) ); + r.setHeight( (TQCOORD)(TQMIN( 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. +*/ + +TQRect TQListView::itemRect( const TQListViewItem * i ) const +{ + if ( !d->drawables || d->drawables->isEmpty() ) + buildDrawableList(); + + TQListViewPrivate::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 < ((TQListView *)this)->visibleHeight() ) { + TQRect r( -contentsX(), y, d->h->width(), i->height() ); + return r; + } + } + + return TQRect( 0, 0, -1, -1 ); +} + + +/*! + \fn void TQListView::doubleClicked( TQListViewItem *item ) + + \obsolete (use doubleClicked( TQListViewItem *, const TQPoint&, 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 TQListView::doubleClicked( TQListViewItem *, const TQPoint&, 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 TQListViewItem (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 TQListViewItem objects in slots + connected to this signal. +*/ + + +/*! + \fn void TQListView::returnPressed( TQListViewItem * ) + + This signal is emitted when Enter or Return is pressed. The + argument is the currentItem(). +*/ + +/*! + \fn void TQListView::spacePressed( TQListViewItem * ) + + 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 TQListView::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) + TQAccessible::updateAccessibility( viewport(), 0, TQAccessible::ObjectReorder ); +#endif +} + +/*! + \enum TQt::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 TQListView::changeSortColumn( int column ) +{ + if ( isRenaming() ) { + if ( d->defRenameAction == TQListView::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 TQListView::handleIndexChange() +{ + if ( isRenaming() ) { + if ( d->defRenameAction == TQListView::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 TQListView::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 TQListView::setSortColumn( int column ) +{ + setSorting( column, d->ascending ); +} + +/*! + Returns the sorting order of the list view items. + + \sa sortColumn() +*/ +TQt::SortOrder TQListView::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 TQListView::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 TQListView::sort() +{ + if ( d->r ) + d->r->sort(); +} + +/*! + \property TQListView::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. + TQListViewItem::paintFocus() draws in the margin. + + \sa TQListViewItem::paintCell() +*/ + +void TQListView::setItemMargin( int m ) +{ + if ( d->margin == m ) + return; + d->margin = m; + if ( isVisible() ) { + if ( d->drawables ) + d->drawables->clear(); + triggerUpdate(); + } +} + +int TQListView::itemMargin() const +{ + return d->margin; +} + + +/*! + \fn void TQListView::rightButtonClicked( TQListViewItem *, const TQPoint&, int ) + + This signal is emitted when the right button is clicked (i.e. when + it's released). The arguments are the relevant TQListViewItem (may + be 0), the point in global coordinates and the relevant column (or + -1 if the click was outside the list). +*/ + + +/*! + \fn void TQListView::rightButtonPressed (TQListViewItem *, const TQPoint &, int) + + This signal is emitted when the right button is pressed. The + arguments are the relevant TQListViewItem (may be 0), the point in + global coordinates and the relevant column (or -1 if the click was + outside the list). +*/ + +/*! + \fn void TQListView::contextMenuRequested( TQListViewItem *item, const TQPoint & 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 TQListView::styleChange( TQStyle& old ) +{ + TQScrollView::styleChange( old ); + reconfigureItems(); +} + + +/*! + \reimp +*/ +void TQListView::setFont( const TQFont & f ) +{ + TQScrollView::setFont( f ); + reconfigureItems(); +} + + +/*! + \reimp +*/ +void TQListView::setPalette( const TQPalette & p ) +{ + TQScrollView::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 TQListViewItem::setup() +*/ + +void TQListView::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 TQListView::widthChanged( const TQListViewItem* item, int c ) +{ + if ( c >= d->h->count() ) + return; + + + TQFontMetrics 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 = TQMAX( 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 TQListView::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 TQListView::setAllColumnsShowFocus( bool enable ) +{ + d->allColumnsShowFocus = enable; +} + +bool TQListView::allColumnsShowFocus() const +{ + return d->allColumnsShowFocus; +} + + +/*! + Returns the first item in this TQListView. Returns 0 if there is no + first item. + + A list view's items can be traversed using firstChild() + and nextSibling() or using a TQListViewItemIterator. + + \sa itemAt() TQListViewItem::itemBelow() TQListViewItem::itemAbove() +*/ + +TQListViewItem * TQListView::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 TQListView. + + This function is slow because it traverses the entire tree to find + the last item. +*/ + +TQListViewItem* TQListView::lastItem() const +{ + TQListViewItem* 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 TQListViewItem::repaint() const +{ + TQListView *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 TQListView::repaintItem( const TQListViewItem * item ) const +{ + if ( !item ) + return; + d->dirtyItemTimer->start( 0, TRUE ); + if ( !d->dirtyItems ) + d->dirtyItems = new TQPtrDict<void>(); + d->dirtyItems->replace( (void *)item, (void *)item ); +} + + +struct TQCheckListItemPrivate +{ + TQCheckListItemPrivate(): + exclusive( 0 ), + currentState( TQCheckListItem::Off ), + statesDict( 0 ), + tristate( FALSE ) {} + + TQCheckListItem *exclusive; + TQCheckListItem::ToggleState currentState; + TQPtrDict<TQCheckListItem::ToggleState> *statesDict; + bool tristate; +}; + + +/*! + \class TQCheckListItem + \brief The TQCheckListItem class provides checkable list view items. + + \ingroup advanced + + TQCheckListItems are used in \l{TQListView}s to provide + \l{TQListViewItem}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 TQListViewItem TQListView +*/ + +// ### obscenity is warranted. + +/*! + \enum TQCheckListItem::Type + + This enum type specifies a TQCheckListItem's type: + + \value RadioButton + \value CheckBox + \value Controller \e obsolete (use \c RadioButtonController instead) + \value RadioButtonController + \value CheckBoxController +*/ + +/*! + \enum TQCheckListItem::ToggleState + + This enum specifies a TQCheckListItem'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. +*/ +TQCheckListItem::TQCheckListItem( TQCheckListItem *parent, const TQString &text, + Type tt ) + : TQListViewItem( parent, text, TQString::null ) +{ + myType = tt; + init(); + if ( myType == RadioButton ) { + if ( parent->type() != RadioButtonController ) + qWarning( "TQCheckListItem::TQCheckListItem(), 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. +*/ +TQCheckListItem::TQCheckListItem( TQCheckListItem *parent, TQListViewItem *after, + const TQString &text, Type tt ) + : TQListViewItem( parent, after, text ) +{ + myType = tt; + init(); + if ( myType == RadioButton ) { + if ( parent->type() != RadioButtonController ) + qWarning( "TQCheckListItem::TQCheckListItem(), 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. +*/ +TQCheckListItem::TQCheckListItem( TQListViewItem *parent, const TQString &text, + Type tt ) + : TQListViewItem( parent, text, TQString::null ) +{ + myType = tt; + if ( myType == RadioButton ) { + qWarning( "TQCheckListItem::TQCheckListItem(), radio button must be " + "child of a TQCheckListItem" ); + } + 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. +*/ +TQCheckListItem::TQCheckListItem( TQListViewItem *parent, TQListViewItem *after, + const TQString &text, Type tt ) + : TQListViewItem( parent, after, text ) +{ + myType = tt; + if ( myType == RadioButton ) { + qWarning( "TQCheckListItem::TQCheckListItem(), radio button must be " + "child of a TQCheckListItem" ); + } + 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. +*/ +TQCheckListItem::TQCheckListItem( TQListView *parent, const TQString &text, + Type tt ) + : TQListViewItem( parent, text ) +{ + myType = tt; + if ( tt == RadioButton ) + qWarning( "TQCheckListItem::TQCheckListItem(), radio button must be " + "child of a TQCheckListItem" ); + 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. +*/ +TQCheckListItem::TQCheckListItem( TQListView *parent, TQListViewItem *after, + const TQString &text, Type tt ) + : TQListViewItem( parent, after, text ) +{ + myType = tt; + if ( tt == RadioButton ) + qWarning( "TQCheckListItem::TQCheckListItem(), radio button must be " + "child of a TQCheckListItem" ); + init(); +} + + +int TQCheckListItem::RTTI = 1; + +/* \reimp */ + +int TQCheckListItem::rtti() const +{ + return RTTI; +} + +/*! + Constructs a \c RadioButtonController item with parent \a parent, + text \a text and pixmap \a p. +*/ +TQCheckListItem::TQCheckListItem( TQListView *parent, const TQString &text, + const TQPixmap & p ) + : TQListViewItem( parent, text ) +{ + myType = RadioButtonController; + setPixmap( 0, p ); + init(); +} + +/*! + Constructs a \c RadioButtonController item with parent \a parent, + text \a text and pixmap \a p. +*/ +TQCheckListItem::TQCheckListItem( TQListViewItem *parent, const TQString &text, + const TQPixmap & p ) + : TQListViewItem( parent, text ) +{ + myType = RadioButtonController; + setPixmap( 0, p ); + init(); +} + +void TQCheckListItem::init() +{ + d = new TQCheckListItemPrivate(); + on = FALSE; // ### remove on ver 4 + if ( myType == CheckBoxController || myType == CheckBox ) { + d->statesDict = new TQPtrDict<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. +*/ +TQCheckListItem::~TQCheckListItem() +{ + 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 TQCheckListItem::Type TQCheckListItem::type() const + + Returns the type of this item. +*/ + +/*! + \fn bool TQCheckListItem::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 TQCheckListItem::setTristate( bool b ) +{ + if ( ( myType != CheckBoxController ) && ( myType != CheckBox ) ) { + qWarning( "TQCheckListItem::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 TQCheckListItem::isTristate() const +{ + return d->tristate; +} + +/*! + Returns the state of the item. + + \sa TQCheckListItem::ToggleState +*/ +TQCheckListItem::ToggleState TQCheckListItem::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. +*/ +TQCheckListItem::ToggleState TQCheckListItem::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 TQCheckListItem::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 TQCheckListItem::setState( ToggleState s, bool update, bool store) +{ + + if ( s == internalState() ) + return; + + if ( myType == CheckBox ) { + setCurrentState( s ); + stateChange( state() ); + if ( update && parent() && parent()->rtti() == 1 + && ((TQCheckListItem*)parent())->type() == CheckBoxController ) + ((TQCheckListItem*)parent())->updateController( update, store ); + } else if ( myType == CheckBoxController ) { + if ( s == NoChange && childCount()) { + restoreState( (void*) this ); + } else { + TQListViewItem *item = firstChild(); + int childCount = 0; + while( item ) { + if ( item->rtti() == 1 && + ( ((TQCheckListItem*)item)->type() == CheckBox || + ((TQCheckListItem*)item)->type() == CheckBoxController ) ) { + TQCheckListItem *checkItem = (TQCheckListItem*)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 && + ((TQCheckListItem*)parent())->type() == CheckBoxController ) + ((TQCheckListItem*)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 + && ((TQCheckListItem*)parent())->type() == CheckBoxController ) + ((TQCheckListItem*)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 TQCheckListItem::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() ) + TQAccessible::updateAccessibility( listView()->viewport(), indexOfItem( this ), TQAccessible::StateChanged ); +#else + Q_UNUSED( old ); +#endif +} + + + +/* + updates the internally stored state of this item for the parent (key) +*/ +void TQCheckListItem::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. +*/ +TQCheckListItem::ToggleState TQCheckListItem::storedState( void *key ) const +{ + if ( !d->statesDict ) + return Off; + + ToggleState *foundState = d->statesDict->find( key ); + if ( foundState ) + return ToggleState( *foundState ); + else + return Off; +} + + +/*! + \fn TQString TQCheckListItem::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 TQCheckListItem::turnOffChild() +{ + if ( myType == RadioButtonController && d->exclusive ) + d->exclusive->setOn( FALSE ); +} + +/*! + Toggle check box or set radio button to on. +*/ +void TQCheckListItem::activate() +{ + TQListView * lv = listView(); + + if ( lv && !lv->isEnabled() || !isEnabled() ) + return; + + TQPoint pos; + int boxsize = lv->style().pixelMetric(TQStyle::PM_CheckListButtonSize, lv); + if ( activatedPos( pos ) ) { + bool parentControl = FALSE; + if ( parent() && parent()->rtti() == 1 && + ((TQCheckListItem*) 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; + + TQRect 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 TQCheckListItem::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 TQCheckListItem::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 TQCheckListItem::stateChange( ToggleState s ) +{ + stateChange( s == On ); +} + +/* + sets the state of the CheckBox and CheckBoxController back to + previous stored state +*/ +void TQCheckListItem::restoreState( void *key, int depth ) +{ + switch ( type() ) { + case CheckBox: + setCurrentState( storedState( key ) ); + stateChange( state() ); + repaint(); + break; + case CheckBoxController: { + TQListViewItem *item = firstChild(); + int childCount = 0; + while ( item ) { + // recursively calling restoreState for children of type CheckBox and CheckBoxController + if ( item->rtti() == 1 && + ( ((TQCheckListItem*)item)->type() == CheckBox || + ((TQCheckListItem*)item)->type() == CheckBoxController ) ) { + ((TQCheckListItem*)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 TQCheckListItem::updateController( bool update , bool store ) +{ + if ( myType != CheckBoxController ) + return; + + TQCheckListItem *controller = 0; + // checks if this CheckBoxController has another CheckBoxController as parent + if ( parent() && parent()->rtti() == 1 + && ((TQCheckListItem*)parent())->type() == CheckBoxController ) + controller = (TQCheckListItem*)parent(); + + ToggleState theState = Off; + bool first = TRUE; + TQListViewItem *item = firstChild(); + while( item && theState != NoChange ) { + if ( item->rtti() == 1 && + ( ((TQCheckListItem*)item)->type() == CheckBox || + ((TQCheckListItem*)item)->type() == CheckBoxController ) ) { + TQCheckListItem *checkItem = (TQCheckListItem*)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 TQCheckListItem::updateStoredState( void *key ) +{ + if ( myType != CheckBoxController ) + return; + + TQListViewItem *item = firstChild(); + while( item ) { + if ( item->rtti() == 1 ) { + TQCheckListItem *checkItem = (TQCheckListItem*)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 TQCheckListItem::setup() +{ + TQListViewItem::setup(); + int h = height(); + TQListView *lv = listView(); + if ( lv ) + h = TQMAX( lv->style().pixelMetric(TQStyle::PM_CheckListButtonSize, lv), + h ); + h = TQMAX( h, TQApplication::globalStrut().height() ); + setHeight( h ); +} + +/*! + \reimp +*/ + +int TQCheckListItem::width( const TQFontMetrics& fm, const TQListView* lv, int column) const +{ + int r = TQListViewItem::width( fm, lv, column ); + if ( column == 0 ) { + r += lv->itemMargin(); + if ( myType == RadioButtonController && pixmap( 0 ) ) { + // r += 0; + } else { + r += lv->style().pixelMetric(TQStyle::PM_CheckListButtonSize, lv) + 4; + } + } + return TQMAX( r, TQApplication::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 TQt::AlignmentFlags for valid alignments.) +*/ +void TQCheckListItem::paintCell( TQPainter * p, const TQColorGroup & cg, + int column, int width, int align ) +{ + if ( !p ) + return; + + TQListView *lv = listView(); + if ( !lv ) + return; + + const BackgroundMode bgmode = lv->viewport()->backgroundMode(); + const TQColorGroup::ColorRole crole = TQPalette::backgroundRoleFromMode( bgmode ); + if ( cg.brush( crole ) != lv->colorGroup().brush( crole ) ) + p->fillRect( 0, 0, width, height(), cg.brush( crole ) ); + else + lv->paintEmptyArea( p, TQRect( 0, 0, width, height() ) ); + + if ( column != 0 ) { + // The rest is text, or for subclasses to change. + TQListViewItem::paintCell( p, cg, column, width, align ); + return; + } + + bool parentControl = FALSE; + if ( parent() && parent()->rtti() == 1 && + ((TQCheckListItem*) parent())->type() == RadioButtonController ) + parentControl = TRUE; + + TQFontMetrics fm( lv->fontMetrics() ); + int boxsize = lv->style().pixelMetric( myType == RadioButtonController ? TQStyle::PM_CheckListControllerSize : + TQStyle::PM_CheckListButtonSize, lv); + int marg = lv->itemMargin(); + int r = marg; + + // Draw controller / checkbox / radiobutton --------------------- + int styleflags = TQStyle::Style_Default; + if ( internalState() == On ) { + styleflags |= TQStyle::Style_On; + } else if ( internalState() == NoChange ) { + if ( myType == CheckBoxController && !isTristate() ) + styleflags |= TQStyle::Style_Off; + else + styleflags |= TQStyle::Style_NoChange; + } else { + styleflags |= TQStyle::Style_Off; + } + if ( isSelected() ) + styleflags |= TQStyle::Style_Selected; + if ( isEnabled() && lv->isEnabled() ) + styleflags |= TQStyle::Style_Enabled; + + if ( myType == RadioButtonController ) { + int x = 0; + if(!parentControl) + x += 3; + if ( !pixmap( 0 ) ) { + lv->style().drawPrimitive(TQStyle::PE_CheckListController, p, + TQRect(x, 0, boxsize, + fm.height() + 2 + marg), + cg, styleflags, TQStyleOption(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(TQStyle::PE_CheckListIndicator, p, + TQRect(x, y, boxsize, + fm.height() + 2 + marg), + cg, styleflags, TQStyleOption(this)); + } else { //radio button look + lv->style().drawPrimitive(TQStyle::PE_CheckListExclusiveIndicator, + p, TQRect(x, y, boxsize, + fm.height() + 2 + marg), + cg, styleflags, TQStyleOption(this)); + } + r += boxsize + 4; + } + + // Draw text ---------------------------------------------------- + p->translate( r, 0 ); + p->setPen( TQPen( cg.text() ) ); + TQListViewItem::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 TQCheckListItem::paintFocus( TQPainter *p, const TQColorGroup & cg, + const TQRect & r ) +{ + bool intersect = TRUE; + TQListView *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( TQRect( p, r.y(), xdepth - p + 1, r.height() ) ); + } + bool parentControl = FALSE; + if ( parent() && parent()->rtti() == 1 && + ((TQCheckListItem*) parent())->type() == RadioButtonController ) + parentControl = TRUE; + if ( myType != RadioButtonController && intersect && + (lv->rootIsDecorated() || myType == RadioButton || + (myType == CheckBox && parentControl) ) ) { + TQRect rect; + int boxsize = lv->style().pixelMetric(TQStyle::PM_CheckListButtonSize, lv); + if ( lv->columnAlignment(0) == AlignCenter ) { + TQFontMetrics 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() ); + TQListViewItem::paintFocus(p, cg, rect); + } else { + TQListViewItem::paintFocus(p, cg, r); + } +} + +/*! + \reimp +*/ +TQSize TQListView::sizeHint() const +{ + if ( cachedSizeHint().isValid() ) + return cachedSizeHint(); + + constPolish(); + + if ( !isVisible() && (!d->drawables || d->drawables->isEmpty()) ) + // force the column widths to sanity, if possible + buildDrawableList(); + + TQSize s( d->h->sizeHint() ); + if ( verticalScrollBar()->isVisible() ) + s.setWidth( s.width() + style().pixelMetric(TQStyle::PM_ScrollBarExtent) ); + s += TQSize(frameWidth()*2,frameWidth()*2); + TQListViewItem * 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 +*/ + +TQSize TQListView::minimumSizeHint() const +{ + return TQScrollView::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 TQListViewItem::setOpen() TQListViewItem::setExpandable() +*/ + +void TQListView::setOpen( TQListViewItem * item, bool open ) +{ + if ( !item || + item->isOpen() == open || + (open && !item->childCount() && !item->isExpandable()) ) + return; + + TQListViewItem* nextParent = 0; + if ( open ) + nextParent = item->itemBelow(); + + item->setOpen( open ); + + if ( open ) { + TQListViewItem* lastChild = item; + TQListViewItem* tmp; + while ( TRUE ) { + tmp = lastChild->itemBelow(); + if ( !tmp || tmp == nextParent ) + break; + lastChild = tmp; + } + ensureItemVisible( lastChild ); + ensureItemVisible( item ); + } + if ( d->drawables ) + d->drawables->clear(); + buildDrawableList(); + + TQListViewPrivate::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 TQPtrDict<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 TQListView::isOpen( const TQListViewItem * item ) const +{ + return item->isOpen(); +} + + +/*! + \property TQListView::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 TQListView::setRootIsDecorated( bool enable ) +{ + if ( enable != (bool)d->rootIsExpandable ) { + d->rootIsExpandable = enable; + if ( isVisible() ) + triggerUpdate(); + } +} + +bool TQListView::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 retquired to show the item. + + \sa itemRect() TQScrollView::ensureVisible() +*/ + +void TQListView::ensureItemVisible( const TQListViewItem * i ) +{ + if ( !i || !i->isVisible() ) + return; + + TQListViewItem *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 TQString TQCheckListItem::text( int n ) const + + \reimp +*/ + +/*! + Returns the TQHeader object that manages this list view's columns. + Please don't modify the header behind the list view's back. + + You may safely call TQHeader::setClickEnabled(), + TQHeader::setResizeEnabled(), TQHeader::setMovingEnabled(), + TQHeader::hide() and all the const TQHeader functions. +*/ + +TQHeader * TQListView::header() const +{ + return d->h; +} + + +/*! + \property TQListView::childCount + \brief the number of parentless (top-level) TQListViewItem objects in this TQListView + + Holds the current number of parentless (top-level) TQListViewItem + objects in this TQListView. + + \sa TQListViewItem::childCount() +*/ + +int TQListView::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 TQListViewItem::moveToJustAfter( TQListViewItem * olderSibling ) +{ + if ( parentItem && olderSibling && + olderSibling->parentItem == parentItem && olderSibling != this ) { + if ( parentItem->childItem == this ) { + parentItem->childItem = siblingItem; + } else { + TQListViewItem * 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 TQListViewItem::moveItem( TQListViewItem *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 ); + TQListView *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 TQListViewItem::enforceSortOrderBackToRoot() +{ + if ( parentItem ) { + parentItem->enforceSortOrderBackToRoot(); + parentItem->enforceSortOrder(); + } +} + +/*! + \reimp +*/ +void TQListView::showEvent( TQShowEvent * ) +{ + 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 + TQListView::itemAt(), but it works for all items whereas + TQListView::itemAt() normally only works for items on the screen. + + \sa TQListView::itemAt() TQListView::itemRect() TQListView::itemPos() +*/ + +int TQListViewItem::itemPos() const +{ + TQPtrStack<TQListViewItem> s; + TQListViewItem * i = (TQListViewItem *)this; + while( i ) { + s.push( i ); + i = i->parentItem; + } + + int a = 0; + TQListViewItem * 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(); + TQListViewItem * s = p->firstChild(); + while( s && s != i ) { + a += s->totalHeight(); + s = s->nextSibling(); + } + } + p = i; + } + return a; +} + + +/*! + \fn void TQListView::removeItem( TQListViewItem * ) + \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 TQListViewItem::takeItem() apply to + this function, too. + + \sa insertItem() +*/ +void TQListView::takeItem( TQListViewItem * i ) +{ + if ( d->r ) + d->r->takeItem( i ); +} + + +void TQListView::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 TQListView::contentsDragEnterEvent( TQDragEnterEvent *e ) +{ + d->oldFocusItem = d->focusItem; + TQListViewItem *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 TQListView::contentsDragMoveEvent( TQDragMoveEvent *e ) +{ + TQListViewItem *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 TQListView::contentsDragLeaveEvent( TQDragLeaveEvent * ) +{ + d->autoopenTimer->stop(); + + if ( d->focusItem ) + d->focusItem->dragLeft(); + + setCurrentItem( d->oldFocusItem ); + d->oldFocusItem = 0; +} + +/*! \reimp */ + +void TQListView::contentsDropEvent( TQDropEvent *e ) +{ + d->autoopenTimer->stop(); + + setCurrentItem( d->oldFocusItem ); + TQListViewItem *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 + TQListViewItem::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 TQDragObject depending on the selected items. +*/ + +TQDragObject *TQListView::dragObject() +{ + return 0; +} + +/*! + Starts a drag. +*/ + +void TQListView::startDrag() +{ + if ( !d->startDragItem ) + return; + + d->startDragItem = 0; + d->buttonDown = FALSE; + + TQDragObject *drag = dragObject(); + if ( !drag ) + return; + + drag->drag(); +} + +#endif // QT_NO_DRAGANDDROP + +/*! + \property TQListView::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 TQListView::setDefaultRenameAction( RenameAction a ) +{ + d->defRenameAction = a; +} + +TQListView::RenameAction TQListView::defaultRenameAction() const +{ + return d->defRenameAction; +} + +/*! + Returns TRUE if an item is being renamed; otherwise returns FALSE. +*/ + +bool TQListView::isRenaming() const +{ + return currentItem() && currentItem()->renameBox; +} + +/********************************************************************** + * + * Class TQListViewItemIterator + * + **********************************************************************/ + + +/*! + \class TQListViewItemIterator + \brief The TQListViewItemIterator class provides an iterator for collections of TQListViewItems. + + \ingroup advanced + + Construct an instance of a TQListViewItemIterator, with either a + TQListView* or a TQListViewItem* as argument, to operate on the tree + of TQListViewItems, starting from the argument. + + A TQListViewItemIterator 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 + TQPtrList: + \code + TQPtrList<TQListViewItem> lst; + TQListViewItemIterator 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 + TQPtrList<TQListViewItem> lst; + TQListViewItemIterator it( myListView, TQListViewItemIterator::Selected ); + while ( it.current() ) { + lst.append( it.current() ); + ++it; + } + \endcode + + A TQListViewItemIterator provides a convenient and easy way to + traverse a hierarchical TQListView. + + Multiple TQListViewItemIterators can operate on the tree of + TQListViewItems. A TQListView knows about all iterators operating on + its TQListViewItems. So when a TQListViewItem 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 TQListView, TQListViewItem +*/ + +/*! + \enum TQListViewItemIterator::IteratorFlag + + These flags can be passed to a TQListViewItemIterator 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. +*/ + +TQListViewItemIterator::TQListViewItemIterator() + : curr( 0 ), listView( 0 ) +{ + init( 0 ); +} + +/*! + Constructs an iterator for the TQListView that contains the \a + item. The current iterator item is set to point to the \a item. +*/ + +TQListViewItemIterator::TQListViewItemIterator( TQListViewItem *item ) + : curr( item ), listView( 0 ) +{ + init( 0 ); + + if ( item ) { + item->enforceSortOrderBackToRoot(); + listView = item->listView(); + } + addToListView(); +} + +/*! + Constructs an iterator for the TQListView 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 TQListViewItemIterator::IteratorFlag +*/ + +TQListViewItemIterator::TQListViewItemIterator( TQListViewItem *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 TQListView as \a it. The + current iterator item is set to point on the current item of \a + it. +*/ + +TQListViewItemIterator::TQListViewItemIterator( const TQListViewItemIterator& it ) + : curr( it.curr ), listView( it.listView ) +{ + init(it.d() ? it.d()->flags : 0); + + addToListView(); +} + +/*! + Constructs an iterator for the TQListView \a lv. The current + iterator item is set to point on the first child (TQListViewItem) + of \a lv. +*/ + +TQListViewItemIterator::TQListViewItemIterator( TQListView *lv ) + : curr( lv->firstChild() ), listView( lv ) +{ + init( 0 ); + + addToListView(); +} + +/*! + Constructs an iterator for the TQListView \a lv with the flags \a + iteratorFlags. The current iterator item is set to point on the + first child (TQListViewItem) of \a lv that matches the flags. + + \sa TQListViewItemIterator::IteratorFlag +*/ + +TQListViewItemIterator::TQListViewItemIterator( TQListView *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. +*/ + +TQListViewItemIterator &TQListViewItemIterator::operator=( const TQListViewItemIterator &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. +*/ + +TQListViewItemIterator::~TQListViewItemIterator() +{ + 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 + TQListView is 0. +*/ + +TQListViewItemIterator &TQListViewItemIterator::operator++() +{ + do { + if ( !curr ) + return *this; + + TQListViewItem *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 TQListViewItemIterator TQListViewItemIterator::operator++( int ) +{ + TQListViewItemIterator 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. +*/ + +TQListViewItemIterator &TQListViewItemIterator::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 + TQListView is 0. +*/ + +TQListViewItemIterator &TQListViewItemIterator::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 + TQListViewItem *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 + TQListViewItemIterator 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 { + TQListViewItem *parent = curr->parent(); + + if ( curr != parent->firstChild() ) { + // go to the previous sibling + TQListViewItem *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 + TQListViewItemIterator 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 TQListViewItemIterator TQListViewItemIterator::operator--( int ) +{ + TQListViewItemIterator 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. +*/ + +TQListViewItemIterator &TQListViewItemIterator::operator-=( int j ) +{ + while ( curr && j-- ) + --( *this ); + + return *this; +} + +/*! + Dereference operator. Returns a reference to the current item. The + same as current(). +*/ + +TQListViewItem* TQListViewItemIterator::operator*() +{ + if ( curr != 0 && !matchesFlags( curr ) ) + qWarning( "TQListViewItemIterator::operator*() curr out of sync" ); + return curr; +} + +/*! + Returns iterator's current item. +*/ + +TQListViewItem *TQListViewItemIterator::current() const +{ + if ( curr != 0 && !matchesFlags( curr ) ) + qWarning( "TQListViewItemIterator::current() curr out of sync" ); + return curr; +} + +TQListViewItemIteratorPrivate* TQListViewItemIterator::d() const +{ + return qt_iteratorprivate_dict ? + qt_iteratorprivate_dict->find( (void *)this ) : 0; +} + +void TQListViewItemIterator::init( int iteratorFlags ) +{ + // makes new global ptrdict if it doesn't exist + if ( !qt_iteratorprivate_dict ) { + qt_iteratorprivate_dict = new TQPtrDict<TQListViewItemIteratorPrivate>; + qt_iteratorprivate_dict->setAutoDelete( TRUE ); + } + + // sets flag, or inserts new TQListViewItemIteratorPrivate with flag + if ( d() ) + d()->flags = iteratorFlags; + else + qt_iteratorprivate_dict->insert( this, new TQListViewItemIteratorPrivate( iteratorFlags ) ); +} + + +/* + Adds this iterator to its TQListView's list of iterators. +*/ + +void TQListViewItemIterator::addToListView() +{ + if ( listView ) { + if ( !listView->d->iterators ) { + listView->d->iterators = new TQPtrList<TQListViewItemIterator>; + 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 TQListViewItemIterator::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 TQListViewItemIterator::matchesFlags( const TQListViewItem *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 TQCheckListItems as well, so we provide this convenience function + that checks if the rtti() is 1 which means TQCheckListItem and if isOn is TRUE, returns FALSE otherwise. +*/ +bool TQListViewItemIterator::isChecked( const TQListViewItem *item ) const +{ + if ( item->rtti() == 1 ) + return ((const TQCheckListItem*)item)->isOn(); + else return FALSE; +} + +void TQListView::handleItemChange( TQListViewItem *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 TQListView::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 TQListView::clearRange( TQListViewItem *from, TQListViewItem *to, bool includeFirst ) +{ + if ( !from || !to ) + return FALSE; + + // Swap + if ( from->itemPos() > to->itemPos() ) { + TQListViewItem *temp = from; + from = to; + to = temp; + } + + // Start on second? + if ( !includeFirst ) { + TQListViewItem *below = (from == to) ? from : from->itemBelow(); + if ( below ) + from = below; + } + + // Clear items <from, to> + bool changed = FALSE; + + TQListViewItemIterator 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 TQListView::selectRange( TQListViewItem *from, TQListViewItem *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() ) { + TQListViewItemIterator it( from ); + bool found = FALSE; + for ( ; it.current(); ++it ) { + if ( it.current() == to ) { + found = TRUE; + break; + } + } + if ( !found ) + swap = TRUE; + } + if ( swap ) { + TQListViewItem *i = from; + from = to; + to = i; + if ( !includeFirst ) + to = to->itemAbove(); + } else { + if ( !includeFirst ) + from = from->itemBelow(); + } + + bool changed = FALSE; + if ( clearSel ) { + TQListViewItemIterator it( firstChild() ); + for ( ; it.current(); ++it ) { + if ( it.current()->selected ) { + it.current()->setSelected( FALSE ); + changed = TRUE; + } + } + it = TQListViewItemIterator( to ); + for ( ; it.current(); ++it ) { + if ( it.current()->selected ) { + it.current()->setSelected( FALSE ); + changed = TRUE; + } + } + } + + for ( TQListViewItem *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 TQListView::selectRange( TQListViewItem *newItem, TQListViewItem *oldItem, TQListViewItem *anchorItem ) +{ + if ( !newItem || !oldItem || !anchorItem ) + return FALSE; + + int anchorPos = anchorItem ? anchorItem->itemPos() : 0, + oldPos = oldItem ? oldItem->itemPos() : 0, + newPos = newItem->itemPos(); + TQListViewItem *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 + TQListViewItemIterator 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 TQt::StringComparisonMode values + in the \a compare flag, to control how the matching is performed. + The default comparison mode is case-sensitive, exact match. +*/ + +TQListViewItem *TQListView::findItem( const TQString& text, int column, + ComparisonFlags compare ) const +{ + if (text.isEmpty() && !(compare & ExactMatch)) + return 0; + + if ( compare == CaseSensitive || compare == 0 ) + compare |= ExactMatch; + + TQString itmtxt; + TQString comtxt = text; + if ( !(compare & CaseSensitive) ) + comtxt = comtxt.lower(); + + TQListViewItemIterator it( d->focusItem ? d->focusItem : firstChild() ); + TQListViewItem *sentinel = 0; + TQListViewItem *item; + TQListViewItem *beginsWithItem = 0; + TQListViewItem *endsWithItem = 0; + TQListViewItem *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 = TQListViewItemIterator( 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 TQListView::windowActivationChange( bool oldActive ) +{ + if ( oldActive && d->scrollTimer ) + d->scrollTimer->stop(); + if ( palette().active() != palette().inactive() ) + viewport()->update(); + TQScrollView::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 TQListView::hideColumn( int column ) +{ + setColumnWidth( column, 0 ); +} + +/*! Adjusts the column \a col to its preferred width */ + +void TQListView::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 = TQMAX( w, 20 ); + TQFontMetrics fm( fontMetrics() ); + TQListViewItem* 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 = TQMAX( w, iw ); + item = item->itemBelow(); + } + w = TQMAX( w, TQApplication::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 000000000..f29b21561 --- /dev/null +++ b/src/widgets/qlistview.h @@ -0,0 +1,605 @@ +/**************************************************************************** +** +** Definition of TQListView widget class +** +** Created : 970809 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQLISTVIEW_H +#define TQLISTVIEW_H + +#ifndef QT_H +#include "qscrollview.h" +#endif // QT_H + +#ifndef QT_NO_LISTVIEW + + +class TQPixmap; +class TQFont; +class TQHeader; +class TQIconSet; + +class TQListView; +struct TQListViewPrivate; +struct TQCheckListItemPrivate; +class TQListViewItemIterator; +struct TQListViewItemIteratorPrivate; +class TQDragObject; +class TQMimeSource; +class TQLineEdit; +class TQListViewToolTip; + +class Q_EXPORT TQListViewItem : public TQt +{ + friend class TQListViewItemIterator; + friend class TQListViewToolTip; + +public: + TQListViewItem( TQListView * parent ); + TQListViewItem( TQListViewItem * parent ); + TQListViewItem( TQListView * parent, TQListViewItem * after ); + TQListViewItem( TQListViewItem * parent, TQListViewItem * after ); + + TQListViewItem( TQListView * parent, + TQString, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null ); + TQListViewItem( TQListViewItem * parent, + TQString, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null ); + + TQListViewItem( TQListView * parent, TQListViewItem * after, + TQString, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null ); + TQListViewItem( TQListViewItem * parent, TQListViewItem * after, + TQString, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null, + TQString = TQString::null, TQString = TQString::null ); + virtual ~TQListViewItem(); + + virtual void insertItem( TQListViewItem * ); + virtual void takeItem( TQListViewItem * ); + virtual void removeItem( TQListViewItem *item ) { takeItem( item ); } //obsolete, use takeItem instead + + int height() const; + virtual void invalidateHeight(); + int totalHeight() const; + virtual int width( const TQFontMetrics&, + const TQListView*, int column) const; + void widthChanged(int column=-1) const; + int depth() const; + + virtual void setText( int, const TQString &); + virtual TQString text( int ) const; + + virtual void setPixmap( int, const TQPixmap & ); + virtual const TQPixmap * pixmap( int ) const; + + virtual TQString key( int, bool ) const; + virtual int compare( TQListViewItem *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( TQPainter *, const TQColorGroup & cg, + int column, int width, int alignment ); + virtual void paintBranches( TQPainter * p, const TQColorGroup & cg, + int w, int y, int h ); + virtual void paintFocus( TQPainter *, const TQColorGroup & cg, + const TQRect & r ); + + TQListViewItem * firstChild() const; + TQListViewItem * nextSibling() const { return siblingItem; } + TQListViewItem * parent() const; + + TQListViewItem * itemAbove(); + TQListViewItem * itemBelow(); + + int itemPos() const; + + TQListView *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( TQListViewItem *after ); + + virtual void setDragEnabled( bool allow ); + virtual void setDropEnabled( bool allow ); + bool dragEnabled() const; + bool dropEnabled() const; + virtual bool acceptDrop( const TQMimeSource *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; + // ### TQt 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( TQPoint & ); +#ifndef QT_NO_DRAGANDDROP + virtual void dropped( TQDropEvent *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( TQListViewItem * ); + 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; + + TQListViewItem * parentItem; + TQListViewItem * siblingItem; + TQListViewItem * childItem; + TQLineEdit *renameBox; + int renameCol; + + void * columns; + + friend class TQListView; +}; + +class TQCheckListItem; + +class Q_EXPORT TQListView: public TQScrollView +{ + friend class TQListViewItemIterator; + friend class TQListViewItem; + friend class TQCheckListItem; + friend class TQListViewToolTip; + + 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: + TQListView( TQWidget* parent=0, const char* name=0, WFlags f = 0 ); + ~TQListView(); + + int treeStepSize() const; + virtual void setTreeStepSize( int ); + + virtual void insertItem( TQListViewItem * ); + virtual void takeItem( TQListViewItem * ); + virtual void removeItem( TQListViewItem *item ) { takeItem( item ); } // obsolete, use takeItem instead + + TQHeader * header() const; + + virtual int addColumn( const TQString &label, int size = -1); + virtual int addColumn( const TQIconSet& iconset, const TQString &label, int size = -1); + virtual void removeColumn( int index ); + virtual void setColumnText( int column, const TQString &label ); + virtual void setColumnText( int column, const TQIconSet& iconset, const TQString &label ); + TQString 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(); + + TQListViewItem * itemAt( const TQPoint & screenPos ) const; + TQRect itemRect( const TQListViewItem * ) const; + int itemPos( const TQListViewItem * ); + + void ensureItemVisible( const TQListViewItem * ); + + void repaintItem( const TQListViewItem * ) 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( TQListViewItem *, bool ); + void setSelectionAnchor( TQListViewItem * ); + bool isSelected( const TQListViewItem * ) const; + TQListViewItem * selectedItem() const; + virtual void setOpen( TQListViewItem *, bool ); + bool isOpen( const TQListViewItem * ) const; + + virtual void setCurrentItem( TQListViewItem * ); + TQListViewItem * currentItem() const; + + TQListViewItem * firstChild() const; + TQListViewItem * 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 TQFont & ); + virtual void setPalette( const TQPalette & ); + + bool eventFilter( TQObject * o, TQEvent * ); + + TQSize sizeHint() const; + TQSize 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; + + TQListViewItem * findItem( const TQString& 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( TQListViewItem * ); + void currentChanged( TQListViewItem * ); + void clicked( TQListViewItem * ); + void clicked( TQListViewItem *, const TQPoint &, int ); + void pressed( TQListViewItem * ); + void pressed( TQListViewItem *, const TQPoint &, int ); + + void doubleClicked( TQListViewItem * ); + void doubleClicked( TQListViewItem *, const TQPoint&, int ); + void returnPressed( TQListViewItem * ); + void spacePressed( TQListViewItem * ); + void rightButtonClicked( TQListViewItem *, const TQPoint&, int ); + void rightButtonPressed( TQListViewItem *, const TQPoint&, int ); + void mouseButtonPressed( int, TQListViewItem *, const TQPoint& , int ); + void mouseButtonClicked( int, TQListViewItem *, const TQPoint&, int ); + + void contextMenuRequested( TQListViewItem *, const TQPoint &, int ); + + void onItem( TQListViewItem *item ); + void onViewport(); + + void expanded( TQListViewItem *item ); + void collapsed( TQListViewItem *item ); +#ifndef QT_NO_DRAGANDDROP + void dropped( TQDropEvent *e ); +#endif + void itemRenamed( TQListViewItem *item, int col, const TQString & ); + void itemRenamed( TQListViewItem *item, int col ); + +protected: + void contentsMousePressEvent( TQMouseEvent * e ); + void contentsMouseReleaseEvent( TQMouseEvent * e ); + void contentsMouseMoveEvent( TQMouseEvent * e ); + void contentsMouseDoubleClickEvent( TQMouseEvent * e ); + void contentsContextMenuEvent( TQContextMenuEvent * e ); +#ifndef QT_NO_DRAGANDDROP + void contentsDragEnterEvent( TQDragEnterEvent *e ); + void contentsDragMoveEvent( TQDragMoveEvent *e ); + void contentsDragLeaveEvent( TQDragLeaveEvent *e ); + void contentsDropEvent( TQDropEvent *e ); + virtual TQDragObject *dragObject(); + virtual void startDrag(); +#endif + + void focusInEvent( TQFocusEvent * e ); + void focusOutEvent( TQFocusEvent * e ); + + void keyPressEvent( TQKeyEvent *e ); + + void resizeEvent( TQResizeEvent *e ); + void viewportResizeEvent( TQResizeEvent *e ); + + void showEvent( TQShowEvent * ); + + void drawContentsOffset( TQPainter *, int ox, int oy, + int cx, int cy, int cw, int ch ); + + virtual void paintEmptyArea( TQPainter *, const TQRect & ); + void styleChange( TQStyle& ); + 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( TQMouseEvent * e ); + void contentsMouseReleaseEventEx( TQMouseEvent * e ); + void init(); + void updateGeometries(); + void buildDrawableList() const; + void reconfigureItems(); + void widthChanged(const TQListViewItem*, int c); + void handleItemChange( TQListViewItem *old, bool shift, bool control ); + void selectRange( TQListViewItem *from, TQListViewItem *to, bool invert, bool includeFirst, bool clearSel = FALSE ); + bool selectRange( TQListViewItem *newItem, TQListViewItem *oldItem, TQListViewItem *anchorItem ); + bool clearRange( TQListViewItem *from, TQListViewItem *to, bool includeFirst = TRUE ); + void doAutoScroll( const TQPoint &cursorPos ); + + TQListViewPrivate * d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQListView( const TQListView & ); + TQListView &operator=( const TQListView & ); +#endif +}; + + +class Q_EXPORT TQCheckListItem : public TQListViewItem +{ +public: + enum Type { RadioButton, + CheckBox, + Controller, + RadioButtonController=Controller, + CheckBoxController }; + // ### should be integrated with qbutton in ver4 perhaps + enum ToggleState { Off, NoChange, On }; + + TQCheckListItem( TQCheckListItem *parent, const TQString &text, + Type = RadioButtonController ); + TQCheckListItem( TQCheckListItem *parent, TQListViewItem *after, + const TQString &text, Type = RadioButtonController ); + TQCheckListItem( TQListViewItem *parent, const TQString &text, + Type = RadioButtonController ); + TQCheckListItem( TQListViewItem *parent, TQListViewItem *after, + const TQString &text, Type = RadioButtonController ); + TQCheckListItem( TQListView *parent, const TQString &text, + Type = RadioButtonController ); + TQCheckListItem( TQListView *parent, TQListViewItem *after, + const TQString &text, Type = RadioButtonController ); + TQCheckListItem( TQListViewItem *parent, const TQString &text, + const TQPixmap & ); + TQCheckListItem( TQListView *parent, const TQString &text, + const TQPixmap & ); + ~TQCheckListItem(); + + void paintCell( TQPainter *, const TQColorGroup & cg, + int column, int width, int alignment ); + virtual void paintFocus( TQPainter *, const TQColorGroup & cg, + const TQRect & r ); + int width( const TQFontMetrics&, const TQListView*, 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; } + TQString text() const { return TQListViewItem::text( 0 ); } + TQString text( int n ) const { return TQListViewItem::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 + TQCheckListItemPrivate *d; +}; + +class Q_EXPORT TQListViewItemIterator +{ + friend struct TQListViewPrivate; + friend class TQListView; + friend class TQListViewItem; + +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 + }; + + TQListViewItemIterator(); + TQListViewItemIterator( TQListViewItem *item ); + TQListViewItemIterator( TQListViewItem *item, int iteratorFlags ); + + TQListViewItemIterator( const TQListViewItemIterator &it ); + TQListViewItemIterator( TQListView *lv ); + TQListViewItemIterator( TQListView *lv, int iteratorFlags ); + + TQListViewItemIterator &operator=( const TQListViewItemIterator &it ); + + ~TQListViewItemIterator(); + + TQListViewItemIterator &operator++(); + const TQListViewItemIterator operator++( int ); + TQListViewItemIterator &operator+=( int j ); + + TQListViewItemIterator &operator--(); + const TQListViewItemIterator operator--( int ); + TQListViewItemIterator &operator-=( int j ); + + TQListViewItem* operator*(); + TQListViewItem *current() const; + +protected: + TQListViewItem *curr; + TQListView *listView; + +private: + TQListViewItemIteratorPrivate* d() const; + void init( int flags ); + void addToListView(); + void currentRemoved(); + bool matchesFlags( const TQListViewItem* ) const; + bool testPair( TQListViewItemIterator::IteratorFlag, TQListViewItemIterator::IteratorFlag, bool ) const; + bool isChecked( const TQListViewItem* ) const; +}; + +#endif // QT_NO_LISTVIEW + +#endif // TQLISTVIEW_H diff --git a/src/widgets/qmainwindow.cpp b/src/widgets/qmainwindow.cpp new file mode 100644 index 000000000..5b8b6ab38 --- /dev/null +++ b/src/widgets/qmainwindow.cpp @@ -0,0 +1,2609 @@ +/**************************************************************************** +** +** Implementation of TQMainWindow class +** +** Created : 980312 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQHideDock; +class TQMainWindowLayout; + +class TQMainWindowPrivate +{ +public: + TQMainWindowPrivate() + : 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( TQt::DockTop, TRUE ); + docks.insert( TQt::DockBottom, TRUE ); + docks.insert( TQt::DockLeft, TRUE ); + docks.insert( TQt::DockRight, TRUE ); + docks.insert( TQt::DockMinimized, FALSE ); + docks.insert( TQt::DockTornOff, TRUE ); + } + + ~TQMainWindowPrivate() + { + } + +#ifndef QT_NO_MENUBAR + TQMenuBar * mb; +#else + TQWidget * mb; +#endif + TQStatusBar * sb; + TQToolTipGroup * ttg; + + TQWidget * mc; + + TQBoxLayout * tll; + TQMainWindowLayout * mwl; + + uint ubp :1; + uint utl :1; + uint justify :1; + uint movable :1; + uint opaque :1; + uint dockMenu :1; + + TQDockArea *topDock, *bottomDock, *leftDock, *rightDock; + + TQPtrList<TQDockWindow> dockWindows; + TQMap<TQt::Dock, bool> docks; + TQStringList disabledDocks; + TQHideDock *hideDock; + + TQGuardedPtr<TQPopupMenu> rmbMenu, tbMenu, dwMenu; + TQMap<TQDockWindow*, bool> appropriate; + TQMap<TQPopupMenu*, TQMainWindow::DockWindows> dockWindowModes; + +}; + + +/* TQMainWindowLayout, respects widthForHeight layouts (like the left + and right docks are) +*/ + +class TQMainWindowLayout : public TQLayout +{ + Q_OBJECT + +public: + TQMainWindowLayout( TQMainWindow *mw, TQLayout* parent = 0 ); + ~TQMainWindowLayout() {} + + void addItem( TQLayoutItem * ); + void setLeftDock( TQDockArea *l ); + void setRightDock( TQDockArea *r ); + void setCentralWidget( TQWidget *w ); + bool hasHeightForWidth() const { return FALSE; } + TQSize sizeHint() const; + TQSize minimumSize() const; + TQLayoutIterator iterator(); + TQSizePolicy::ExpandData expanding() const { return TQSizePolicy::BothDirections; } + void invalidate() {} + +protected: + void setGeometry( const TQRect &r ) { + TQLayout::setGeometry( r ); + layoutItems( r ); + } + +private: + int layoutItems( const TQRect&, bool testonly = FALSE ); + int extraPixels() const; + + TQDockArea *left, *right; + TQWidget *central; + TQMainWindow *mainWindow; + +}; + +TQSize TQMainWindowLayout::sizeHint() const +{ + int w = 0; + int h = 0; + + if ( left ) { + w += left->sizeHint().width(); + h = TQMAX( h, left->sizeHint().height() ); + } + if ( right ) { + w += right->sizeHint().width(); + h = TQMAX( h, right->sizeHint().height() ); + } + if ( central ) { + w += central->sizeHint().width(); + int diff = extraPixels(); + h = TQMAX( h, central->sizeHint().height() + diff ); + } + return TQSize( w, h ); +} + +TQSize TQMainWindowLayout::minimumSize() const +{ + int w = 0; + int h = 0; + + if ( left ) { + TQSize ms = left->minimumSizeHint().expandedTo( left->minimumSize() ); + w += ms.width(); + h = TQMAX( h, ms.height() ); + } + if ( right ) { + TQSize ms = right->minimumSizeHint().expandedTo( right->minimumSize() ); + w += ms.width(); + h = TQMAX( h, ms.height() ); + } + if ( central ) { + TQSize min = central->minimumSize().isNull() ? + central->minimumSizeHint() : central->minimumSize(); + w += min.width(); + int diff = extraPixels(); + h = TQMAX( h, min.height() + diff ); + } + return TQSize( w, h ); +} + +TQMainWindowLayout::TQMainWindowLayout( TQMainWindow *mw, TQLayout* parent ) + : TQLayout( parent ), left( 0 ), right( 0 ), central( 0 ) +{ + mainWindow = mw; +} + +void TQMainWindowLayout::setLeftDock( TQDockArea *l ) +{ + left = l; +} + +void TQMainWindowLayout::setRightDock( TQDockArea *r ) +{ + right = r; +} + +void TQMainWindowLayout::setCentralWidget( TQWidget *w ) +{ + central = w; +} + +int TQMainWindowLayout::layoutItems( const TQRect &r, bool testonly ) +{ + if ( !left && !central && !right ) + return 0; + + int wl = 0, wr = 0; + if ( left ) + wl = ( (TQDockAreaLayout*)left->TQWidget::layout() )->widthForHeight( r.height() ); + if ( right ) + wr = ( (TQDockAreaLayout*)right->TQWidget::layout() )->widthForHeight( r.height() ); + int w = r.width() - wr - wl; + if ( w < 0 ) + w = 0; + + int diff = extraPixels(); + if ( !testonly ) { + TQRect g( geometry() ); + if ( left ) + left->setGeometry( TQRect( g.x(), g.y() + diff, wl, r.height() - diff ) ); + if ( right ) + right->setGeometry( TQRect( 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 TQMainWindowLayout::extraPixels() const +{ + if ( mainWindow->d->topDock->isEmpty() && + !(mainWindow->d->leftDock->isEmpty() && + mainWindow->d->rightDock->isEmpty()) ) { + return 2; + } else { + return 0; + } +} + +void TQMainWindowLayout::addItem( TQLayoutItem * /* item */ ) +{ +} + + +TQLayoutIterator TQMainWindowLayout::iterator() +{ + return 0; +} + + +/* + TQHideToolTip and TQHideDock - minimized dock +*/ + +#ifndef QT_NO_TOOLTIP +class TQHideToolTip : public TQToolTip +{ +public: + TQHideToolTip( TQWidget *parent ) : TQToolTip( parent ) {} + ~TQHideToolTip() {} + + virtual void maybeTip( const TQPoint &pos ); +}; +#endif + + +class TQHideDock : public TQWidget +{ + Q_OBJECT + +public: + TQHideDock( TQMainWindow *parent ) : TQWidget( parent, "qt_hide_dock" ) { + hide(); + setFixedHeight( style().pixelMetric( TQStyle::PM_DockWindowHandleExtent, + this ) + 3 ); + pressedHandle = -1; + pressed = FALSE; + setMouseTracking( TRUE ); + win = parent; +#ifndef QT_NO_TOOLTIP + tip = new TQHideToolTip( this ); +#endif + } + ~TQHideDock() + { +#ifndef QT_NO_TOOLTIP + delete tip; +#endif + } + +protected: + void paintEvent( TQPaintEvent *e ) { + if ( !children() || children()->isEmpty() ) + return; + TQPainter p( this ); + p.setClipRegion( e->rect() ); + p.fillRect( e->rect(), colorGroup().brush( TQColorGroup::Background ) ); + int x = 0; + int i = -1; + TQObjectListIt it( *children() ); + TQObject *o; + while ( ( o = it.current() ) ) { + ++it; + ++i; + TQDockWindow *dw = ::qt_cast<TQDockWindow*>(o); + if ( !dw || !dw->isVisible() ) + continue; + + TQStyle::SFlags flags = TQStyle::Style_Default; + if ( i == pressedHandle ) + flags |= TQStyle::Style_On; + + style().drawPrimitive( TQStyle::PE_DockWindowHandle, &p, + TQRect( x, 0, 30, 10 ), colorGroup(), + flags ); + x += 30; + } + } + + void mousePressEvent( TQMouseEvent *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( TQMouseEvent *e ) { + if ( !children() || children()->isEmpty() ) + return; + if ( !pressed ) + return; + int x = 0; + int i = -1; + if ( e->y() >= 0 && e->y() <= height() ) { + TQObjectListIt it( *children() ); + TQObject *o; + while ( ( o = it.current() ) ) { + ++it; + ++i; + TQDockWindow *dw = ::qt_cast<TQDockWindow*>(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( TQMouseEvent *e ) { + pressed = FALSE; + if ( pressedHandle == -1 ) + return; + if ( !children() || children()->isEmpty() ) + return; + if ( e->button() == LeftButton ) { + if ( e->y() >= 0 && e->y() <= height() ) { + TQObject *o = ( (TQObjectList*)children() )->at( pressedHandle ); + TQDockWindow *dw = ::qt_cast<TQDockWindow*>(o); + if ( dw ) { + dw->show(); + dw->dock(); + } + } + } + pressedHandle = -1; + repaint( FALSE ); + } + + bool eventFilter( TQObject *o, TQEvent *e ) { + if ( o == this || !o->isWidgetType() ) + return TQWidget::eventFilter( o, e ); + if ( e->type() == TQEvent::Hide || + e->type() == TQEvent::Show || + e->type() == TQEvent::ShowToParent ) + updateState(); + return TQWidget::eventFilter( o, e ); + } + + void updateState() { + bool visible = TRUE; + if ( !children() || children()->isEmpty() ) { + visible = FALSE; + } else { + TQObjectListIt it( *children() ); + TQObject *o; + while ( ( o = it.current() ) ) { + ++it; + TQDockWindow *dw = ::qt_cast<TQDockWindow*>(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( TQChildEvent *e ) { + TQWidget::childEvent( e ); + if ( e->type() == TQEvent::ChildInserted ) + e->child()->installEventFilter( this ); + else + e->child()->removeEventFilter( this ); + updateState(); + } + +private: + TQMainWindow *win; + int pressedHandle; + bool pressed; +#ifndef QT_NO_TOOLTIP + TQHideToolTip *tip; + friend class TQHideToolTip; +#endif +}; + +#ifndef QT_NO_TOOLTIP +void TQHideToolTip::maybeTip( const TQPoint &pos ) +{ + if ( !parentWidget() ) + return; + TQHideDock *dock = (TQHideDock*)parentWidget(); + + if ( !dock->children() || dock->children()->isEmpty() ) + return; + TQObjectListIt it( *dock->children() ); + TQObject *o; + int x = 0; + while ( ( o = it.current() ) ) { + ++it; + TQDockWindow *dw = ::qt_cast<TQDockWindow*>(o); + if ( !dw || !dw->isVisible() ) + continue; + + if ( pos.x() >= x && pos.x() <= x + 30 ) { + TQDockWindow *dw = (TQDockWindow*)o; + if ( !dw->caption().isEmpty() ) + tip( TQRect( x, 0, 30, dock->height() ), dw->caption() ); + return; + } + x += 30; + } +} +#endif + +/*! + \class TQMainWindow qmainwindow.h + \brief The TQMainWindow 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 TQWorkspace (for MDI applications). TQMainWindow + 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 TQt + Designer\endlink. We'll briefly review adding menu items and + toolbar buttons then describe the facilities of TQMainWindow + itself. + + \code + TQMainWindow *mw = new TQMainWindow; + TQTextEdit *edit = new TQTextEdit( mw, "editor" ); + edit->setFocus(); + mw->setCaption( "Main Window" ); + mw->setCentralWidget( edit ); + mw->show(); + \endcode + + TQMainWindows 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 + TQMainWindow that we must write for ourselves; this is the usual + approach to using TQMainWindow. (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 TQMainWindow instance + directly we can add menu items and toolbars just as easily by + passing the TQMainWindow 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 TQMainWindow 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 TQToolButton + \printuntil open file + + This extract shows the creation of a toolbar with one toolbar + button. TQMainWindow supplies four dock areas for toolbars. When a + toolbar is created as a child of a TQMainWindow (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 TQMainWindow + 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 TQStatusBar 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 TQAction * 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 TQPopupMenu * file + \printuntil menuBar + \skipto fileOpen + \printline + + The extract above shows the creation of a popup menu. We add the + menu to the TQMainWindow's menu bar and add our action. + + \quotefile action/application.cpp + \skipto TQToolBar * fileTool + \printuntil OpenAction + + Here we create a new toolbar as a child of the TQMainWindow and add + our action to the toolbar. + + We'll now explore the functionality offered by TQMainWindow. + + 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. TQMainWindow automatically detects the creation of + a menu bar or status bar if you specify the TQMainWindow 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. + + TQMainWindow provides a TQToolTipGroup connected to the status bar. + The function toolTipGroup() provides access to the default + TQToolTipGroup. It isn't possible to set a different tool tip + group. + + New dock windows and toolbars can be added to a TQMainWindow using + addDockWindow(). Dock windows can be moved using moveDockWindow() + and removed with removeDockWindow(). TQMainWindow 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 TQt::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. + TQToolbar is a subclass of TQDockWindow 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 retquired 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{TQMainWindow::DockWindows}.) + + Some functions change the appearance of a TQMainWindow globally: + \list + \i TQDockWindow::setHorizontalStretchable() and + TQDockWindow::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 TQIconSet for more information). + \i setUsesTextLabel() is used to set whether tool buttons + should display a textual label in addition to pixmaps + (see TQToolButton 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 + TQDockWindow::caption() or TQToolBar::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 TQMenuBar 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 + TQToolBar *tb = new TQToolBar( this ); + addDockWindow( tb, tr( "Menubar" ), Top, FALSE ); + TQMenuBar *mb = new TQMenuBar( tb ); + mb->setFrameStyle( TQFrame::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 TQMainWindow. + + To save the layout and positions of all the dock windows do this: + + \code + TQFile file( filename ); + if ( file.open( IO_WriteOnly ) ) { + TQTextStream 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 + TQFile file( filename ); + if ( file.open( IO_ReadOnly ) ) { + TQTextStream stream( &file ); + stream >> *mainWindow; + file.close(); + } + \endcode + + The TQSettings class can be used in conjunction with the streaming + operators to store the application's settings. + + TQMainWindow's management of dock windows and toolbars is done + transparently behind-the-scenes by TQDockArea. + + For multi-document interfaces (MDI), use a TQWorkspace as the + central widget. + + Adding dock windows, e.g. toolbars, to TQMainWindow's dock areas is + straightforward. If the supplied dock areas are not sufficient for + your application we suggest that you create a TQWidget subclass and + add your own dock areas (see \l TQDockArea) to the subclass since + TQMainWindow provides functionality specific to the standard dock + areas it provides. + + <img src=qmainwindow-m.png> <img src=qmainwindow-w.png> + + \sa TQToolBar TQDockWindow TQStatusBar TQAction TQMenuBar TQPopupMenu TQToolTipGroup TQDialog +*/ + +/*! \enum TQt::ToolBarDock + \internal +*/ + +/*! + \enum TQt::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 TQMainWindow. +*/ + +/*! + \enum TQMainWindow::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 TQMainWindow::addToolBar( TQDockWindow *, Dock = Top, bool newLine = FALSE ); +*/ + +/*! + \obsolete + \overload void TQMainWindow::addToolBar( TQDockWindow *, const TQString &label, Dock = Top, bool newLine = FALSE ); +*/ + +/*! + \obsolete + \fn void TQMainWindow::moveToolBar( TQDockWindow *, Dock = Top ); +*/ + +/*! + \obsolete + \overload void TQMainWindow::moveToolBar( TQDockWindow *, Dock, bool nl, int index, int extraOffset = -1 ); +*/ + +/*! + \obsolete + \fn void TQMainWindow::removeToolBar( TQDockWindow * ); +*/ + +/*! + \obsolete + \fn void TQMainWindow::lineUpToolBars( bool keepNewLines = FALSE ); +*/ + +/*! + \obsolete + \fn void TQMainWindow::toolBarPositionChanged( TQToolBar * ); +*/ + +/*! + \obsolete + \fn bool TQMainWindow::toolBarsMovable() const +*/ + +/*! + \obsolete + \fn void TQMainWindow::setToolBarsMovable( bool ) +*/ + +/*! + Constructs an empty main window. The \a parent, \a name and widget + flags \a f, are passed on to the TQWidget constructor. + + By default, the widget flags are set to \c WType_TopLevel rather + than 0 as they are with TQWidget. If you don't want your + TQMainWindow to be a top level widget then you will need to set \a + f to 0. +*/ + +TQMainWindow::TQMainWindow( TQWidget * parent, const char * name, WFlags f ) + : TQWidget( parent, name, f ) +{ + d = new TQMainWindowPrivate; +#ifdef Q_WS_MACX + d->opaque = TRUE; +#else + d->opaque = FALSE; +#endif + installEventFilter( this ); + d->topDock = new TQDockArea( Horizontal, TQDockArea::Normal, this, "qt_top_dock" ); + d->topDock->installEventFilter( this ); + d->bottomDock = new TQDockArea( Horizontal, TQDockArea::Reverse, this, "qt_bottom_dock" ); + d->bottomDock->installEventFilter( this ); + d->leftDock = new TQDockArea( Vertical, TQDockArea::Normal, this, "qt_left_dock" ); + d->leftDock->installEventFilter( this ); + d->rightDock = new TQDockArea( Vertical, TQDockArea::Reverse, this, "qt_right_dock" ); + d->rightDock->installEventFilter( this ); + d->hideDock = new TQHideDock( this ); +} + + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQMainWindow::~TQMainWindow() +{ + 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 TQMainWindow::setMenuBar( TQMenuBar * 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() +*/ + +TQMenuBar * TQMainWindow::menuBar() const +{ + if ( d->mb ) + return d->mb; + + TQObjectList * l + = ((TQObject*)this)->queryList( "TQMenuBar", 0, FALSE, FALSE ); + TQMenuBar * b; + if ( l && l->count() ) { + b = (TQMenuBar *)l->first(); + } else { + b = new TQMenuBar( (TQMainWindow *)this, "automatic menu bar" ); + b->show(); + } + delete l; + d->mb = b; + d->mb->installEventFilter( this ); + ((TQMainWindow *)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 TQMainWindow::setStatusBar( TQStatusBar * 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 TQString&)), + d->sb, SLOT(message(const TQString&)) ); + 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() +*/ + +TQStatusBar * TQMainWindow::statusBar() const +{ + if ( d->sb ) + return d->sb; + + TQObjectList * l + = ((TQObject*)this)->queryList( "TQStatusBar", 0, FALSE, FALSE ); + TQStatusBar * s; + if ( l && l->count() ) { + s = (TQStatusBar *)l->first(); + } else { + s = new TQStatusBar( (TQMainWindow *)this, "automatic status bar" ); + s->show(); + } + delete l; + ((TQMainWindow *)this)->setStatusBar( s ); + ((TQMainWindow *)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 TQMainWindow::setToolTipGroup( TQToolTipGroup * newToolTipGroup ) +{ + if ( !newToolTipGroup || newToolTipGroup == d->ttg ) + return; + if ( d->ttg ) + delete d->ttg; + d->ttg = newToolTipGroup; + + connect( toolTipGroup(), SIGNAL(showTip(const TQString&)), + statusBar(), SLOT(message(const TQString&)) ); + 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() +*/ + +TQToolTipGroup * TQMainWindow::toolTipGroup() const +{ + if ( d->ttg ) + return d->ttg; + + TQToolTipGroup * t = new TQToolTipGroup( (TQMainWindow*)this, + "automatic tool tip group" ); + ((TQMainWindowPrivate*)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 TQMainWindow::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 TQMainWindow::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 TQMainWindow::isDockEnabled( TQDockArea *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 TQMainWindow::setDockEnabled( TQDockWindow *dw, Dock dock, bool enable ) +{ + if ( d->dockWindows.find( dw ) == -1 ) { + d->dockWindows.append( dw ); + connect( dw, SIGNAL( placeChanged(TQDockWindow::Place) ), + this, SLOT( slotPlaceChanged() ) ); + } + TQString 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 TQMainWindow::isDockEnabled( TQDockWindow *dw, TQDockArea *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 TQMainWindow::isDockEnabled( TQDockWindow *tb, Dock dock ) const +{ + if ( !isDockEnabled( dock ) ) + return FALSE; + TQString 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 TQMainWindow::addDockWindow( TQDockWindow *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(TQDockWindow::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 TQMainWindow::addDockWindow( TQDockWindow * dockWindow, const TQString &label, + Dock edge, bool newLine ) +{ + addDockWindow( dockWindow, edge, newLine ); +#ifndef QT_NO_TOOLBAR + TQToolBar *tb = ::qt_cast<TQToolBar*>(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 TQMainWindow::moveDockWindow( TQDockWindow * 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 TQMainWindow::moveDockWindow( TQDockWindow * 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 TQMainWindow::removeDockWindow( TQDockWindow * 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(TQDockWindow::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 TQMainWindow::setUpLayout() +{ +#ifndef QT_NO_MENUBAR + if ( !d->mb ) { + // slightly evil hack here. reconsider this + TQObjectList * l + = ((TQObject*)this)->queryList( "TQMenuBar", 0, FALSE, FALSE ); + if ( l && l->count() ) + d->mb = menuBar(); + delete l; + } +#endif + if ( !d->sb ) { + // as above. + TQObjectList * l + = ((TQObject*)this)->queryList( "TQStatusBar", 0, FALSE, FALSE ); + if ( l && l->count() ) + d->sb = statusBar(); + delete l; + } + + if (!d->tll) { + d->tll = new TQBoxLayout( this, TQBoxLayout::Down ); + d->tll->setResizeMode( minimumSize().isNull() ? TQLayout::Minimum : TQLayout::FreeResize ); + } else { + d->tll->setMenuBar( 0 ); + TQLayoutIterator it = d->tll->iterator(); + TQLayoutItem *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(TQStyle::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 ); + + TQMainWindowLayout *mwl = new TQMainWindowLayout( 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 TQMainWindow::show() +{ + if ( !d->tll ) + setUpLayout(); + + // show all floating dock windows not explicitly hidden + if (!isVisible()) { + TQPtrListIterator<TQDockWindow> it(d->dockWindows); + while ( it.current() ) { + TQDockWindow *dw = it.current(); + ++it; + if ( dw->isTopLevel() && !dw->isVisible() && !dw->testWState(WState_ForceHide) ) + dw->show(); + } + } + + // show us last so we get focus + TQWidget::show(); +} + + +/*! \reimp +*/ +void TQMainWindow::hide() +{ + if ( isVisible() ) { + TQPtrListIterator<TQDockWindow> it(d->dockWindows); + while ( it.current() ) { + TQDockWindow *dw = it.current(); + ++it; + if ( dw->isTopLevel() && dw->isVisible() ) { + dw->hide(); // implicit hide, so clear forcehide + ((TQMainWindow*)dw)->clearWState(WState_ForceHide); + } + } + } + + TQWidget::hide(); +} + + +/*! \reimp */ +TQSize TQMainWindow::sizeHint() const +{ + TQMainWindow* that = (TQMainWindow*) this; + // Workaround: because d->tll get's deleted in + // totalSizeHint->polish->sendPostedEvents->childEvent->triggerLayout + // [eg. canvas example on TQt/Embedded] + TQApplication::sendPostedEvents( that, TQEvent::ChildInserted ); + if ( !that->d->tll ) + that->setUpLayout(); + return that->d->tll->totalSizeHint(); +} + +/*! \reimp */ +TQSize TQMainWindow::minimumSizeHint() const +{ + if ( !d->tll ) { + TQMainWindow* that = (TQMainWindow*) 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 TQMainWindow::setCentralWidget( TQWidget * 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() +*/ + +TQWidget * TQMainWindow::centralWidget() const +{ + return d->mc; +} + + +/*! \reimp */ + +void TQMainWindow::paintEvent( TQPaintEvent * ) +{ + if (d->mb && + style().styleHint(TQStyle::SH_MainWindow_SpaceBelowMenuBar, this)) { + TQPainter p( this ); + int y = d->mb->height() + 1; + style().drawPrimitive(TQStyle::PE_Separator, &p, TQRect(0, y, width(), 1), + colorGroup(), TQStyle::Style_Sunken); + } +} + + +bool TQMainWindow::dockMainWindow( TQObject *dock ) +{ + while ( dock ) { + if ( dock->parent() && dock->parent() == this ) + return TRUE; + if ( ::qt_cast<TQMainWindow*>(dock->parent()) ) + return FALSE; + dock = dock->parent(); + } + return FALSE; +} + +/*! + \reimp +*/ + +bool TQMainWindow::eventFilter( TQObject* o, TQEvent *e ) +{ + if ( e->type() == TQEvent::Show && o == this ) { + if ( !d->tll ) + setUpLayout(); + d->tll->activate(); + } else if ( e->type() == TQEvent::ContextMenu && d->dockMenu && + ( ::qt_cast<TQDockArea*>(o) && dockMainWindow( o ) || o == d->hideDock || o == d->mb ) ) { + if ( showDockMenu( ( (TQMouseEvent*)e )->globalPos() ) ) { + ( (TQContextMenuEvent*)e )->accept(); + return TRUE; + } + } + + return TQWidget::eventFilter( o, e ); +} + + +/*! + Monitors events, recieved in \a e, to ensure the layout is updated. +*/ +void TQMainWindow::childEvent( TQChildEvent* e) +{ + if ( e->type() == TQEvent::ChildRemoved ) { + if ( e->child() == 0 || + !e->child()->isWidgetType() || + ((TQWidget*)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<TQDockWindow*>(e->child()) ) { + removeDockWindow( (TQDockWindow *)(e->child()) ); + d->appropriate.remove( (TQDockWindow*)e->child() ); + triggerLayout(); + } + } else if ( e->type() == TQEvent::ChildInserted && !d->sb ) { + d->sb = ::qt_cast<TQStatusBar*>(e->child()); + if ( d->sb ) { + if ( d->tll ) { + if ( !d->tll->findWidget( d->sb ) ) + d->tll->addWidget( d->sb ); + } else { + triggerLayout(); + } + } + } +} + +/*! + \reimp +*/ + +bool TQMainWindow::event( TQEvent * e ) +{ + if ( e->type() == TQEvent::ChildRemoved && ( (TQChildEvent*)e )->child() == d->mc ) { + d->mc->removeEventFilter( this ); + d->mc = 0; + d->mwl->setCentralWidget( 0 ); + } + + return TQWidget::event( e ); +} + + +/*! + \property TQMainWindow::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 TQMainWindow::usesBigPixmaps() const +{ + return d->ubp; +} + +void TQMainWindow::setUsesBigPixmaps( bool enable ) +{ + if ( enable == (bool)d->ubp ) + return; + + d->ubp = enable; + emit pixmapSizeChanged( enable ); + + TQObjectList *l = queryList( "TQLayout" ); + if ( !l || !l->first() ) { + delete l; + return; + } + for ( TQLayout *lay = (TQLayout*)l->first(); lay; lay = (TQLayout*)l->next() ) + lay->activate(); + delete l; +} + +/*! + \property TQMainWindow::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 TQToolButton::setUsesTextLabel() +*/ + +bool TQMainWindow::usesTextLabel() const +{ + return d->utl; +} + + +void TQMainWindow::setUsesTextLabel( bool enable ) +{ + if ( enable == (bool)d->utl ) + return; + + d->utl = enable; + emit usesTextLabelChanged( enable ); + + TQObjectList *l = queryList( "TQLayout" ); + if ( !l || !l->first() ) { + delete l; + return; + } + for ( TQLayout *lay = (TQLayout*)l->first(); lay; lay = (TQLayout*)l->next() ) + lay->activate(); + delete l; +} + + +/*! + \fn void TQMainWindow::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 TQMainWindow::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 TQMainWindow::dockWindowPositionChanged( TQDockWindow *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 TQMainWindow::setRightJustification( bool enable ) +{ + if ( enable == (bool)d->justify ) + return; + d->justify = enable; + triggerLayout( TRUE ); +} + + +/*! + \obsolete + \property TQMainWindow::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 TQDockWindow::setVerticalStretchable(), TQDockWindow::setHorizontalStretchable() +*/ + +bool TQMainWindow::rightJustification() const +{ + return d->justify; +} + +/*! \internal + */ + +void TQMainWindow::triggerLayout( bool deleteLayout ) +{ + if ( deleteLayout || !d->tll ) + setUpLayout(); + TQApplication::postEvent( this, new TQEvent( TQEvent::LayoutHint ) ); +} + +/*! + Enters 'What's This?' mode and returns immediately. + + This is the same as TQWhatsThis::enterWhatsThisMode(), but + implemented as a main window object's slot. This way it can easily + be used for popup menus, for example: + + \code + TQPopupMenu * help = new TQPopupMenu( this ); + help->insertItem( "What's &This", this , SLOT(whatsThis()), SHIFT+Key_F1); + \endcode + + \sa TQWhatsThis::enterWhatsThisMode() +*/ +void TQMainWindow::whatsThis() +{ +#ifndef QT_NO_WHATSTHIS + TQWhatsThis::enterWhatsThisMode(); +#endif +} + + +/*! + \reimp +*/ + +void TQMainWindow::styleChange( TQStyle& old ) +{ + TQWidget::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 TQMainWindow::getLocation( TQDockWindow *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() +*/ + +TQPtrList<TQToolBar> TQMainWindow::toolBars( Dock dock ) const +{ + TQPtrList<TQDockWindow> lst = dockWindows( dock ); + TQPtrList<TQToolBar> tbl; + for ( TQDockWindow *w = lst.first(); w; w = lst.next() ) { + TQToolBar *tb = ::qt_cast<TQToolBar*>(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. +*/ + +TQPtrList<TQDockWindow> TQMainWindow::dockWindows( Dock dock ) const +{ + TQPtrList<TQDockWindow> 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 ( TQDockWindow *w = d->dockWindows.first(); w; w = d->dockWindows.next() ) { + if ( !w->area() && w->place() == TQDockWindow::OutsideDock ) + lst.append( w ); + } + } + return lst; + case DockMinimized: { + if ( d->hideDock->children() ) { + TQObjectListIt it( *d->hideDock->children() ); + TQObject *o; + while ( ( o = it.current() ) ) { + ++it; + TQDockWindow *dw = ::qt_cast<TQDockWindow*>(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). +*/ + +TQPtrList<TQDockWindow> TQMainWindow::dockWindows() const +{ + return d->dockWindows; +} + +void TQMainWindow::setDockWindowsMovable( bool enable ) +{ + d->movable = enable; + TQObjectList *l = queryList( "TQDockWindow" ); + if ( l ) { + for ( TQObject *o = l->first(); o; o = l->next() ) + ( (TQDockWindow*)o )->setMovingEnabled( enable ); + } + delete l; +} + +/*! + \property TQMainWindow::dockWindowsMovable + \brief whether the dock windows are movable + + If TRUE (the default), the user will be able to move movable dock + windows from one TQMainWindow 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 TQMainWindow 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 TQMainWindow::dockWindowsMovable() const +{ + return d->movable; +} + +void TQMainWindow::setOpaqueMoving( bool b ) +{ + d->opaque = b; + TQObjectList *l = queryList( "TQDockWindow" ); + if ( l ) { + for ( TQObject *o = l->first(); o; o = l->next() ) + ( (TQDockWindow*)o )->setOpaqueMoving( b ); + } + delete l; +} + +/*! + \property TQMainWindow::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 TQMainWindow::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 TQMainWindow::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 TQMainWindow::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 TQMainWindow::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 +### TQt 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. +*/ + +TQPopupMenu *TQMainWindow::createDockWindowMenu( DockWindows dockWindows ) const +{ + TQObjectList *l = queryList( "TQDockWindow" ); + + if ( !l || l->isEmpty() ) + return 0; + + delete l; + + TQPopupMenu *menu = new TQPopupMenu( (TQMainWindow*)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 +### TQt 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 TQMainWindow::menuAboutToShow() +{ + TQPopupMenu *menu = (TQPopupMenu*)sender(); + TQMap<TQPopupMenu*, DockWindows>::Iterator it = d->dockWindowModes.find( menu ); + if ( it == d->dockWindowModes.end() ) + return; + menu->clear(); + + DockWindows dockWindows = *it; + + TQObjectList *l = queryList( "TQDockWindow" ); + + bool empty = TRUE; + if ( l && !l->isEmpty() ) { + + TQObject *o = 0; + if ( dockWindows == AllDockWindows || dockWindows == NoToolBars ) { + for ( o = l->first(); o; o = l->next() ) { + TQDockWindow *dw = (TQDockWindow*)o; + if ( !appropriate( dw ) || ::qt_cast<TQToolBar*>(dw) || !dockMainWindow( dw ) ) + continue; + TQString 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() ) { + TQToolBar *tb = ::qt_cast<TQToolBar*>(o); + if ( !tb || !appropriate(tb) || !dockMainWindow(tb) ) + continue; + TQString 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 +### TQt 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 TQMainWindow::showDockMenu( const TQPoint &globalPos ) +{ + if ( !d->dockMenu ) + return FALSE; + if ( !d->rmbMenu ) + d->rmbMenu = createDockWindowMenu(); + if ( !d->rmbMenu ) + return FALSE; + + d->rmbMenu->exec( globalPos ); + return TRUE; +} + +void TQMainWindow::slotPlaceChanged() +{ + TQObject* obj = (TQObject*)sender(); + TQDockWindow *dw = ::qt_cast<TQDockWindow*>(obj); + if ( dw ) + emit dockWindowPositionChanged( dw ); +#ifndef QT_NO_TOOLBAR + TQToolBar *tb = ::qt_cast<TQToolBar*>(obj); + if ( tb ) + emit toolBarPositionChanged( tb ); +#endif +} + +/*! + \internal + For internal use of TQDockWindow only. + */ + +TQDockArea *TQMainWindow::dockingArea( const TQPoint &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 TQMainWindow::hasDockWindow( TQDockWindow *dw ) +{ + return d->dockWindows.findRef( dw ) != -1; +} + +/*! + Returns the \c Left dock area + + \sa rightDock() topDock() bottomDock() +*/ + +TQDockArea *TQMainWindow::leftDock() const +{ + return d->leftDock; +} + +/*! + Returns the \c Right dock area + + \sa leftDock() topDock() bottomDock() +*/ + +TQDockArea *TQMainWindow::rightDock() const +{ + return d->rightDock; +} + +/*! + Returns the \c Top dock area + + \sa bottomDock() leftDock() rightDock() +*/ + +TQDockArea *TQMainWindow::topDock() const +{ + return d->topDock; +} + +/*! + Returns a pointer the \c Bottom dock area + + \sa topDock() leftDock() rightDock() +*/ + +TQDockArea *TQMainWindow::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 TQMainWindow::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 TQMainWindow::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 TQMainWindow::appropriate( TQDockWindow *dw ) const +{ + TQMap<TQDockWindow*, 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 TQMainWindow::setAppropriate( TQDockWindow *dw, bool a ) +{ + d->appropriate.replace( dw, a ); +} + +#ifndef QT_NO_TEXTSTREAM +static void saveDockArea( TQTextStream &ts, TQDockArea *a ) +{ + TQPtrList<TQDockWindow> l = a->dockWindowList(); + for ( TQDockWindow *dw = l.first(); dw; dw = l.next() ) { + ts << TQString( dw->caption() ); + ts << ","; + } + ts << endl; + ts << *a; +} + +/*! + \relates TQMainWindow + + Writes the layout (sizes and positions) of the dock windows in the + dock areas of the TQMainWindow \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 TQSettings to + save the user's layout when the \mainWindow receives a closeEvent. + + \sa operator>>() closeEvent() +*/ + +TQTextStream &operator<<( TQTextStream &ts, const TQMainWindow &mainWindow ) +{ + TQPtrList<TQDockWindow> l = mainWindow.dockWindows( TQt::DockMinimized ); + TQDockWindow *dw = 0; + for ( dw = l.first(); dw; dw = l.next() ) { + ts << dw->caption(); + ts << ","; + } + ts << endl; + + l = mainWindow.dockWindows( TQt::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 TQStringList &names, TQDockArea *a, TQt::Dock d, TQPtrList<TQDockWindow> &l, TQMainWindow *mw, TQTextStream &ts ) +{ + for ( TQStringList::ConstIterator it = names.begin(); it != names.end(); ++it ) { + for ( TQDockWindow *dw = l.first(); dw; dw = l.next() ) { + if ( dw->caption() == *it ) { + mw->addDockWindow( dw, d ); + break; + } + } + } + if ( a ) { + ts >> *a; + } else if ( d == TQt::DockTornOff ) { + TQString s = ts.readLine(); + enum State { Pre, Name, X, Y, Width, Height, Visible, Post }; + int state = Pre; + TQString name, x, y, w, h, visible; + TQChar 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 ( TQDockWindow *dw = l.first(); dw; dw = l.next() ) { + if ( TQString( dw->caption() ) == name ) { + if ( !::qt_cast<TQToolBar*>(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 TQMainWindow + + Reads the layout (sizes and positions) of the dock windows in the + dock areas of the TQMainWindow \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 TQSettings to + restore the user's layout. + + \sa operator<<() +*/ + +TQTextStream &operator>>( TQTextStream &ts, TQMainWindow &mainWindow ) +{ + TQPtrList<TQDockWindow> l = mainWindow.dockWindows(); + + TQString s = ts.readLine(); + TQStringList names = TQStringList::split( ',', s ); + loadDockArea( names, 0, TQt::DockMinimized, l, &mainWindow, ts ); + + s = ts.readLine(); + names = TQStringList::split( ',', s ); + loadDockArea( names, 0, TQt::DockTornOff, l, &mainWindow, ts ); + + int i = 0; + TQDockArea *areas[] = { mainWindow.topDock(), mainWindow.bottomDock(), mainWindow.rightDock(), mainWindow.leftDock() }; + for ( int d = (int)TQt::DockTop; d != (int)TQt::DockMinimized; ++d, ++i ) { + s = ts.readLine(); + names = TQStringList::split( ',', s ); + loadDockArea( names, areas[ i ], (TQt::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 000000000..853e021f9 --- /dev/null +++ b/src/widgets/qmainwindow.h @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Definition of TQMainWindow class +** +** Created : 980316 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQMAINWINDOW_H +#define TQMAINWINDOW_H + +#ifndef QT_H +#include "qwidget.h" +#include "qtoolbar.h" +#include "qptrlist.h" +#include "qtextstream.h" +#endif // QT_H + +#ifndef QT_NO_MAINWINDOW + +class TQMenuBar; +class TQStatusBar; +class TQToolTipGroup; +class TQMainWindowPrivate; +class TQMainWindowLayout; +class TQPopupMenu; + +class Q_EXPORT TQMainWindow: public TQWidget +{ + 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: + TQMainWindow( TQWidget* parent=0, const char* name=0, WFlags f = WType_TopLevel ); + ~TQMainWindow(); + +#ifndef QT_NO_MENUBAR + TQMenuBar * menuBar() const; +#endif + TQStatusBar * statusBar() const; +#ifndef QT_NO_TOOLTIP + TQToolTipGroup * toolTipGroup() const; +#endif + + virtual void setCentralWidget( TQWidget * ); + TQWidget * centralWidget() const; + + virtual void setDockEnabled( Dock dock, bool enable ); + bool isDockEnabled( Dock dock ) const; + bool isDockEnabled( TQDockArea *area ) const; + virtual void setDockEnabled( TQDockWindow *tb, Dock dock, bool enable ); + bool isDockEnabled( TQDockWindow *tb, Dock dock ) const; + bool isDockEnabled( TQDockWindow *tb, TQDockArea *area ) const; + + virtual void addDockWindow( TQDockWindow *, Dock = DockTop, bool newLine = FALSE ); + virtual void addDockWindow( TQDockWindow *, const TQString &label, + Dock = DockTop, bool newLine = FALSE ); + virtual void moveDockWindow( TQDockWindow *, Dock = DockTop ); + virtual void moveDockWindow( TQDockWindow *, Dock, bool nl, int index, int extraOffset = -1 ); + virtual void removeDockWindow( TQDockWindow * ); + + void show(); + void hide(); + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + + bool rightJustification() const; + bool usesBigPixmaps() const; + bool usesTextLabel() const; + bool dockWindowsMovable() const; + bool opaqueMoving() const; + + bool eventFilter( TQObject*, TQEvent* ); + + bool getLocation( TQDockWindow *tb, Dock &dock, int &index, bool &nl, int &extraOffset ) const; + + TQPtrList<TQDockWindow> dockWindows( Dock dock ) const; + TQPtrList<TQDockWindow> dockWindows() const; + void lineUpDockWindows( bool keepNewLines = FALSE ); + + bool isDockMenuEnabled() const; + + // compatibility stuff + bool hasDockWindow( TQDockWindow *dw ); +#ifndef QT_NO_TOOLBAR + void addToolBar( TQDockWindow *, Dock = DockTop, bool newLine = FALSE ); + void addToolBar( TQDockWindow *, const TQString &label, + Dock = DockTop, bool newLine = FALSE ); + void moveToolBar( TQDockWindow *, Dock = DockTop ); + void moveToolBar( TQDockWindow *, Dock, bool nl, int index, int extraOffset = -1 ); + void removeToolBar( TQDockWindow * ); + + bool toolBarsMovable() const; + TQPtrList<TQToolBar> toolBars( Dock dock ) const; + void lineUpToolBars( bool keepNewLines = FALSE ); +#endif + + virtual TQDockArea *dockingArea( const TQPoint &p ); + TQDockArea *leftDock() const; + TQDockArea *rightDock() const; + TQDockArea *topDock() const; + TQDockArea *bottomDock() const; + + virtual bool isCustomizable() const; + + bool appropriate( TQDockWindow *dw ) const; + + enum DockWindows { OnlyToolBars, NoToolBars, AllDockWindows }; + TQPopupMenu *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( TQDockWindow *dw, bool a ); + virtual void customize(); + + // compatibility stuff + void setToolBarsMovable( bool ); + +signals: + void pixmapSizeChanged( bool ); + void usesTextLabelChanged( bool ); + void dockWindowPositionChanged( TQDockWindow * ); + +#ifndef QT_NO_TOOLBAR + // compatibility stuff + void toolBarPositionChanged( TQToolBar * ); +#endif + +protected slots: + virtual void setUpLayout(); + virtual bool showDockMenu( const TQPoint &globalPos ); + void menuAboutToShow(); + +protected: + void paintEvent( TQPaintEvent * ); + void childEvent( TQChildEvent * ); + bool event( TQEvent * ); + void styleChange( TQStyle& ); + +private slots: + void slotPlaceChanged(); + void doLineUp() { lineUpDockWindows( TRUE ); } + +private: + TQMainWindowPrivate * d; + void triggerLayout( bool deleteLayout = TRUE); + bool dockMainWindow( TQObject *dock ); + +#ifndef QT_NO_MENUBAR + virtual void setMenuBar( TQMenuBar * ); +#endif + virtual void setStatusBar( TQStatusBar * ); +#ifndef QT_NO_TOOLTIP + virtual void setToolTipGroup( TQToolTipGroup * ); +#endif + + friend class TQDockWindow; + friend class TQMenuBar; + friend class TQHideDock; + friend class TQToolBar; + friend class TQMainWindowLayout; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQMainWindow( const TQMainWindow & ); + TQMainWindow& operator=( const TQMainWindow & ); +#endif +}; + +#ifndef QT_NO_TOOLBAR +inline void TQMainWindow::addToolBar( TQDockWindow *w, ToolBarDock dock, bool newLine ) +{ + addDockWindow( w, dock, newLine ); +} + +inline void TQMainWindow::addToolBar( TQDockWindow *w, const TQString &label, + ToolBarDock dock, bool newLine ) +{ + addDockWindow( w, label, dock, newLine ); +} + +inline void TQMainWindow::moveToolBar( TQDockWindow *w, ToolBarDock dock ) +{ + moveDockWindow( w, dock ); +} + +inline void TQMainWindow::moveToolBar( TQDockWindow *w, ToolBarDock dock, bool nl, int index, int extraOffset ) +{ + moveDockWindow( w, dock, nl, index, extraOffset ); +} + +inline void TQMainWindow::removeToolBar( TQDockWindow *w ) +{ + removeDockWindow( w ); +} + +inline bool TQMainWindow::toolBarsMovable() const +{ + return dockWindowsMovable(); +} + +inline void TQMainWindow::lineUpToolBars( bool keepNewLines ) +{ + lineUpDockWindows( keepNewLines ); +} + +inline void TQMainWindow::setToolBarsMovable( bool b ) +{ + setDockWindowsMovable( b ); +} +#endif + +#ifndef QT_NO_TEXTSTREAM +Q_EXPORT TQTextStream &operator<<( TQTextStream &, const TQMainWindow & ); +Q_EXPORT TQTextStream &operator>>( TQTextStream &, TQMainWindow & ); +#endif + +#endif // QT_NO_MAINWINDOW + +#endif // TQMAINWINDOW_H diff --git a/src/widgets/qmenubar.cpp b/src/widgets/qmenubar.cpp new file mode 100644 index 000000000..e0a7c431b --- /dev/null +++ b/src/widgets/qmenubar.cpp @@ -0,0 +1,1683 @@ +/**************************************************************************** +** +** Implementation of TQMenuBar class +** +** Created : 941209 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +// 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 TQMenuDataData { + // attention: also defined in qmenudata.cpp +public: + TQMenuDataData(); + TQGuardedPtr<TQWidget> 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 TQMenuBar qmenubar.h + \brief The TQMenuBar 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 TQMenuData::insertItem() + insertItem()\endlink. For example, asuming that \c menubar is a + pointer to a TQMenuBar and \c filemenu is a pointer to a + TQPopupMenu, 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 TQPopupMenu + \printline + \skipto Key_O + \printline + \printline + \skipto new TQMenuBar + \printline + \skipto insertItem + \printline + + In most main window style applications you would use the menuBar() + provided in TQMainWindow, adding \l{TQPopupMenu}s to the menu bar + and adding \l{TQAction}s to the popup menus. + + Example (from \l action/application.cpp): + \quotefile action/application.cpp + \skipto file = new TQPopupMenu + \printuntil fileNewAction + + Menu items can have text and pixmaps (or iconsets), see the + various \link TQMenuData::insertItem() insertItem()\endlink + overloads, as well as separators, see \link + TQMenuData::insertSeparator() insertSeparator()\endlink. You can + also add custom menu items that are derived from + \l{TQCustomMenuItem}. + + Menu items may be removed with removeItem() and enabled or + disabled with \link TQMenuData::setItemEnabled() + setItemEnabled()\endlink. + + <img src=qmenubar-m.png> <img src=qmenubar-w.png> + + \section1 TQMenuBar on TQt/Mac + + TQMenuBar on TQt/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 TQt widgets \e cannot be inserted into a + TQMenuBar on the Mac because TQt uses Mac's native menus which don't + support this functionality. This limitation does not apply to + stand-alone TQPopupMenus. + + TQt/Mac also provides a menubar merging feature to make TQMenuBar + conform more closely to accepted Mac OS X menubar layout. The + merging functionality is based on string matching the title of a + TQPopupMenu entry. These strings are translated (using + TQObject::tr()) in the "TQMenuBar" 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 tquit or exit + \i Application Menu | Quit <application name> + \i If this entry is not found a default Quit item will be + created to call TQApplication::tquit() + \endtable + + \link menu-example.html menu/menu.cpp\endlink is an example of + TQMenuBar and TQPopupMenu use. + + \sa TQPopupMenu TQAccel TQAction \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 TQMenuBar::Separator + + This enum type is used to decide whether TQMenuBar 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 TQMenuBar::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 + TQMenuData::insertItem(), but sometimes you will want to connect + several items to a single slot (most often if the user selects + from an array). This signal is useful in such cases. + + \sa highlighted(), TQMenuData::insertItem() +*/ + +/*! + \fn void TQMenuBar::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 + TQMenuData::insertItem(), but sometimes you will want to connect + several items to a single slot (most often if the user selects + from an array). This signal is useful in such cases. + + \sa activated(), TQMenuData::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 +| | +| + +*/ + + +/***************************************************************************** + TQMenuBar member functions + *****************************************************************************/ + + +/*! + Constructs a menu bar called \a name with parent \a parent. +*/ +TQMenuBar::TQMenuBar( TQWidget *parent, const char *name ) + : TQFrame( parent, name, WNoAutoErase ) +{ +#if defined( Q_WS_MAC ) && !defined(TQMAC_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 + TQWidget *tlw = topLevelWidget(); + if ( tlw != parent ) + tlw->installEventFilter( this ); + } + installEventFilter( this ); + + setBackgroundMode( PaletteButton ); + setFrameStyle( TQFrame::MenuBarPanel | TQFrame::Raised ); + + TQFontMetrics fm = fontMetrics(); + + int h; + int gs = style().styleHint(TQStyle::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(TQStyle::SH_MenuBar_MouseTracking) ); +} + + + +/*! \reimp */ + +void TQMenuBar::styleChange( TQStyle& old ) +{ + setMouseTracking( style().styleHint(TQStyle::SH_MenuBar_MouseTracking) ); + TQFrame::styleChange( old ); +} + + + +/*! + Destroys the menu bar. +*/ + +TQMenuBar::~TQMenuBar() +{ +#ifndef QT_NO_ACCEL + delete autoaccel; +#endif +#if defined(Q_WS_MAC) && !defined(TQMAC_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 TQMenuBar::updateItem( int id ) +{ + int i = indexOf( id ); + if ( i >= 0 && irects ) + repaint( irects[i], FALSE ); +} + +#if defined(Q_WS_MAC) && !defined(TQMAC_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 + TQMenuData whenever it needs to be called. +*/ + +void TQMenuBar::menuContentsChanged() +{ + // here the part that can't be delayed + TQMenuData::menuContentsChanged(); + badSize = TRUE; // might change the size + if( pendingDelayedContentsChanges ) + return; + pendingDelayedContentsChanges = 1; + if( !pendingDelayedStateChanges )// if the timer hasn't been started yet + TQTimer::singleShot( 0, this, SLOT(performDelayedChanges())); +} + +void TQMenuBar::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 + TQMainWindow *mw = ::qt_cast<TQMainWindow*>(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 + TQMenuData whenever it needs to be called. +*/ + +void TQMenuBar::menuStateChanged() +{ + if( pendingDelayedStateChanges ) + return; + pendingDelayedStateChanges = 1; + if( !pendingDelayedContentsChanges ) // if the timer hasn't been started yet + TQTimer::singleShot( 0, this, SLOT(performDelayedChanges())); +} + +void TQMenuBar::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 TQMenuBar::performDelayedChanges() +{ +#if defined(Q_WS_MAC) && !defined(TQMAC_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(TQMAC_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 TQMenuBar::menuInsPopup( TQPopupMenu *popup ) +{ + connect( popup, SIGNAL(activatedRedirect(int)), + SLOT(subActivated(int)) ); + connect( popup, SIGNAL(highlightedRedirect(int)), + SLOT(subHighlighted(int)) ); + connect( popup, SIGNAL(destroyed(TQObject*)), + this, SLOT(popupDestroyed(TQObject*)) ); +} + +void TQMenuBar::menuDelPopup( TQPopupMenu *popup ) +{ + popup->disconnect( SIGNAL(activatedRedirect(int)) ); + popup->disconnect( SIGNAL(highlightedRedirect(int)) ); + disconnect( popup, SIGNAL(destroyed(TQObject*)), + this, SLOT(popupDestroyed(TQObject*)) ); +} + +void TQMenuBar::frameChanged() +{ +#if defined(Q_WS_MAC) && !defined(TQMAC_QMENUBAR_NO_NATIVE) + fromFrameChange = TRUE; +#endif + menuContentsChanged(); +#if defined(Q_WS_MAC) && !defined(TQMAC_QMENUBAR_NO_NATIVE) + fromFrameChange = FALSE; +#endif +} + +void TQMenuBar::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 + TQObject::eventFilter() is. + + Resizes the menu bar to fit in the parent widget when the parent + receives a resize event. +*/ + +bool TQMenuBar::eventFilter( TQObject *object, TQEvent *event ) +{ + if ( object == parent() && object +#ifndef QT_NO_TOOLBAR + && !::qt_cast<TQToolBar*>(object) +#endif + && event->type() == TQEvent::Resize ) { + TQResizeEvent *e = (TQResizeEvent *)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() == TQEvent::LanguageChange ) { + badSize = TRUE; + calculateRects(); + return FALSE; + } else if ( event->type() == TQEvent::MouseButtonPress || + event->type() == TQEvent::MouseButtonRelease ) { + waitforalt = 0; + return FALSE; + } else if ( waitforalt && event->type() == TQEvent::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 + TQWidget * f = ((TQWidget *)object)->focusWidget(); + if (f) + f->removeEventFilter( this ); + return FALSE; + } else if ( !( event->type() == TQEvent::Accel || + event->type() == TQEvent::AccelOverride || + event->type() == TQEvent::KeyPress || + event->type() == TQEvent::KeyRelease ) || + !style().styleHint(TQStyle::SH_MenuBar_AltKeyNavigation, this) ) { + return FALSE; + } + + TQKeyEvent * ke = (TQKeyEvent *) event; +#ifndef QT_NO_ACCEL + // look for Alt press and Alt-anything press + if ( event->type() == TQEvent::Accel ) { + TQWidget * f = ((TQWidget *)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) + TQMenuData::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 ( ((TQWidget*)object)->focusWidget() == object || + (object->parent() == 0 && ((TQWidget*)object)->focusWidget() == 0) ) { + if ( waitforalt && event->type() == TQEvent::KeyRelease && + ( ke->key() == Key_Alt || ke->key() == Key_Meta ) +#if defined(Q_WS_X11) + && TQMenuData::d->aInt == qt_xfocusout_grab_counter +#endif + ) { + setAltMode( TRUE ); + if ( object->parent() ) + object->removeEventFilter( this ); + TQWidget * tlw = ((TQWidget *)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() == TQEvent::AccelOverride ) && + !(((TQKeyEvent *)event)->key() == Key_Alt || + ((TQKeyEvent *)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 TQMenuBar::subActivated( int id ) +{ + emit activated( id ); +} + +/*! + \internal + Receives signals from menu items. +*/ + +void TQMenuBar::subHighlighted( int id ) +{ + emit highlighted( id ); +} + +/*! + \internal + Receives signals from menu accelerator. +*/ +#ifndef QT_NO_ACCEL +void TQMenuBar::accelActivated( int id ) +{ +#if defined(Q_WS_MAC) && !defined(TQMAC_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 TQMenuBar::accelDestroyed() +{ + autoaccel = 0; // don't delete it twice! +} +#endif + +void TQMenuBar::popupDestroyed( TQObject *o ) +{ + removePopup( (TQPopupMenu*)o ); +} + +bool TQMenuBar::tryMouseEvent( TQPopupMenu *, TQMouseEvent *e ) +{ + TQPoint pos = mapFromGlobal( e->globalPos() ); + if ( !rect().contains( pos ) ) // outside + return FALSE; + int item = itemAtPos( pos ); + if ( item == -1 && (e->type() == TQEvent::MouseButtonPress || + e->type() == TQEvent::MouseButtonRelease) ) { + hidePopups(); + goodbye(); + return FALSE; + } + TQMouseEvent ee( e->type(), pos, e->globalPos(), e->button(), e->state() ); + event( &ee ); + return TRUE; +} + + +void TQMenuBar::tryKeyEvent( TQPopupMenu *, TQKeyEvent *e ) +{ + event( e ); +} + + +void TQMenuBar::goodbye( bool cancelled ) +{ + mouseBtDn = FALSE; + popupvisible = 0; + setAltMode( cancelled && style().styleHint(TQStyle::SH_MenuBar_AltKeyNavigation, this) ); +} + + +void TQMenuBar::openActPopup() +{ +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( !inMenu ) { + TQAccessible::updateAccessibility( this, 0, TQAccessible::MenuStart ); + inMenu = TRUE; + } +#endif + + if ( actItem < 0 ) + return; + TQPopupMenu *popup = mitems->at(actItem)->popup(); + if ( !popup || !popup->isEnabled() ) + return; + + TQRect r = itemRect( actItem ); + bool reverse = TQApplication::reverseLayout(); + const int yoffset = 1; //(style().styleHint( TQStyle::SH_GUIStyle ) == TQStyle::WindowsStyle) ? 4 : 1; ### this breaks designer mainwindow editing + TQPoint pos = r.bottomLeft() + TQPoint(0,yoffset); + if( reverse ) { + pos = r.bottomRight() + TQPoint(0,yoffset); + pos.rx() -= popup->sizeHint().width(); + } + + int ph = popup->sizeHint().height(); + pos = mapToGlobal(pos); + int sh = TQApplication::desktop()->height(); + if ( defaultup || (pos.y() + ph > sh) ) { + TQPoint t = mapToGlobal( r.topLeft() ); + if( reverse ) { + t = mapToGlobal( r.topRight() ); + t.rx() -= popup->sizeHint().width(); + } + t.ry() -= (TQCOORD)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 TQMenuBar::hidePopups() +{ +#if defined(QT_ACCESSIBILITY_SUPPORT) + bool anyVisible = FALSE; +#endif + TQMenuItemListIt it(*mitems); + register TQMenuItem *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 ) { + TQAccessible::updateAccessibility( this, 0, TQAccessible::MenuEnd ); + inMenu = FALSE; + } +#endif +} + + +/*! + Reimplements TQWidget::show() in order to set up the correct + keyboard accelerators and to raise itself to the top of the widget + stack. +*/ + +void TQMenuBar::show() +{ +#ifndef QT_NO_ACCEL + setupAccelerators(); +#endif + + if ( parentWidget() ) + resize( parentWidget()->width(), height() ); + + TQApplication::sendPostedEvents( this, TQEvent::Resize ); + performDelayedChanges(); + calculateRects(); + +#if defined(Q_WS_MAC) && !defined(TQMAC_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) + TQWidget::hide(); + else + TQWidget::show(); + } else { + TQWidget::show(); + } +#else + TQWidget::show(); +#endif + +#ifndef QT_NO_MAINWINDOW + TQMainWindow *mw = ::qt_cast<TQMainWindow*>(parent()); + if ( mw ) //### ugly workaround + mw->triggerLayout(); +#endif + raise(); +} + +/*! + Reimplements TQWidget::hide() in order to deselect any selected + item, and calls setUpLayout() for the main window. +*/ + +void TQMenuBar::hide() +{ + TQWidget::hide(); + setAltMode( FALSE ); + hidePopups(); +#ifndef QT_NO_MAINWINDOW + TQMainWindow *mw = ::qt_cast<TQMainWindow*>(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 TQMenuBar::fontChange( const TQFont & f ) +{ + badSize = TRUE; + updateGeometry(); + if ( isVisible() ) + calculateRects(); + TQWidget::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 retquire almost identical steps. +*/ +int TQMenuBar::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 TQRect[ i ]; + Q_CHECK_PTR( irects ); + } + max_width = width(); + } + TQFontMetrics fm = fontMetrics(); + int max_height = 0; + int max_item_height = 0; + int nlitems = 0; // number on items on cur line + int gs = style().styleHint(TQStyle::SH_GUIStyle); + bool reverse = TQApplication::reverseLayout(); + int x = frameWidth(); + int y = frameWidth(); + if ( gs == MotifStyle ) { + x += motifBarHMargin; + y += motifBarVMargin; + } else if ( style().inherits("TQWindowsXPStyle") && style().styleHint(TQStyle::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(TQStyle::PM_MenuBarItemSpacing); + const int lastItem = reverse ? 0 : mitems->count() - 1; + + while ( i < (int)mitems->count() ) { // for each menu item... + TQMenuItem *mi = mitems->at(i); + + int w=0, h=0; + if ( !mi->isVisible() +#if defined(Q_WS_MAC) && !defined(TQMAC_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, TQPoint(0,0) ); + } + w = mi->widget()->sizeHint().expandedTo( TQApplication::globalStrut() ).width()+2; + h = mi->widget()->sizeHint().expandedTo( TQApplication::globalStrut() ).height()+2; + if ( i && separator < 0 ) + separator = i; + } else if ( mi->pixmap() ) { // pixmap item + w = TQMAX( mi->pixmap()->width() + 4, TQApplication::globalStrut().width() ); + h = TQMAX( mi->pixmap()->height() + 4, TQApplication::globalStrut().height() ); + } else if ( !mi->text().isNull() ) { // text item + TQString 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 = TQMAX( w, TQApplication::globalStrut().width() ); + if (gs == GtkStyle ) { + h = TQMAX( fm.height() + gtkItemVMargin, TQApplication::globalStrut().height() ); + } else { + h = TQMAX( fm.height() + motifItemVMargin, TQApplication::globalStrut().height() ); + } + } else if ( mi->isSeparator() ) { // separator item + if ( style().styleHint(TQStyle::SH_GUIStyle) == MotifStyle ) + separator = i; //### only motif? + } + if ( !mi->isSeparator() || mi->widget() ) { +#if defined(Q_WS_MAC) && !defined(TQMAC_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(TQMAC_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(TQStyle::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 ); + TQMenuItem *mi = mitems->at(i); + if ( mi->widget() ) { + TQRect r ( TQPoint(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 TQMenuBar::heightForWidth(int max_width) const +{ + // Okay to cast away const, as we are not updating. + if ( max_width < 0 ) max_width = 0; + return ((TQMenuBar*)this)->calculateRects( max_width ); +} + +/*! + \internal + Return the bounding rectangle for the menu item at position \a index. +*/ + +TQRect TQMenuBar::itemRect( int index ) +{ + calculateRects(); + return irects ? irects[index] : TQRect(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 TQMenuBar::itemAtPos( const TQPoint &pos_ ) +{ + calculateRects(); + if ( !irects ) + return -1; + int i = 0; + TQPoint pos = pos_; + // Fitts' Law for edges - compensate for the extra margin + // added in calculateRects() + const int margin = 2; + pos.setX( TQMAX( margin, TQMIN( width() - margin, pos.x()))); + pos.setY( TQMAX( margin, TQMIN( height() - margin, pos.y()))); + while ( i < (int)mitems->count() ) { + if ( !irects[i].isEmpty() && irects[i].contains( pos ) ) { + TQMenuItem *mi = mitems->at(i); + return mi->isSeparator() ? -1 : i; + } + ++i; + } + return -1; // no match +} + + +/*! + \property TQMenuBar::separator + \brief in which cases a menubar sparator is drawn + + \obsolete +*/ +void TQMenuBar::setSeparator( Separator when ) +{ + mseparator = when; +} + +TQMenuBar::Separator TQMenuBar::separator() const +{ + return mseparator ? InWindowsStyle : Never; +} + +/***************************************************************************** + Event handlers + *****************************************************************************/ + +/*! + Called from TQFrame::paintEvent(). Draws the menu bar contents + using painter \a p. +*/ + +void TQMenuBar::drawContents( TQPainter *p ) +{ + performDelayedChanges(); + TQRegion reg( contentsRect() ); + TQColorGroup g = colorGroup(); + bool e; + + // this shouldn't happen + if ( !irects ) + return; + + for ( int i=0; i<(int)mitems->count(); i++ ) { + TQMenuItem *mi = mitems->at( i ); + if ( !mi->text().isNull() || mi->pixmap() ) { + TQRect 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 ); + TQSharedDoubleBuffer buffer( p, r ); + buffer.painter()->setFont( p->font() ); + buffer.painter()->setPen( p->pen() ); + buffer.painter()->setBrush( p->brush() ); + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled() && e) + flags |= TQStyle::Style_Enabled; + if ( i == actItem ) + flags |= TQStyle::Style_Active; + if ( actItemDown ) + flags |= TQStyle::Style_Down; + if (hasFocus() || hasmouse || popupvisible) + flags |= TQStyle::Style_HasFocus; + style().drawControl(TQStyle::CE_MenuBarItem, buffer.painter(), this, + r, g, flags, TQStyleOption(mi)); + } + } + + p->save(); + p->setClipRegion(reg); + style().drawControl(TQStyle::CE_MenuBarEmptyArea, p, this, contentsRect(), g); + p->restore(); + +#if defined(Q_WS_MAC) && !defined(TQMAC_QMENUBAR_NO_NATIVE) + if ( !mac_eaten_menubar ) +#endif + { + TQt::GUIStyle gs = (TQt::GUIStyle) style().styleHint(TQStyle::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 TQMenuBar::mousePressEvent( TQMouseEvent *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 ) { + TQFocusEvent::Reason oldReason = TQFocusEvent::reason(); + TQMenuItem *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() ) + TQFocusEvent::setReason( TQFocusEvent::Popup ); + setAltMode( TRUE ); + TQFocusEvent::setReason( oldReason ); + } + setActiveItem( item, TRUE, FALSE ); +} + + +/*! + \reimp +*/ +void TQMenuBar::mouseReleaseEvent( TQMouseEvent *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(TQStyle::SH_GUIStyle) == WindowsStyle && + actItem == item ) { + showMenu = FALSE; + setAltMode( FALSE ); + } + setActiveItem( item, showMenu, !hasMouseTracking() ); + toggleclose = 0; +} + + +/*! + \reimp +*/ +void TQMenuBar::mouseMoveEvent( TQMouseEvent *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 TQMenuBar::leaveEvent( TQEvent * e ) +{ + hasmouse = 0; + int actId = idAt( actItem ); + if ( !hasFocus() && !popupvisible ) + actItem = -1; + updateItem( actId ); + TQFrame::leaveEvent( e ); +} + + +/*! + \reimp +*/ +void TQMenuBar::keyPressEvent( TQKeyEvent *e ) +{ + if ( actItem < 0 ) + return; + + TQMenuItem *mi = 0; + int dx = 0; + + if ( e->state() & TQt::ControlButton && + ( e->key() == TQt::Key_Tab || e->key() == TQt::Key_Backtab ) ) + { + e->ignore(); + return; + } + + switch ( e->key() ) { + case Key_Left: + dx = TQApplication::reverseLayout() ? 1 : -1; + break; + + case Key_Right: + case Key_Tab: + dx = TQApplication::reverseLayout() ? -1 : 1; + break; + + case Key_Up: + case Key_Down: + case Key_Enter: + case Key_Return: + if ( style().styleHint(TQStyle::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 + // TQMenuBar's reliance on TQPopupMenu + if ( /* (style() == WindowsStyle || */ mi->isEnabledAndVisible() /* ) */ + && !mi->isSeparator() ) + break; + } + setActiveItem( i, popupvisible ); + } else if ( ( !e->state() || (e->state()&(MetaButton|AltButton)) ) && e->text().length()==1 && !popupvisible ) { + TQChar c = e->text()[0].upper(); + + TQMenuItemListIt it(*mitems); + TQMenuItem* first = 0; + TQMenuItem* currentSelected = 0; + TQMenuItem* firstAfterCurrent = 0; + + register TQMenuItem *m; + int indx = 0; + int clashCount = 0; + while ( (m=it.current()) ) { + ++it; + TQString 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 TQMenuBar::resizeEvent( TQResizeEvent *e ) +{ + TQFrame::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 TQMenuBar::setActiveItem( int i, bool show, bool activate_first_item ) +{ + if ( i == actItem && (uint)show == popupvisible ) + return; + + TQMenuItem* 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 = TQMAX( actItem, i ); + actItem = i; + if ( irects && n >= 0 ) + repaint( irects[n], FALSE ); + } else if ( TQABS(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 ) { + TQRect mfrect = irects[actItem]; + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); + } + +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( mi ) + TQAccessible::updateAccessibility( this, indexOf( mi->id() )+1, TQAccessible::Focus ); +#endif + + if ( actItem < 0 || !popupvisible || !mi ) + return; + + TQPopupMenu *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 TQMenuBar::setAltMode( bool enable ) +{ +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( inMenu && !enable ) { + TQAccessible::updateAccessibility( this, 0, TQAccessible::MenuEnd ); + inMenu = FALSE; + } else if ( !inMenu && enable ) { + TQAccessible::updateAccessibility( this, 0, TQAccessible::MenuStart ); + inMenu = TRUE; + } +#endif + + waitforalt = 0; + actItemDown = FALSE; + if ( enable ) { + if ( !TQMenuData::d->aWidget ) + TQMenuData::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 ( TQMenuData::d->aWidget ) + TQMenuData::d->aWidget->setFocus(); + else + clearFocus(); + } + int actId = idAt( actItem ); + actItem = -1; + updateItem( actId ); + TQMenuData::d->aWidget = 0; + } +} + +/*! + Sets up keyboard accelerators for the menu bar. +*/ +#ifndef QT_NO_ACCEL + +void TQMenuBar::setupAccelerators() +{ + delete autoaccel; + autoaccel = 0; + + TQMenuItemListIt it(*mitems); + register TQMenuItem *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; + TQString s = mi->text(); + if ( !s.isEmpty() ) { + int i = TQAccel::shortcutKey( s ); + if ( i ) { + if ( !autoaccel ) { + autoaccel = new TQAccel( 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() ) { + TQPopupMenu* popup = mi->popup(); + popup->updateAccel( this ); + if ( !popup->isEnabled() ) + popup->enableAccel( FALSE ); + } + } +} +#endif + +/*! + \reimp + */ +bool TQMenuBar::customWhatsThis() const +{ + return TRUE; +} + + + +/*! + \reimp + */ +void TQMenuBar::focusInEvent( TQFocusEvent * ) +{ + if ( actItem < 0 ) { + int i = -1; + while ( actItem < 0 && ++i < (int) mitems->count() ) { + TQMenuItem* mi = mitems->at( i ); + if ( mi && mi->isEnabledAndVisible() && !mi->isSeparator() ) + setActiveItem( i, FALSE ); + } + } else if ( !popupvisible ) { + updateItem( idAt( actItem ) ); + } +} + +/*! + \reimp + */ +void TQMenuBar::focusOutEvent( TQFocusEvent * ) +{ + updateItem( idAt( actItem ) ); + if ( !popupvisible ) + setAltMode( FALSE ); +} + +/*! + \reimp +*/ + +TQSize TQMenuBar::sizeHint() const +{ + int h = height(); + if ( badSize ) + h = ( (TQMenuBar*)this )->calculateRects(); + TQSize 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(TQStyle::CT_MenuBar, this, s. + expandedTo(TQApplication::globalStrut()))); +} + +/*! + \reimp +*/ + +TQSize TQMenuBar::minimumSize() const +{ +#ifndef QT_NO_TOOLBAR + TQToolBar *tb = ::qt_cast<TQToolBar*>(parent()); + if ( tb ) + return sizeHint(); +#endif + return TQFrame::minimumSize(); +} + +/*! + \reimp +*/ + +TQSize TQMenuBar::minimumSizeHint() const +{ + return minimumSize(); +} + +/*! + \property TQMenuBar::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 TQMenuBar::setDefaultUp( bool on ) +{ + defaultup = on; +} + +bool TQMenuBar::isDefaultUp() const +{ + return defaultup; +} + + +/*! + \reimp + */ +void TQMenuBar::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 000000000..5bcea545e --- /dev/null +++ b/src/widgets/qmenubar.h @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Definition of TQMenuBar class +** +** Created : 941209 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQMENUBAR_H +#define TQMENUBAR_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 TQPopupMenu; + +class Q_EXPORT TQMenuBar : public TQFrame, public TQMenuData +{ + Q_OBJECT + Q_ENUMS( Separator ) + Q_PROPERTY( Separator separator READ separator WRITE setSeparator DESIGNABLE false ) + Q_PROPERTY( bool defaultUp READ isDefaultUp WRITE setDefaultUp ) + +public: + TQMenuBar( TQWidget* parent=0, const char* name=0 ); + ~TQMenuBar(); + + void updateItem( int id ); + + void show(); // reimplemented show + void hide(); // reimplemented hide + + bool eventFilter( TQObject *, TQEvent * ); + + 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; + + TQSize sizeHint() const; + TQSize minimumSize() const; + TQSize minimumSizeHint() const; + + void activateItemAt( int index ); + +#if defined(Q_WS_MAC) && !defined(TQMAC_QMENUBAR_NO_NATIVE) + static void initialize(); + static void cleanup(); +#endif + +signals: + void activated( int itemId ); + void highlighted( int itemId ); + +protected: + void drawContents( TQPainter * ); + void fontChange( const TQFont & ); + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void keyPressEvent( TQKeyEvent * ); + void focusInEvent( TQFocusEvent * ); + void focusOutEvent( TQFocusEvent * ); + void resizeEvent( TQResizeEvent * ); + void leaveEvent( TQEvent * ); + void menuContentsChanged(); + void menuStateChanged(); + void styleChange( TQStyle& ); + int itemAtPos( const TQPoint & ); + void hidePopups(); + TQRect 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( TQObject* ); + void performDelayedChanges(); + + void languageChange(); + +private: + void performDelayedContentsChanged(); + void performDelayedStateChanged(); + void menuInsPopup( TQPopupMenu * ); + void menuDelPopup( TQPopupMenu * ); + void frameChanged(); + + bool tryMouseEvent( TQPopupMenu *, TQMouseEvent * ); + void tryKeyEvent( TQPopupMenu *, TQKeyEvent * ); + 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(); + TQAccel *autoaccel; +#endif + TQRect *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 TQPopupMenu; + +#if defined(Q_WS_MAC) && !defined(TQMAC_QMENUBAR_NO_NATIVE) + friend class TQWidget; + friend class TQApplication; + friend void qt_mac_set_modal_state(bool, TQMenuBar *); + + void macCreateNativeMenubar(); + void macRemoveNativeMenubar(); + void macDirtyNativeMenubar(); + +#if !defined(TQMAC_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, TQPopupMenu *d); + MenuRef createMacPopup(TQPopupMenu *d, int id, bool =FALSE); + bool updateMenuBar(); +#if !defined(TQMAC_QMENUBAR_NO_MERGE) + uint isCommand(TQMenuItem *, 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) + TQMenuBar( const TQMenuBar & ); + TQMenuBar &operator=( const TQMenuBar & ); +#endif +}; + + +#endif // QT_NO_MENUBAR + +#endif // TQMENUBAR_H diff --git a/src/widgets/qmenudata.cpp b/src/widgets/qmenudata.cpp new file mode 100644 index 000000000..98aa064d1 --- /dev/null +++ b/src/widgets/qmenudata.cpp @@ -0,0 +1,1603 @@ +/**************************************************************************** +** +** Implementation of TQMenuData class +** +** Created : 941128 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qmenudata.h" +#ifndef QT_NO_MENUDATA +#include "qpopupmenu.h" +#include "qmenubar.h" +#include "qapplication.h" +#include "qguardedptr.h" + +class TQMenuItemData { +public: + TQCustomMenuItem *custom_item; // custom menu item +}; + +class TQMenuDataData { + // attention: also defined in qmenubar.cpp and qpopupmenu.cpp +public: + TQMenuDataData(); + TQGuardedPtr<TQWidget> aWidget; + int aInt; +}; +TQMenuDataData::TQMenuDataData() + : aInt(-1) +{} + +/*! + \class TQMenuData qmenudata.h + \brief The TQMenuData class is a base class for TQMenuBar and TQPopupMenu. + + \ingroup misc + + TQMenuData 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 TQMenuBar::activated() + activated()\endlink signal when it is chosen and a \link + TQMenuBar::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 TQAccel TQPopupMenu TQAction +*/ + + +/***************************************************************************** + TQMenuItem member functions + *****************************************************************************/ + +TQMenuItem::TQMenuItem() + :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) +{} + +TQMenuItem::~TQMenuItem() +{ + delete iconset_data; + delete pixmap_data; + delete signal_data; + delete widget_item; + if ( d ) + delete d->custom_item; + delete d; +} + + +/***************************************************************************** + TQMenuData member functions + *****************************************************************************/ + +TQMenuItemData* TQMenuItem::extra() +{ + if ( !d ) d = new TQMenuItemData; + return d; +} + +TQCustomMenuItem *TQMenuItem::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. +*/ + +TQMenuData::TQMenuData() +{ + actItem = -1; // no active menu item + mitems = new TQMenuItemList; // 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 TQMenuDataData; +} + +/*! + Removes all menu items and disconnects any signals that have been + connected. +*/ + +TQMenuData::~TQMenuData() +{ + delete mitems; // delete menu item list + delete d; +} + + +/*! + Virtual function; notifies subclasses about an item with \a id + that has been changed. +*/ + +void TQMenuData::updateItem( int /* id */ ) // reimplemented in subclass +{ +} + +/*! + Virtual function; notifies subclasses that one or more items have + been inserted or removed. +*/ + +void TQMenuData::menuContentsChanged() // reimplemented in subclass +{ +} + +/*! + Virtual function; notifies subclasses that one or more items have + changed state (enabled/disabled or checked/unchecked). +*/ + +void TQMenuData::menuStateChanged() // reimplemented in subclass +{ +} + +/*! + Virtual function; notifies subclasses that a popup menu item has + been inserted. +*/ + +void TQMenuData::menuInsPopup( TQPopupMenu * ) // reimplemented in subclass +{ +} + +/*! + Virtual function; notifies subclasses that a popup menu item has + been removed. +*/ + +void TQMenuData::menuDelPopup( TQPopupMenu * ) // reimplemented in subclass +{ +} + + +/*! + Returns the number of items in the menu. +*/ + +uint TQMenuData::count() const +{ + return mitems->count(); +} + + + +/*! + \internal + + Internal function that insert a menu item. Called by all insert() + functions. +*/ + +int TQMenuData::insertAny( const TQString *text, const TQPixmap *pixmap, + TQPopupMenu *popup, const TQIconSet* iconset, int id, int index, + TQWidget* widget, TQCustomMenuItem* 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 TQMenuItem *mi = new TQMenuItem; + 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 TQIconSet( *iconset ); + } else if ( text == 0 && pixmap == 0 && popup == 0 ) { + mi->is_separator = TRUE; // separator + } else { +#ifndef Q_OS_TEMP + mi->text_data = text?*text:TQString(); +#else + TQString newText( *text ); + newText.truncate( newText.findRev( '\t' ) ); + mi->text_data = newText.isEmpty()?TQString():newText; +#endif +#ifndef QT_NO_ACCEL + mi->accel_key = TQt::Key_unknown; +#endif + if ( pixmap && !pixmap->isNull() ) + mi->pixmap_data = new TQPixmap( *pixmap ); + if ( (mi->popup_menu = popup) ) + menuInsPopup( popup ); + if ( iconset && !iconset->isNull() ) + mi->iconset_data = new TQIconSet( *iconset ); + } + + mitems->insert( index, mi ); + TQPopupMenu* p = ::qt_cast<TQPopupMenu*>(TQMenuData::d->aWidget); + if (p && p->isVisible() && p->mitems) { + p->mitems->clear(); + for ( TQMenuItemListIt it( *mitems ); it.current(); ++it ) { + if ( it.current()->id() != TQMenuData::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. +*/ +TQMenuItem *TQMenuData::findPopup( TQPopupMenu *popup, int *index ) +{ + int i = 0; + TQMenuItem *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 TQMenuData::removePopup( TQPopupMenu *popup ) +{ + int index = 0; + TQMenuItem *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{TQCustomMenuItem}) 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 tquite 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 + TQMenuBar *mainMenu = new TQMenuBar; + TQPopupMenu *fileMenu = new TQPopupMenu; + 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 TQKeySequence 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 TQIconSet parameter to specify the + little menu item icon. Note that you can always pass a TQPixmap + 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 TQt 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 TQt are not application-global, + instead they are bound to a certain top-level window. For example, + accelerators in TQPopupMenu 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 TQAccel + 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{(TQObject*)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 TQt + version. + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem(), TQAccel, + qnamespace.h +*/ + +int TQMenuData::insertItem( const TQString &text, + const TQObject *receiver, const char* member, + const TQKeySequence& 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(), TQAccel, + qnamespace.h +*/ + +int TQMenuData::insertItem( const TQIconSet& icon, + const TQString &text, + const TQObject *receiver, const char* member, + const TQKeySequence& 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 TQPixmap::mask()). + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int TQMenuData::insertItem( const TQPixmap &pixmap, + const TQObject *receiver, const char* member, + const TQKeySequence& 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 TQPixmap::mask()). + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem(), TQAccel, + qnamespace.h +*/ + +int TQMenuData::insertItem( const TQIconSet& icon, + const TQPixmap &pixmap, + const TQObject *receiver, const char* member, + const TQKeySequence& 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 TQMenuData::insertItem( const TQString &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 TQMenuData::insertItem( const TQIconSet& icon, + const TQString &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 TQMenuData::insertItem( const TQString &text, TQPopupMenu *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 TQMenuData::insertItem( const TQIconSet& icon, + const TQString &text, TQPopupMenu *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 TQPixmap::mask()). + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int TQMenuData::insertItem( const TQPixmap &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 TQMenuData::insertItem( const TQIconSet& icon, + const TQPixmap &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 TQMenuData::insertItem( const TQPixmap &pixmap, TQPopupMenu *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 TQMenuData::insertItem( const TQIconSet& icon, + const TQPixmap &pixmap, TQPopupMenu *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{TQWidget::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 TQLabel 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 TQLineEdit, 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 + TQWidget::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("TQPopupMenu") ) + parentWidget()->close(); + \endcode + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem() +*/ +int TQMenuData::insertItem( TQWidget* 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(), TQCustomMenuItem +*/ +int TQMenuData::insertItem( TQCustomMenuItem* 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(), TQCustomMenuItem +*/ +int TQMenuData::insertItem( const TQIconSet& icon, TQCustomMenuItem* 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 TQMenuData::insertSeparator( int index ) +{ + return insertAny( 0, 0, 0, 0, -1, index ); +} + +/*! + \fn void TQMenuData::removeItem( int id ) + + Removes the menu item that has the identifier \a id. + + \sa removeItemAt(), clear() +*/ + +void TQMenuData::removeItem( int id ) +{ + TQMenuData *parent; + if ( findItem( id, &parent ) ) + parent->removeItemAt(parent->indexOf(id)); +} + +/*! + Removes the menu item at position \a index. + + \sa removeItem(), clear() +*/ + +void TQMenuData::removeItemAt( int index ) +{ + if ( index < 0 || index >= (int)mitems->count() ) { +#if defined(QT_CHECK_RANGE) + qWarning( "TQMenuData::removeItem: Index %d out of range", index ); +#endif + return; + } + TQMenuItem *mi = mitems->at( index ); + if ( mi->popup_menu ) + menuDelPopup( mi->popup_menu ); + mitems->remove(); + TQPopupMenu* p = ::qt_cast<TQPopupMenu*>(TQMenuData::d->aWidget); + if (p && p->isVisible() && p->mitems) { + p->mitems->clear(); + for ( TQMenuItemListIt it( *mitems ); it.current(); ++it ) { + if ( it.current()->id() != TQMenuData::d->aInt && !it.current()->widget() ) + p->mitems->append( it.current() ); + } + } + if ( !TQApplication::closingDown() ) // avoid trouble + menuContentsChanged(); +} + + +/*! + Removes all menu items. + + \sa removeItem(), removeItemAt() +*/ + +void TQMenuData::clear() +{ + register TQMenuItem *mi = mitems->first(); + while ( mi ) { + if ( mi->popup_menu ) + menuDelPopup( mi->popup_menu ); + mitems->remove(); + mi = mitems->current(); + } + TQPopupMenu* p = ::qt_cast<TQPopupMenu*>(TQMenuData::d->aWidget); + if (p && p->isVisible() && p->mitems) { + p->mitems->clear(); + } + if ( !TQApplication::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(), TQAccel, qnamespace.h +*/ + +TQKeySequence TQMenuData::accel( int id ) const +{ + TQMenuItem *mi = findItem( id ); + if ( mi ) + return mi->key(); + return TQKeySequence(); +} + +/*! + 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 TQt are not application-global, + instead they are bound to a certain top-level window. For example, + accelerators in TQPopupMenu 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 TQAccel + object. + + Example: + \code + TQMenuBar *mainMenu = new TQMenuBar; + TQPopupMenu *fileMenu = new TQPopupMenu; // 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 tquit + 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 TQAction to associate accelerators with menu + items. + + \sa accel() insertItem() TQAccel TQAction +*/ + +void TQMenuData::setAccel( const TQKeySequence& key, int id ) +{ + TQMenuData *parent; + TQMenuItem *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() +*/ + +TQIconSet* TQMenuData::iconSet( int id ) const +{ + TQMenuItem *mi = findItem( id ); + return mi ? mi->iconSet() : 0; +} + +/*! + Returns the text that has been set for menu item \a id, or + TQString::null if no text has been set. + + \sa changeItem(), pixmap(), iconSet() +*/ + +TQString TQMenuData::text( int id ) const +{ + TQMenuItem *mi = findItem( id ); + return mi ? mi->text() : TQString::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() +*/ + +TQPixmap *TQMenuData::pixmap( int id ) const +{ + TQMenuItem *mi = findItem( id ); + return mi ? mi->pixmap() : 0; +} + +/*! + \fn void TQMenuData::changeItem( const TQString &, 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 TQMenuData::changeItem( const TQPixmap &, 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 TQMenuData::changeItem( const TQIconSet &, const TQString &, 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 TQMenuData::changeItem( int id, const TQString &text ) +{ + TQMenuData *parent; + TQMenuItem *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 = TQt::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 TQMenuData::changeItem( int id, const TQPixmap &pixmap ) +{ + TQMenuData *parent; + TQMenuItem *mi = findItem( id, &parent ); + if ( mi ) { // item found + register TQPixmap *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 = TQString::null; + if ( !pixmap.isNull() ) + mi->pixmap_data = new TQPixmap( 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 TQMenuData::changeItem( int id, const TQIconSet &icon, const TQString &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 TQMenuData::changeItem( int id, const TQIconSet &icon, const TQPixmap &pixmap ) +{ + changeItem( id, pixmap ); + changeItemIconSet( id, icon ); +} + + + +/*! + Changes the icon of the menu item \a id to \a icon. + + \sa pixmap() +*/ + +void TQMenuData::changeItemIconSet( int id, const TQIconSet &icon ) +{ + TQMenuData *parent; + TQMenuItem *mi = findItem( id, &parent ); + if ( mi ) { // item found + register TQIconSet *i = mi->iconset_data; + bool fast_refresh = i != 0; + if ( !icon.isNull() ) + mi->iconset_data = new TQIconSet( 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 TQMenuData::isItemEnabled( int id ) const +{ + TQMenuItem *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 TQMenuData::setItemEnabled( int id, bool enable ) +{ + TQMenuData *parent; + TQMenuItem *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 TQMenuData::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 TQMenuData::isItemChecked( int id ) const +{ + TQMenuItem *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 + TQPopupMenu::setCheckable( TRUE ) if necessary. + + \sa isItemChecked() +*/ + +void TQMenuData::setItemChecked( int id, bool check ) +{ + TQMenuData *parent; + TQMenuItem *mi = findItem( id, &parent ); + if ( mi && (bool)mi->is_checked != check ) { + mi->is_checked = check; +#ifndef QT_NO_POPUPMENU + if ( parent->isPopupMenu && !((TQPopupMenu *)parent)->isCheckable() ) + ((TQPopupMenu *)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 TQMenuData::isItemVisible( int id ) const +{ + TQMenuItem *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 TQMenuData::setItemVisible( int id, bool visible ) +{ + TQMenuData *parent; + TQMenuItem *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 TQMenuItem 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() +*/ + +TQMenuItem *TQMenuData::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 TQMenuItem 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() +*/ + +TQMenuItem * TQMenuData::findItem( int id, TQMenuData ** parent ) const +{ + if ( parent ) + *parent = (TQMenuData *)this; // ### + + if ( id == -1 ) // bad identifier + return 0; + TQMenuItemListIt it( *mitems ); + TQMenuItem *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 ) { + TQPopupMenu *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 TQMenuData::indexOf( int id ) const +{ + if ( id == -1 ) // bad identifier + return -1; + TQMenuItemListIt it( *mitems ); + TQMenuItem *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 TQMenuData::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 TQMenuData::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 TQMenuData::setItemParameter( int id, int param ) { + TQMenuItem *mi = findItem( id ); + if ( !mi ) // no such identifier + return FALSE; + if ( !mi->signal_data ) { // create new signal + mi->signal_data = new TQSignal; + 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 TQMenuData::itemParameter( int id ) const +{ + TQMenuItem *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 TQMenuData::connectItem( int id, const TQObject *receiver, + const char* member ) +{ + TQMenuItem *mi = findItem( id ); + if ( !mi ) // no such identifier + return FALSE; + if ( !mi->signal_data ) { // create new signal + mi->signal_data = new TQSignal; + 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 TQMenuData::disconnectItem( int id, const TQObject *receiver, + const char* member ) +{ + TQMenuItem *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 TQMenuData::setWhatsThis( int id, const TQString& text ) +{ + + TQMenuData *parent; + TQMenuItem *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 TQString::null if no text has yet been defined. + + \sa setWhatsThis() +*/ +TQString TQMenuData::whatsThis( int id ) const +{ + + TQMenuItem *mi = findItem( id ); + return mi? mi->whatsThis() : TQString::null; +} + + + +/*! + \class TQCustomMenuItem qmenudata.h + \brief The TQCustomMenuItem 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 + TQPopupMenu::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 TQCustomMenuItem. 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 TQCustomMenuItem will not + recognize shortcut keys that are from text with ampersands. Normal + accelerators work though. + + <img src=qpopmenu-fancy.png> + + \sa TQMenuData, TQPopupMenu +*/ + + + +/*! + Constructs a TQCustomMenuItem +*/ +TQCustomMenuItem::TQCustomMenuItem() +{ +} + +/*! + Destroys a TQCustomMenuItem +*/ +TQCustomMenuItem::~TQCustomMenuItem() +{ +} + + +/*! + 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 TQCustomMenuItem::setFont( const TQFont& /* 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 TQCustomMenuItem::fullSpan() const +{ + return FALSE; +} + +/*! + Returns TRUE if this item is just a separator; otherwise returns + FALSE. +*/ +bool TQCustomMenuItem::isSeparator() const +{ + return FALSE; +} + + +/*! + \fn void TQCustomMenuItem::paint( TQPainter* p, const TQColorGroup& 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 TQSize TQCustomMenuItem::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 TQMenuData::activateItemAt( int index ) +{ +#ifndef QT_NO_MENUBAR + if ( isMenuBar ) + ( (TQMenuBar*)this )->activateItemAt( index ); + else +#endif + { +#ifndef QT_NO_POPUPMENU + if ( isPopupMenu ) + ( (TQPopupMenu*)this )->activateItemAt( index ); +#endif + } +} + +#endif diff --git a/src/widgets/qmenudata.h b/src/widgets/qmenudata.h new file mode 100644 index 000000000..00f061498 --- /dev/null +++ b/src/widgets/qmenudata.h @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Definition of TQMenuData class +** +** Created : 941128 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQMENUDATA_H +#define TQMENUDATA_H + +#ifndef QT_H +#include "qglobal.h" +#include "qiconset.h" // conversion TQPixmap->TQIconset +#include "qkeysequence.h" +#include "qstring.h" +#include "qsignal.h" +#include "qfont.h" +#endif // QT_H + +#ifndef QT_NO_MENUDATA + +class TQPopupMenu; +class TQMenuDataData; +class TQObject; + +class TQCustomMenuItem; +class TQMenuItemData; + +class Q_EXPORT TQMenuItem // internal menu item class +{ +friend class TQMenuData; +public: + TQMenuItem(); + ~TQMenuItem(); + + int id() const { return ident; } + TQIconSet *iconSet() const { return iconset_data; } + TQString text() const { return text_data; } + TQString whatsThis() const { return whatsthis_data; } + TQPixmap *pixmap() const { return pixmap_data; } + TQPopupMenu *popup() const { return popup_menu; } + TQWidget *widget() const { return widget_item; } + TQCustomMenuItem *custom() const; +#ifndef QT_NO_ACCEL + TQKeySequence key() const { return accel_key; } +#endif + TQSignal *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 TQString &text ) { text_data = text; } + void setDirty( bool dirty ) { is_dirty = dirty; } + void setVisible( bool visible ) { is_visible = visible; } + void setWhatsThis( const TQString &text ) { whatsthis_data = text; } + +private: + int ident; // item identifier + TQIconSet *iconset_data; // icons + TQString text_data; // item text + TQString whatsthis_data; // item Whats This help text + TQPixmap *pixmap_data; // item pixmap + TQPopupMenu *popup_menu; // item popup menu + TQWidget *widget_item; // widget menu item +#ifndef QT_NO_ACCEL + TQKeySequence accel_key; // accelerator key (state|ascii) +#endif + TQSignal *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 + TQMenuItemData* d; + + TQMenuItemData* extra(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQMenuItem( const TQMenuItem & ); + TQMenuItem &operator=( const TQMenuItem & ); +#endif +}; + +#include "qptrlist.h" +typedef TQPtrList<TQMenuItem> TQMenuItemList; +typedef TQPtrListIterator<TQMenuItem> TQMenuItemListIt; + + +class Q_EXPORT TQCustomMenuItem : public TQt +{ +public: + TQCustomMenuItem(); + virtual ~TQCustomMenuItem(); + virtual bool fullSpan() const; + virtual bool isSeparator() const; + virtual void setFont( const TQFont& font ); + virtual void paint( TQPainter* p, const TQColorGroup& cg, bool act, + bool enabled, int x, int y, int w, int h ) = 0; + virtual TQSize sizeHint() = 0; +}; + + +class Q_EXPORT TQMenuData // menu data class +{ +friend class TQMenuBar; +friend class TQPopupMenu; +public: + TQMenuData(); + virtual ~TQMenuData(); + + uint count() const; + + + int insertItem( const TQString &text, + const TQObject *receiver, const char* member, + const TQKeySequence& accel = 0, int id = -1, int index = -1 ); + int insertItem( const TQIconSet& icon, + const TQString &text, + const TQObject *receiver, const char* member, + const TQKeySequence& accel = 0, int id = -1, int index = -1 ); + int insertItem( const TQPixmap &pixmap, + const TQObject *receiver, const char* member, + const TQKeySequence& accel = 0, int id = -1, int index = -1 ); + int insertItem( const TQIconSet& icon, + const TQPixmap &pixmap, + const TQObject *receiver, const char* member, + const TQKeySequence& accel = 0, int id = -1, int index = -1 ); + + int insertItem( const TQString &text, int id=-1, int index=-1 ); + int insertItem( const TQIconSet& icon, + const TQString &text, int id=-1, int index=-1 ); + + int insertItem( const TQString &text, TQPopupMenu *popup, + int id=-1, int index=-1 ); + int insertItem( const TQIconSet& icon, + const TQString &text, TQPopupMenu *popup, + int id=-1, int index=-1 ); + + + int insertItem( const TQPixmap &pixmap, int id=-1, int index=-1 ); + int insertItem( const TQIconSet& icon, + const TQPixmap &pixmap, int id=-1, int index=-1 ); + int insertItem( const TQPixmap &pixmap, TQPopupMenu *popup, + int id=-1, int index=-1 ); + int insertItem( const TQIconSet& icon, + const TQPixmap &pixmap, TQPopupMenu *popup, + int id=-1, int index=-1 ); + + int insertItem( TQWidget* widget, int id=-1, int index=-1 ); + + int insertItem( const TQIconSet& icon, TQCustomMenuItem* custom, int id=-1, int index=-1 ); + int insertItem( TQCustomMenuItem* 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 + TQKeySequence accel( int id ) const; + void setAccel( const TQKeySequence& key, int id ); +#endif + + TQIconSet *iconSet( int id ) const; + TQString text( int id ) const; + TQPixmap *pixmap( int id ) const; + + void setWhatsThis( int id, const TQString& ); + TQString whatsThis( int id ) const; + + + void changeItem( int id, const TQString &text ); + void changeItem( int id, const TQPixmap &pixmap ); + void changeItem( int id, const TQIconSet &icon, const TQString &text ); + void changeItem( int id, const TQIconSet &icon, const TQPixmap &pixmap ); + + void changeItem( const TQString &text, int id ) { changeItem( id, text); } // obsolete + void changeItem( const TQPixmap &pixmap, int id ) { changeItem( id, pixmap ); } // obsolete + void changeItem( const TQIconSet &icon, const TQString &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 TQObject *receiver, const char* member ); + bool disconnectItem( int id, + const TQObject *receiver, const char* member ); + + bool setItemParameter( int id, int param ); + int itemParameter( int id ) const; + + TQMenuItem *findItem( int id ) const; + TQMenuItem *findItem( int id, TQMenuData ** parent ) const; + TQMenuItem * findPopup( TQPopupMenu *, int *index = 0 ); + + virtual void activateItemAt( int index ); + +protected: + int actItem; + TQMenuItemList *mitems; + TQMenuData *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( TQPopupMenu * ); + virtual void menuDelPopup( TQPopupMenu * ); + +private: + int insertAny( const TQString *, const TQPixmap *, TQPopupMenu *, + const TQIconSet*, int, int, TQWidget* = 0, TQCustomMenuItem* = 0); + void removePopup( TQPopupMenu * ); + void changeItemIconSet( int id, const TQIconSet &icon ); + + TQMenuDataData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQMenuData( const TQMenuData & ); + TQMenuData &operator=( const TQMenuData & ); +#endif +}; + + +#endif // QT_NO_MENUDATA + +#endif // TQMENUDATA_H diff --git a/src/widgets/qmultilineedit.cpp b/src/widgets/qmultilineedit.cpp new file mode 100644 index 000000000..be5a7aac3 --- /dev/null +++ b/src/widgets/qmultilineedit.cpp @@ -0,0 +1,541 @@ +/********************************************************************** +** +** Implementation of TQMultiLineEdit widget class +** +** Created : 961005 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQMultiLineEdit qmultilineedit.h + \obsolete + + \brief The TQMultiLineEdit widget is a simple editor for inputting text. + + \ingroup advanced + + The TQMultiLineEdit was a simple editor widget in former TQt versions. TQt + 3.0 includes a new richtext engine which obsoletes TQMultiLineEdit. It is + still included for compatibility reasons. It is now a subclass of + \l TQTextEdit, and provides enough of the old TQMultiLineEdit API to keep old + applications working. + + If you implement something new with TQMultiLineEdit, we suggest using + \l TQTextEdit instead and call TQTextEdit::setTextFormat(TQt::PlainText). + + Although most of the old TQMultiLineEdit API is still available, there is + a few difference. The old TQMultiLineEdit 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 TQMultiLineEdit + should still work without problems. Some odd usage will retquire some + porting. In these cases, it may be better to use \l TQTextEdit now. + + <img src=qmlined-m.png> <img src=qmlined-w.png> + + \sa TQTextEdit +*/ + +/*! + \fn bool TQMultiLineEdit::autoUpdate() const + \obsolete +*/ + +/*! + \fn virtual void TQMultiLineEdit::setAutoUpdate( bool ) + \obsolete +*/ + +/*! + \fn int TQMultiLineEdit::totalWidth() const + \obsolete +*/ + +/*! + \fn int TQMultiLineEdit::totalHeight() const + \obsolete +*/ + +/*! + \fn int TQMultiLineEdit::maxLines() const + \obsolete +*/ + +/*! + \fn void TQMultiLineEdit::setMaxLines( int ) + \obsolete +*/ + +/*! + \fn void TQMultiLineEdit::deselect() + \obsolete +*/ + + +class TQMultiLineEditData +{ +}; + + +/*! + Constructs a new, empty, TQMultiLineEdit with parent \a parent called + \a name. +*/ + +TQMultiLineEdit::TQMultiLineEdit( TQWidget *parent , const char *name ) + : TQTextEdit( parent, name ) +{ + d = new TQMultiLineEditData; + setTextFormat( TQt::PlainText ); +} + +/*! \property TQMultiLineEdit::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 TQMultiLineEdit::numLines() const +{ + return document()->lastParagraph()->paragId() + 1; +} + +/*! \property TQMultiLineEdit::atEnd + \brief whether the cursor is placed at the end of the text + + \sa atBeginning +*/ + +bool TQMultiLineEdit::atEnd() const +{ + return textCursor()->paragraph() == document()->lastParagraph() && textCursor()->atParagEnd(); +} + + +/*! \property TQMultiLineEdit::atBeginning + \brief whether the cursor is placed at the beginning of the text + + \sa atEnd +*/ + +bool TQMultiLineEdit::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 TQMultiLineEdit::lineLength( int row ) const +{ + if ( row < 0 || row > numLines() ) + return -1; + return document()->paragAt( row )->length() - 1; +} + + +/*! \reimp */ + +TQMultiLineEdit::~TQMultiLineEdit() +{ + 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 TQMultiLineEdit::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 TQMultiLineEdit::hasMarkedText() const +{ + return hasSelectedText(); +} + + +/*! + Returns a copy of the selected text. +*/ + +TQString TQMultiLineEdit::markedText() const +{ + return selectedText(); +} + +/*! + Moves the cursor one page down. If \a mark is TRUE, the text + is selected. +*/ + +void TQMultiLineEdit::pageDown( bool mark ) +{ + moveCursor( MoveDown, mark ); +} + + +/*! + Moves the cursor one page up. If \a mark is TRUE, the text + is selected. +*/ + +void TQMultiLineEdit::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 TQMultiLineEdit::insertLine( const TQString &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 TQMultiLineEdit::removeLine( int paragraph ) +{ + removeParagraph( paragraph ); +} + +/*! Inserts \a str at the current cursor position and selects the + text if \a mark is TRUE. +*/ + +void TQMultiLineEdit::insertAndMark( const TQString& str, bool mark ) +{ + insert( str ); + if ( mark ) + document()->setSelectionEnd( TQTextDocument::Standard, *textCursor() ); +} + +/*! Splits the paragraph at the current cursor position. +*/ + +void TQMultiLineEdit::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 TQMultiLineEdit::backspace() +{ + if ( document()->hasSelection( TQTextDocument::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 TQMultiLineEdit::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 TQMultiLineEdit::end( bool mark ) +{ + moveCursor( MoveLineEnd, mark ); +} + + +/*! + \fn void TQMultiLineEdit::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 TQMultiLineEdit::setCursorPosition( int line, int col, bool mark ) +{ + if ( !mark ) + selectAll( FALSE ); + TQTextEdit::setCursorPosition( line, col ); + if ( mark ) + document()->setSelectionEnd( TQTextDocument::Standard, *textCursor() ); +} + +/*! Returns the top center point where the cursor is drawn. +*/ + +TQPoint TQMultiLineEdit::cursorPoint() const +{ + return TQPoint( textCursor()->x(), textCursor()->y() + textCursor()->paragraph()->rect().y() ); +} + +/*! \property TQMultiLineEdit::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 TQt::AlignmentFlags +*/ +void TQMultiLineEdit::setAlignment( int flag ) +{ + if ( flag == AlignCenter ) + flag = AlignHCenter; + if ( flag != AlignLeft && flag != AlignRight && flag != AlignHCenter ) + return; + TQTextParagraph *p = document()->firstParagraph(); + while ( p ) { + p->setAlignment( flag ); + p = p->next(); + } +} + +int TQMultiLineEdit::alignment() const +{ + return document()->firstParagraph()->alignment(); +} + + +void TQMultiLineEdit::setEdited( bool e ) +{ + setModified( e ); +} + +/*! \property TQMultiLineEdit::edited + \brief whether the document has been edited by the user + + This is the same as TQTextEdit's "modifed" property. + + \sa TQTextEdit::modified +*/ +bool TQMultiLineEdit::edited() const +{ + return isModified(); +} + +/*! Moves the cursor one word to the right. If \a mark is TRUE, the text + is selected. + + \sa cursorWordBackward() +*/ +void TQMultiLineEdit::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 TQMultiLineEdit::cursorWordBackward( bool mark ) +{ + moveCursor( MoveWordBackward, mark ); +} + +/*! + \fn TQMultiLineEdit::insertAt( const TQString &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 TQMultiLineEdit::insertAt( const TQString &s, int line, int col, bool mark ) +{ + TQTextEdit::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 TQMultiLineEdit::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 TQMultiLineEdit::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 TQMultiLineEdit::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 TQMultiLineEdit::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 TQMultiLineEdit::cursorDown( bool mark ) +{ + moveCursor( MoveDown, mark ); +} + + +/*! Returns the text at line number \a line (possibly the empty + string), or a \link TQString::operator!() null string\endlink if \a + line is invalid. +*/ + +TQString TQMultiLineEdit::textLine( int line ) const +{ + if ( line < 0 || line >= numLines() ) + return TQString::null; + TQString 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 000000000..50a7c4596 --- /dev/null +++ b/src/widgets/qmultilineedit.h @@ -0,0 +1,141 @@ +/********************************************************************** +** +** Definition of TQMultiLineEdit widget class +** +** Created : 961005 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQMULTILINEEDIT_H +#define TQMULTILINEEDIT_H + +#ifndef QT_H +#include "qtextedit.h" +#endif // QT_H + +#ifndef QT_NO_MULTILINEEDIT + +class TQMultiLineEditCommand; +class TQValidator; +class TQMultiLineEditData; + +class Q_EXPORT TQMultiLineEdit : public TQTextEdit +{ + 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: + TQMultiLineEdit( TQWidget* parent=0, const char* name=0 ); + ~TQMultiLineEdit(); + + TQString textLine( int line ) const; + int numLines() const; + + virtual void insertLine( const TQString &s, int line = -1 ); + virtual void insertAt( const TQString &s, int line, int col ) { + insertAt( s, line, col, FALSE ); + } + virtual void insertAt( const TQString &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; + TQString 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 TQWIDGETSIZE_MAX; } + void setMaxLines( int ) {} + +public slots: + void deselect() { selectAll( FALSE ); } + +protected: + TQPoint cursorPoint() const; + +protected: + virtual void insertAndMark( const TQString&, 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: + TQMultiLineEditData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQMultiLineEdit( const TQMultiLineEdit & ); + TQMultiLineEdit &operator=( const TQMultiLineEdit & ); +#endif +}; + +#endif // QT_NO_MULTILINEEDIT + +#endif // TQMULTILINED_H diff --git a/src/widgets/qpopupmenu.cpp b/src/widgets/qpopupmenu.cpp new file mode 100644 index 000000000..400b89e1a --- /dev/null +++ b/src/widgets/qpopupmenu.cpp @@ -0,0 +1,2886 @@ +/**************************************************************************** +** +** Implementation of TQPopupMenu class +** +** Created : 941128 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQPopupMenu * syncMenu = 0; +static int syncMenuId = 0; + +// Used to detect motion prior to mouse-release +static int motion; + +// used to provide ONE single-shot timer +static TQTimer * singleSingleShot = 0; + +static bool supressAboutToShow = FALSE; + +static void cleanup() +{ + delete singleSingleShot; + singleSingleShot = 0; +} + +static void popupSubMenuLater( int msec, TQPopupMenu * receiver ) { + if ( !singleSingleShot ) { + singleSingleShot = new TQTimer( qApp, "popup submenu timer" ); + qAddPostRoutine( cleanup ); + } + + singleSingleShot->disconnect( SIGNAL(timeout()) ); + TQObject::connect( singleSingleShot, SIGNAL(timeout()), + receiver, SLOT(subMenuTimer()) ); + singleSingleShot->start( msec, TRUE ); +} + +static bool preventAnimation = FALSE; + +#ifndef QT_NO_WHATSTHIS +extern void qWhatsThisBDH(); +static TQMenuItem* whatsThisItem = 0; +#endif + +/*! + \class TQPopupMenu qpopupmenu.h + \brief The TQPopupMenu class provides a popup menu widget. + + \ingroup application + \ingroup basic + \mainclass + + A popup menu widget is a selection menu. It can be either a + pull-down menu in a menu bar or a standalone context (popup) menu. + Pull-down menus are shown by the menu bar when the user clicks on + the respective item or presses the specified shortcut key. Use + TQMenuBar::insertItem() to insert a popup menu into a menu bar. + Show a context menu either asynchronously with popup() or + synchronously with exec(). + + Technically, a popup menu consists of a list of menu items. You + add items with insertItem(). An item is either a string, a pixmap + or a custom item that provides its own drawing function (see + TQCustomMenuItem). In addition, items can have an optional icon + drawn on the very left side and an accelerator key such as + "Ctrl+X". + + There are three kinds of menu items: separators, menu items that + perform an action and menu items that show a submenu. Separators + are inserted with insertSeparator(). For submenus, you pass a + pointer to a TQPopupMenu in your call to insertItem(). All other + items are considered action items. + + When inserting action items you usually specify a receiver and a + slot. The receiver will be notifed whenever the item is selected. + In addition, TQPopupMenu provides two signals, activated() and + highlighted(), which signal the identifier of the respective menu + item. It is sometimes practical to connect several items to one + slot. To distinguish between them, specify a slot that takes an + integer argument and use setItemParameter() to associate a unique + value with each item. + + You clear a popup menu with clear() and remove single items with + removeItem() or removeItemAt(). + + A popup menu can display check marks for certain items when + enabled with setCheckable(TRUE). You check or uncheck items with + setItemChecked(). + + Items are either enabled or disabled. You toggle their state with + setItemEnabled(). Just before a popup menu becomes visible, it + emits the aboutToShow() signal. You can use this signal to set the + correct enabled/disabled states of all menu items before the user + sees it. The corresponding aboutToHide() signal is emitted when + the menu hides again. + + You can provide What's This? help for single menu items with + setWhatsThis(). See TQWhatsThis for general information about this + kind of lightweight online help. + + For ultimate flexibility, you can also add entire widgets as items + into a popup menu (for example, a color selector). + + A TQPopupMenu can also provide a tear-off menu. A tear-off menu is + a top-level window that 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 TQToolBar + 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 + TQMenuBar and TQPopupMenu use. + + \important insertItem removeItem removeItemAt clear text pixmap iconSet insertSeparator changeItem whatsThis setWhatsThis accel setAccel setItemEnabled isItemEnabled setItemVisible isItemVisible setItemChecked isItemChecked connectItem disconnectItem setItemParameter itemParameter + + <img src=qpopmenu-m.png> <img src=qpopmenu-w.png> + + \sa TQMenuBar + \link guibooks.html#fowler GUI Design Handbook: Menu, Drop-Down and + Pop-Up\endlink +*/ + + +/*! + \fn void TQPopupMenu::aboutToShow() + + This signal is emitted just before the popup menu is displayed. + You can connect it to any slot that sets up the menu contents + (e.g. to ensure that the right items are enabled). + + \sa aboutToHide(), setItemEnabled(), setItemChecked(), insertItem(), removeItem() +*/ + +/*! + \fn void TQPopupMenu::aboutToHide() + + This signal is emitted just before the popup menu is hidden after + it has been displayed. + + \warning Do not open a widget in a slot connected to this signal. + + \sa aboutToShow(), setItemEnabled(), setItemChecked(), insertItem(), removeItem() +*/ + + + +/***************************************************************************** + TQPopupMenu member functions + *****************************************************************************/ + +class TQMenuDataData { + // attention: also defined in qmenudata.cpp +public: + TQMenuDataData(); + TQGuardedPtr<TQWidget> aWidget; + int aInt; +}; + +class TQPopupMenuPrivate { +public: + struct Scroll { + enum { ScrollNone=0, ScrollUp=0x01, ScrollDown=0x02 }; + uint scrollable : 2; + uint direction : 1; + int topScrollableIndex, scrollableSize; + TQTime lastScroll; + TQTimer *scrolltimer; + } scroll; + TQSize calcSize; + TQRegion mouseMoveBuffer; + uint hasmouse : 1; + TQPoint ignoremousepos; +}; + +static TQPopupMenu* 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 TQObject). +*/ + +TQPopupMenu::TQPopupMenu( TQWidget *parent, const char *name ) + : TQFrame( parent, name, WType_Popup | WNoAutoErase ) +{ + d = new TQPopupMenuPrivate; + d->scroll.scrollableSize = d->scroll.topScrollableIndex = 0; + d->scroll.scrollable = TQPopupMenuPrivate::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( TQFrame::PopupPanel | TQFrame::Raised ); + setMouseTracking(style().styleHint(TQStyle::SH_PopupMenu_MouseTracking, this)); + style().polishPopupMenu( this ); + setBackgroundMode( PaletteButton ); + connectModalRecursionSafety = 0; + + setFocusPolicy( StrongFocus ); +#ifdef Q_WS_X11 + x11SetWindowType( X11WindowTypePopup ); +#endif +} + +/*! + Destroys the popup menu. +*/ + +TQPopupMenu::~TQPopupMenu() +{ + if ( syncMenu == this && qApp ) { + qApp->exit_loop(); + syncMenu = 0; + } + + if(d->scroll.scrolltimer) + delete d->scroll.scrolltimer; + + if ( isVisible() ) { + parentMenu = 0; + hidePopups(); + } + + delete (TQWidget*) TQMenuData::d->aWidget; // tear-off menu + + preventAnimation = FALSE; + delete d; +} + + +/*! + Updates the item with identity \a id. +*/ +void TQPopupMenu::updateItem( int id ) // update popup menu item +{ + updateRow( indexOf(id) ); +} + + +void TQPopupMenu::setCheckable( bool enable ) +{ + if ( isCheckable() != enable ) { + checkable = enable; + badSize = TRUE; + if ( TQMenuData::d->aWidget ) + ( (TQPopupMenu*)(TQWidget*)TQMenuData::d->aWidget)->setCheckable( enable ); + } +} + +/*! + \property TQPopupMenu::checkable + \brief whether the display of check marks on menu items is enabled + + When TRUE, the display of check marks on menu items is enabled. + Checking is always enabled when in Windows-style. + + \sa TQMenuData::setItemChecked() +*/ + +bool TQPopupMenu::isCheckable() const +{ + return checkable; +} + +void TQPopupMenu::menuContentsChanged() +{ + // here the part that can't be delayed + TQMenuData::menuContentsChanged(); + badSize = TRUE; // might change the size +#if defined(Q_WS_MAC) && !defined(TQMAC_QMENUBAR_NO_NATIVE) + mac_dirty_popup = 1; +#endif + if( pendingDelayedContentsChanges ) + return; + pendingDelayedContentsChanges = 1; + if( !pendingDelayedStateChanges ) // if the timer hasn't been started yet + TQTimer::singleShot( 0, this, SLOT(performDelayedChanges())); +} + +void TQPopupMenu::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(); + } + TQPopupMenu* p = (TQPopupMenu*)(TQWidget*)TQMenuData::d->aWidget; + if ( p && p->isVisible() ) { + p->updateSize(TRUE); + p->update(); + } +#if defined(Q_WS_MAC) && !defined(TQMAC_QMENUBAR_NO_NATIVE) + mac_dirty_popup = 1; +#endif +} + + +void TQPopupMenu::menuStateChanged() +{ + // here the part that can't be delayed + if( pendingDelayedStateChanges ) + return; + pendingDelayedStateChanges = 1; + if( !pendingDelayedContentsChanges ) // if the timer hasn't been started yet + TQTimer::singleShot( 0, this, SLOT(performDelayedChanges())); +} + +void TQPopupMenu::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 ( TQMenuData::d->aWidget ) + TQMenuData::d->aWidget->update(); +} + +void TQPopupMenu::performDelayedChanges() +{ + if( pendingDelayedContentsChanges ) + performDelayedContentsChanged(); + if( pendingDelayedStateChanges ) + performDelayedStateChanged(); +} + +void TQPopupMenu::menuInsPopup( TQPopupMenu *popup ) +{ + connect( popup, SIGNAL(activatedRedirect(int)), + SLOT(subActivated(int)) ); + connect( popup, SIGNAL(highlightedRedirect(int)), + SLOT(subHighlighted(int)) ); + connect( popup, SIGNAL(destroyed(TQObject*)), + this, SLOT(popupDestroyed(TQObject*)) ); +} + +void TQPopupMenu::menuDelPopup( TQPopupMenu *popup ) +{ + popup->disconnect( SIGNAL(activatedRedirect(int)) ); + popup->disconnect( SIGNAL(highlightedRedirect(int)) ); + disconnect( popup, SIGNAL(destroyed(TQObject*)), + this, SLOT(popupDestroyed(TQObject*)) ); +} + + +void TQPopupMenu::frameChanged() +{ + menuContentsChanged(); +} + +TQRect TQPopupMenu::screenRect( const TQPoint& pos ) +{ + int screen_num = TQApplication::desktop()->screenNumber( pos ); +#ifdef Q_WS_MAC + return TQApplication::desktop()->availableGeometry( screen_num ); +#else + return TQApplication::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 + TQWidget::mapToGlobal(). + + When positioning a popup with exec() or popup(), bear in mind that + you cannot rely on the popup menu's current size(). For + performance reasons, the popup adapts its size only when + necessary, so in many cases, the size before and after the show is + different. Instead, use sizeHint(). It calculates the proper size + depending on the menu's current contents. +*/ + +void TQPopupMenu::popup( const TQPoint &pos, int indexAtPoint ) +{ + if ( !isPopup() && isVisible() ) + hide(); + + //avoid circularity + if ( isVisible() || !isEnabled() ) + return; + +#if defined(Q_WS_MAC) && !defined(TQMAC_QMENUBAR_NO_NATIVE) + if( macPopupMenu(pos, indexAtPoint )) + return; +#endif + +#if (QT_VERSION-0 >= 0x040000) +#error "Fix this now" + // #### should move to TQWidget - anything might need this functionality, + // #### since anything can have WType_Popup window flag. + // #### This includes stuff in TQPushButton and some stuff for setting + // #### the geometry of TQDialog. + // TQPopupMenu + // ::exec() + // ::popup() + // TQPushButton (shouldn't retquire TQMenuPopup) + // ::popupPressed + // Some stuff in qwidget.cpp for dialogs... can't remember exactly. + // Also the code here indicatets the parameter should be a rect, not a + // point. +#endif + + TQRect screen = screenRect( geometry().center()); + TQRect screen2 = screenRect( TQApplication::reverseLayout() + ? pos+TQPoint(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 = TQPopupMenuPrivate::Scroll::ScrollNone; + d->scroll.topScrollableIndex = d->scroll.scrollableSize = 0; + badSize = TRUE; + } + updateSize(); + + TQPoint mouse = TQCursor::pos(); + snapToMouse = pos == mouse; + + // have to emit here as a menu might be setup in a slot connected + // to aboutToShow which will change the size of the menu + bool s = supressAboutToShow; + supressAboutToShow = TRUE; + if ( !s) { + emit aboutToShow(); + updateSize(TRUE); + } + + int 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 + TQMenuData *top = this; // find top level + while ( top->parentMenu ) + top = top->parentMenu; + if( top->isMenuBar ) + x11SetWindowType( X11WindowTypeDropdown ); + if( parentMenu && parentMenu->isMenuBar ) + x11SetWindowTransient( static_cast< TQMenuBar* >( parentMenu )->topLevelWidget()); +#endif + if( parentMenu && !parentMenu->isMenuBar ) + x11SetWindowTransient( static_cast< TQPopupMenu* >( parentMenu )); + if( !parentMenu ) { + // hackish ... try to find the main window related to this popup + TQWidget* parent = parentWidget() ? parentWidget()->topLevelWidget() : NULL; + if( parent == NULL ) + parent = TQApplication::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(TQStyle::SH_PopupMenu_Scrollable, this)) { + int off_top = 0, off_bottom = 0; + if(y+h > sy+sh) + off_bottom = (y+h) - (sy+sh); + if(y < sy) + off_top = sy - y; + if(off_bottom || off_top) { + int ch = updateSize().height(); //store the old height, before setting scrollable --Sam + const int vextra = style().pixelMetric(TQStyle::PM_PopupMenuFrameVerticalExtra, this); + d->scroll.scrollableSize = h - off_top - off_bottom - 2*vextra; + if(off_top) { + move( x, y = sy ); + d->scroll.scrollable = d->scroll.scrollable | TQPopupMenuPrivate::Scroll::ScrollUp; + } + if( off_bottom ) + d->scroll.scrollable = d->scroll.scrollable | TQPopupMenuPrivate::Scroll::ScrollDown; + if( off_top != off_bottom && indexAtPoint >= 0 ) { + ch -= (vextra * 2); + if(ch > sh) //no bigger than the screen! + ch = sh; + if( ch > d->scroll.scrollableSize ) + d->scroll.scrollableSize = ch; + } + + updateSize(TRUE); //now set the size using the scrollable/scrollableSize as above + w = width(); + h = height(); + if(indexAtPoint >= 0) { + if(off_top) { //scroll to it + register TQMenuItem *mi = NULL; + TQMenuItemListIt it(*mitems); + for(int tmp_y = 0; tmp_y < off_top && (mi=it.current()); ) { + TQSize sz = style().sizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemHeight( mi )), + TQStyleOption(mi,maxPMWidth,0)); + tmp_y += sz.height(); + d->scroll.topScrollableIndex++; + } + } + } + } + } + move( x, y ); + motion=0; + actItem = -1; + +#ifndef QT_NO_EFFECTS + int hGuess = qApp->reverseLayout() ? TQEffects::LeftScroll : TQEffects::RightScroll; + int vGuess = TQEffects::DownScroll; + if ( qApp->reverseLayout() ) { + if ( snapToMouse && ( x + w/2 > mouse.x() ) || + ( parentMenu && parentMenu->isPopupMenu && + ( x + w/2 > ((TQPopupMenu*)parentMenu)->x() ) ) ) + hGuess = TQEffects::RightScroll; + } else { + if ( snapToMouse && ( x + w/2 < mouse.x() ) || + ( parentMenu && parentMenu->isPopupMenu && + ( x + w/2 < ((TQPopupMenu*)parentMenu)->x() ) ) ) + hGuess = TQEffects::LeftScroll; + } + +#ifndef QT_NO_MENUBAR + if ( snapToMouse && ( y + h/2 < mouse.y() ) || + ( parentMenu && parentMenu->isMenuBar && + ( y + h/2 < ((TQMenuBar*)parentMenu)->mapToGlobal( ((TQMenuBar*)parentMenu)->pos() ).y() ) ) ) + vGuess = TQEffects::UpScroll; +#endif + + if ( TQApplication::isEffectEnabled( UI_AnimateMenu ) && + preventAnimation == FALSE ) { + if ( TQApplication::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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::PopupMenuStart ); +#endif +} + +/*! + \fn void TQPopupMenu::activated( int id ) + + This signal is emitted when a menu item is selected; \a id is the + id of the selected item. + + Normally, you connect each menu item to a single slot using + TQMenuData::insertItem(), but sometimes you will want to connect + several items to a single slot (most often if the user selects + from an array). This signal is useful in such cases. + + \sa highlighted(), TQMenuData::insertItem() +*/ + +/*! + \fn void TQPopupMenu::highlighted( int id ) + + This signal is emitted when a menu item is highlighted; \a id is + the id of the highlighted item. + + \sa activated(), TQMenuData::insertItem() +*/ + +/*! \fn void TQPopupMenu::highlightedRedirect( int id ) + \internal + Used internally to connect submenus to their parents. +*/ + +/*! \fn void TQPopupMenu::activatedRedirect( int id ) + \internal + Used internally to connect submenus to their parents. +*/ + +void TQPopupMenu::subActivated( int id ) +{ + emit activatedRedirect( id ); +} + +void TQPopupMenu::subHighlighted( int id ) +{ + emit highlightedRedirect( id ); +} + +static bool fromAccel = FALSE; + +#ifndef QT_NO_ACCEL +void TQPopupMenu::accelActivated( int id ) +{ + TQMenuItem *mi = findItem( id ); + if ( mi && mi->isEnabledAndVisible() ) { + TQGuardedPtr<TQSignal> signal = mi->signal(); + fromAccel = TRUE; + actSig( mi->id() ); + fromAccel = FALSE; + if ( signal ) + signal->activate(); + } +} + +void TQPopupMenu::accelDestroyed() // accel about to be deleted +{ + autoaccel = 0; // don't delete it twice! +} +#endif //QT_NO_ACCEL + +void TQPopupMenu::popupDestroyed( TQObject *o ) +{ + removePopup( (TQPopupMenu*)o ); +} + +void TQPopupMenu::actSig( int id, bool inwhatsthis ) +{ + if ( !inwhatsthis ) { + emit activated( id ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( !fromAccel ) + TQAccessible::updateAccessibility( this, indexOf(id)+1, TQAccessible::MenuCommand ); +#endif + } else { +#ifndef QT_NO_WHATSTHIS + TQRect r( itemGeometry( indexOf( id ) ) ); + TQPoint p( r.center().x(), r.bottom() ); + TQString whatsThis = findItem( id )->whatsThis(); + if ( whatsThis.isNull() ) + whatsThis = TQWhatsThis::textFor( this, p ); + TQWhatsThis::leaveWhatsThisMode( whatsThis, mapToGlobal( p ), this ); +#endif + } + + emit activatedRedirect( id ); +} + +void TQPopupMenu::hilitSig( int id ) +{ + emit highlighted( id ); + emit highlightedRedirect( id ); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, indexOf(id)+1, TQAccessible::Focus ); + TQAccessible::updateAccessibility( this, indexOf(id)+1, TQAccessible::Selection ); +#endif +} + +void TQPopupMenu::setFirstItemActive() +{ + TQMenuItemListIt it(*mitems); + register TQMenuItem *mi; + int ai = 0; + if(d->scroll.scrollable) + ai = d->scroll.topScrollableIndex; + while ( (mi=it.current()) ) { + ++it; + if ( !mi->isSeparator() && mi->id() != TQMenuData::d->aInt && + ( style().styleHint( TQStyle::SH_PopupMenu_AllowActiveAndDisabled, this ) || mi->isEnabledAndVisible() )) { + setActiveItem( ai ); + return; + } + ai++; + } + actItem = -1; +} + +/*! + \internal + Hides all popup menus (in this menu tree) that are currently open. +*/ + +void TQPopupMenu::hideAllPopups() +{ + register TQMenuData *top = this; // find top level popup + if ( !preventAnimation ) + TQTimer::singleShot( 10, this, SLOT(allowAnimation()) ); + preventAnimation = TRUE; + + if ( !isPopup() ) + return; // nothing to do + + while ( top->parentMenu && top->parentMenu->isPopupMenu + && ((TQPopupMenu*)top->parentMenu)->isPopup() ) + top = top->parentMenu; + ((TQPopupMenu*)top)->hide(); // cascade from top level + +#ifndef QT_NO_WHATSTHIS + if (whatsThisItem) { + qWhatsThisBDH(); + whatsThisItem = 0; + } +#endif + +} + +/*! + \internal + Hides all popup sub-menus. +*/ + +void TQPopupMenu::hidePopups() +{ + if ( !preventAnimation ) + TQTimer::singleShot( 10, this, SLOT(allowAnimation()) ); + preventAnimation = TRUE; + + TQMenuItemListIt it(*mitems); + register TQMenuItem *mi; + while ( (mi=it.current()) ) { + ++it; + if ( mi->popup() && mi->popup()->parentMenu == this ) //avoid circularity + mi->popup()->hide(); + } + popupActive = -1; // no active sub menu + if(style().styleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, this)) + d->mouseMoveBuffer = TQRegion(); + + TQRect mfrect = itemGeometry( actItem ); + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); +} + + +/*! + \internal + Sends the event to the menu bar. +*/ + +bool TQPopupMenu::tryMenuBar( TQMouseEvent *e ) +{ + register TQMenuData *top = this; // find top level + while ( top->parentMenu ) + top = top->parentMenu; +#ifndef QT_NO_MENUBAR + return top->isMenuBar ? + ((TQMenuBar *)top)->tryMouseEvent( this, e ) : + ((TQPopupMenu*)top)->tryMouseEvent(this, e ); +#else + return ((TQPopupMenu*)top)->tryMouseEvent(this, e ); +#endif +} + + +/*! + \internal +*/ +bool TQPopupMenu::tryMouseEvent( TQPopupMenu *p, TQMouseEvent * e) +{ + if ( p == this ) + return FALSE; + TQPoint pos = mapFromGlobal( e->globalPos() ); + if ( !rect().contains( pos ) ) // outside + return FALSE; + TQMouseEvent ee( e->type(), pos, e->globalPos(), e->button(), e->state() ); + event( &ee ); + return TRUE; +} + +/*! + \internal + Tells the menu bar to go back to idle state. +*/ + +void TQPopupMenu::byeMenuBar() +{ +#ifndef QT_NO_MENUBAR + register TQMenuData *top = this; // find top level + while ( top->parentMenu ) + top = top->parentMenu; +#endif + hideAllPopups(); +#ifndef QT_NO_MENUBAR + if ( top->isMenuBar ) + ((TQMenuBar *)top)->goodbye(); +#endif +} + + +/*! + \internal + Return the item at \a pos, or -1 if there is no item there or if + it is a separator item. +*/ + +int TQPopupMenu::itemAtPos( const TQPoint &pos, bool ignoreSeparator ) const +{ + if ( !contentsRect().contains(pos) ) + return -1; + + int row = 0; + int x = contentsRect().x(); + int y = contentsRect().y(); + TQMenuItem *mi; + TQMenuItemListIt it( *mitems ); + if(d->scroll.scrollable) { + if(d->scroll.topScrollableIndex) { + for( ; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++) + ++it; + if(!mi) { + row = 0; + it.toFirst(); + } + y += style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + } + } + int itemw = contentsRect().width() / ncols; + TQSize sz; + while ( (mi=it.current()) ) { + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + y >= contentsRect().height() - style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this)) + return -1; + ++it; + if ( !mi->isVisible() ) { + ++row; + continue; + } + int itemh = itemHeight( mi ); + + sz = style().sizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemh), + TQStyleOption(mi,maxPMWidth)); + sz = sz.expandedTo(TQSize(itemw, sz.height())); + itemw = sz.width(); + itemh = sz.height(); + + if ( ncols > 1 && y + itemh > contentsRect().bottom() ) { + y = contentsRect().y(); + x +=itemw; + } + if ( TQRect( x, y, itemw, itemh ).contains( pos ) ) + break; + y += itemh; + ++row; + } + + if ( mi && ( !ignoreSeparator || !mi->isSeparator() ) ) + return row; + return -1; +} + +/*! + \internal + Returns the geometry of item number \a index. +*/ + +TQRect TQPopupMenu::itemGeometry( int index ) +{ + TQMenuItem *mi; + TQSize sz; + int row = 0, scrollh = 0; + int x = contentsRect().x(); + int y = contentsRect().y(); + TQMenuItemListIt it( *mitems ); + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp) { + scrollh = style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + y += scrollh; + if(d->scroll.topScrollableIndex) { + for( ; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++) + ++it; + if(!mi) { + row = 0; + it.toFirst(); + } + } + } + int itemw = contentsRect().width() / ncols; + while ( (mi=it.current()) ) { + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + y >= contentsRect().height() - scrollh) + break; + ++it; + if ( !mi->isVisible() ) { + ++row; + continue; + } + int itemh = itemHeight( mi ); + + sz = style().sizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemh), + TQStyleOption(mi,maxPMWidth)); + sz = sz.expandedTo(TQSize(itemw, sz.height())); + itemw = sz.width(); + itemh = sz.height(); + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + (y + itemh > contentsRect().height() - scrollh)) + itemh -= (y + itemh) - (contentsRect().height() - scrollh); + if ( ncols > 1 && y + itemh > contentsRect().bottom() ) { + y = contentsRect().y(); + x +=itemw; + } + if ( row == index ) + return TQRect( x,y,itemw,itemh ); + y += itemh; + ++row; + } + + return TQRect(0,0,0,0); +} + + +/*! + \internal + Calculates and sets the size of the popup menu, based on the size + of the items. +*/ + +TQSize TQPopupMenu::updateSize(bool force_update, bool do_resize) +{ + polish(); + if ( count() == 0 ) { + TQSize ret = TQSize( 50, 8 ); + if(do_resize) + setFixedSize( ret ); + badSize = TRUE; + return ret; + } + + int scrheight = 0; + if(d->scroll.scrollableSize) { + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp) + scrheight += style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown) + scrheight += style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + } + + if(badSize || force_update) { +#ifndef QT_NO_ACCEL + updateAccel( 0 ); +#endif + int height = 0; + int max_width = 0, max_height = 0; + TQFontMetrics fm = fontMetrics(); + register TQMenuItem *mi; + maxPMWidth = 0; + int maxWidgetWidth = 0; + tab = 0; + + for ( TQMenuItemListIt it( *mitems ); it.current(); ++it ) { + mi = it.current(); + TQWidget *miw = mi->widget(); + if (miw) { + if ( miw->parentWidget() != this ) + miw->reparent( this, TQPoint(0,0), TRUE ); + // widget items musn't propgate mouse events + ((TQPopupMenu*)miw)->setWFlags(WNoMousePropagation); + } + if ( mi->custom() ) + mi->custom()->setFont( font() ); + if ( mi->iconSet() != 0) + maxPMWidth = TQMAX( maxPMWidth, + mi->iconSet()->pixmap( TQIconSet::Small, TQIconSet::Normal ).width() + 4 ); + } + + int dh = screenRect( geometry().center()).height(); + ncols = 1; + + for ( TQMenuItemListIt it2( *mitems ); it2.current(); ++it2 ) { + mi = it2.current(); + if ( !mi->isVisible() ) + continue; + int w = 0; + int itemHeight = TQPopupMenu::itemHeight( mi ); + + if ( mi->widget() ) { + TQSize s( mi->widget()->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 = TQMAX( maxWidgetWidth, + mi->custom()->sizeHint().width() ); + } else { + TQSize s ( mi->custom()->sizeHint() ); + w += s.width(); + } + } + + w += maxPMWidth; + + if (! mi->text().isNull()) { + TQString 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() ) { + TQSize s ( mi->custom()->sizeHint() ); + w += s.width(); + } else { + w = itemHeight = 2; + } + } + + TQSize sz = style().sizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(w, itemHeight), + TQStyleOption(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( "TQPopupMenu: (%s) Popup has invalid menu item", + name( "unnamed" ) ); +#endif + } + height += itemHeight; + if(style().styleHint(TQStyle::SH_PopupMenu_Scrollable, this)) { + if(scrheight && height >= d->scroll.scrollableSize - scrheight) { + height = d->scroll.scrollableSize - scrheight; + break; + } + } else if( height + 2*frameWidth() >= dh ) { + ncols++; + max_height = TQMAX(max_height, height - itemHeight); + height = itemHeight; + } + if ( w > max_width ) + max_width = w; + } + if( ncols == 1 && !max_height ) + max_height = height; + + if(style().styleHint(TQStyle::SH_PopupMenu_Scrollable, this)) { + height += scrheight; + setMouseTracking(TRUE); + } + + if ( tab ) + tab -= fontMetrics().minRightBearing(); + else + max_width -= fontMetrics().minRightBearing(); + + if ( max_width + tab < maxWidgetWidth ) + max_width = maxWidgetWidth - tab; + + const int fw = frameWidth(); + int extra_width = (fw+style().pixelMetric(TQStyle::PM_PopupMenuFrameHorizontalExtra, this)) * 2, + extra_height = (fw+style().pixelMetric(TQStyle::PM_PopupMenuFrameVerticalExtra, this)) * 2; + if ( ncols == 1 ) + d->calcSize = TQSize( TQMAX( minimumWidth(), max_width + tab + extra_width ), + TQMAX( minimumHeight() , height + extra_height ) ); + else + d->calcSize = TQSize( TQMAX( minimumWidth(), (ncols*(max_width + tab)) + extra_width ), + TQMAX( minimumHeight(), TQMIN( max_height + extra_height + 1, dh ) ) ); + badSize = FALSE; + } + + { + // Position the widget items. It could be done in drawContents + // but this way we get less flicker. + TQSize sz; + int x = contentsRect().x(); + int y = contentsRect().y(); + int itemw = contentsRect().width() / ncols; + for(TQMenuItemListIt it(*mitems); it.current(); ++it) { + TQMenuItem *mi = it.current(); + if ( !mi->isVisible() ) + continue; + + int itemh = itemHeight( mi ); + + sz = style().sizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemh), TQStyleOption(mi,maxPMWidth)); + sz = sz.expandedTo(TQSize(itemw, sz.height())); + itemw = sz.width(); + itemh = sz.height(); + + if ( ncols > 1 && y + itemh > contentsRect().bottom() ) { + y = contentsRect().y(); + x +=itemw; + } + if ( mi->widget() ) + mi->widget()->setGeometry( x, y, itemw, mi->widget()->height() ); + y += itemh; + } + } + + if( do_resize && size() != d->calcSize ) { + setMaximumSize( d->calcSize ); + d->calcSize = 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 TQPopupMenu::updateAccel( TQWidget *parent ) +{ + TQMenuItemListIt it(*mitems); + register TQMenuItem *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 { + TQWidget *w = (TQWidget *) 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 TQAccel( 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; + TQKeySequence 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() ) { + TQString 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 ) { + TQString t = (TQString)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 + TQPopupMenu* 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 TQPopupMenu::enableAccel( bool enable ) +{ + if ( autoaccel ) + autoaccel->setEnabled( enable ); + accelDisabled = !enable; // rememeber when updateAccel + TQMenuItemListIt it(*mitems); + register TQMenuItem *mi; + while ( (mi=it.current()) ) { // do the same for sub popups + ++it; + if ( mi->popup() ) // call recursively + mi->popup()->enableAccel( enable ); + } +} +#endif + +/*! + \reimp +*/ +void TQPopupMenu::setFont( const TQFont &font ) +{ + TQWidget::setFont( font ); + badSize = TRUE; + if ( isVisible() ) { + updateSize(); + update(); + } +} + +/*! + \reimp +*/ +void TQPopupMenu::show() +{ + if ( !isPopup() && isVisible() ) + hide(); + + if ( isVisible() ) { + supressAboutToShow = FALSE; + TQWidget::show(); + return; + } + if (!supressAboutToShow) + emit aboutToShow(); + else + supressAboutToShow = FALSE; + performDelayedChanges(); + updateSize(TRUE); + TQWidget::show(); + popupActive = -1; + if(style().styleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, this)) + d->mouseMoveBuffer = TQRegion(); + d->ignoremousepos = TQCursor::pos(); +} + +/*! + \reimp +*/ + +void TQPopupMenu::hide() +{ + if ( syncMenu == this && qApp ) { + qApp->exit_loop(); + syncMenu = 0; + } + + if ( !isVisible() ) { + TQWidget::hide(); + return; + } + emit aboutToHide(); + + actItem = popupActive = -1; + if(style().styleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, this)) + d->mouseMoveBuffer = TQRegion(); + mouseBtDn = FALSE; // mouse button up +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::PopupMenuEnd ); +#endif +#ifndef QT_NO_MENUBAR + TQMenuData *top = this; // find top level + while ( top->parentMenu ) + top = top->parentMenu; + if( top->isMenuBar ) + x11SetWindowType( X11WindowTypePopup ); // reset +#endif + parentMenu = 0; + hidePopups(); + TQWidget::hide(); +} + + +/*! + Calculates the height in pixels of the item in row \a row. +*/ +int TQPopupMenu::itemHeight( int row ) const +{ + return itemHeight( mitems->at( row ) ); +} + +/*! + \overload + + Calculates the height in pixels of the menu item \a mi. +*/ +int TQPopupMenu::itemHeight( TQMenuItem *mi ) const +{ + if ( mi->widget() ) + return mi->widget()->height(); + if ( mi->custom() && mi->custom()->fullSpan() ) + return mi->custom()->sizeHint().height(); + + TQFontMetrics fm(fontMetrics()); + int h = 0; + if ( mi->isSeparator() ) // separator height + h = 2; + else if ( mi->pixmap() ) // pixmap height + h = mi->pixmap()->height(); + else // text height + h = fm.height(); + + if ( !mi->isSeparator() && mi->iconSet() != 0 ) + h = TQMAX(h, mi->iconSet()->pixmap( TQIconSet::Small, + TQIconSet::Normal ).height()); + if ( mi->custom() ) + h = TQMAX(h, mi->custom()->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 TQStyle::drawControl() +*/ +void TQPopupMenu::drawItem( TQPainter* p, int tab_, TQMenuItem* mi, + bool act, int x, int y, int w, int h) +{ + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled() && mi->isEnabledAndVisible() && (!mi->popup() || mi->popup()->isEnabled()) ) + flags |= TQStyle::Style_Enabled; + if (act) + flags |= TQStyle::Style_Active; + if (mouseBtDn) + flags |= TQStyle::Style_Down; + + const TQColorGroup &cg = ((flags&TQStyle::Style_Enabled) ? colorGroup() : palette().disabled() ); + + if ( mi->custom() && mi->custom()->fullSpan() ) { + TQMenuItem dummy; + style().drawControl(TQStyle::CE_PopupMenuItem, p, this, TQRect(x, y, w, h), cg, + flags, TQStyleOption(&dummy,maxPMWidth,tab_)); + mi->custom()->paint( p, cg, act, flags&TQStyle::Style_Enabled, x, y, w, h ); + } else + style().drawControl(TQStyle::CE_PopupMenuItem, p, this, TQRect(x, y, w, h), cg, + flags, TQStyleOption(mi,maxPMWidth,tab_)); +} + + +/*! + Draws all menu items using painter \a p. +*/ +void TQPopupMenu::drawContents( TQPainter* p ) +{ + TQMenuItemListIt it(*mitems); + TQMenuItem *mi = 0; + int row = 0; + int x = contentsRect().x(); + int y = contentsRect().y(); + if(d->scroll.scrollable) { + if(d->scroll.topScrollableIndex) { + for( ; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++) + ++it; + if(!mi) + it.toFirst(); + } + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp) { + TQRect rect(x, y, contentsRect().width(), + style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this)); + if(!p->hasClipping() || p->clipRegion().contains(rect)) { + TQStyle::SFlags flags = TQStyle::Style_Up; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + style().drawControl(TQStyle::CE_PopupMenuScroller, p, this, rect, + colorGroup(), flags, TQStyleOption(maxPMWidth)); + } + y += rect.height(); + } + } + + int itemw = contentsRect().width() / ncols; + TQSize sz; + TQStyle::SFlags flags; + while ( (mi=it.current()) ) { + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + y >= contentsRect().height() - style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this)) + break; + ++it; + if ( !mi->isVisible() ) { + ++row; + continue; + } + int itemh = itemHeight( mi ); + sz = style().sizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemh), + TQStyleOption(mi,maxPMWidth,0) + ); + sz = sz.expandedTo(TQSize(itemw, sz.height())); + itemw = sz.width(); + itemh = sz.height(); + + if ( ncols > 1 && y + itemh > contentsRect().bottom() ) { + if ( y < contentsRect().bottom() ) { + TQRect rect(x, y, itemw, contentsRect().bottom() - y); + if(!p->hasClipping() || p->clipRegion().contains(rect)) { + flags = TQStyle::Style_Default; + if (isEnabled() && mi->isEnabledAndVisible()) + flags |= TQStyle::Style_Enabled; + style().drawControl(TQStyle::CE_PopupMenuItem, p, this, rect, + colorGroup(), flags, TQStyleOption((TQMenuItem*)0,maxPMWidth)); + } + } + y = contentsRect().y(); + x +=itemw; + } + if (!mi->widget() && (!p->hasClipping() || p->clipRegion().contains(TQRect(x, y, itemw, itemh)))) + drawItem( p, tab, mi, row == actItem, x, y, itemw, itemh ); + y += itemh; + ++row; + } + if ( y < contentsRect().bottom() ) { + TQRect rect(x, y, itemw, contentsRect().bottom() - y); + if(!p->hasClipping() || p->clipRegion().contains(rect)) { + flags = TQStyle::Style_Default; + if ( isEnabled() ) + flags |= TQStyle::Style_Enabled; + style().drawControl(TQStyle::CE_PopupMenuItem, p, this, rect, + colorGroup(), flags, TQStyleOption((TQMenuItem*)0,maxPMWidth)); + } + } + if( d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown ) { + int sh = style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + TQRect rect(x, contentsRect().height() - sh, contentsRect().width(), sh); + if(!p->hasClipping() || p->clipRegion().contains(rect)) { + TQStyle::SFlags flags = TQStyle::Style_Down; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + style().drawControl(TQStyle::CE_PopupMenuScroller, p, this, rect, + colorGroup(), flags, TQStyleOption(maxPMWidth)); + } + } +#if defined( DEBUG_SLOPPY_SUBMENU ) + if ( style().styleHint(TQStyle::SH_PopupMenu_SloppySubMenus, this )) { + p->setClipRegion( d->mouseMoveBuffer ); + p->fillRect( d->mouseMoveBuffer.boundingRect(), colorGroup().brush( TQColorGroup::Highlight ) ); + } +#endif +} + + +/***************************************************************************** + Event handlers + *****************************************************************************/ + +/*! + \reimp +*/ + +void TQPopupMenu::paintEvent( TQPaintEvent *e ) +{ + TQFrame::paintEvent( e ); +} + +/*! + \reimp +*/ + +void TQPopupMenu::closeEvent( TQCloseEvent * e) { + e->accept(); + byeMenuBar(); +} + + +/*! + \reimp +*/ + +void TQPopupMenu::mousePressEvent( TQMouseEvent *e ) +{ + int sh = style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if (rect().contains(e->pos()) && + ((d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) || //up + (d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + e->pos().y() >= contentsRect().height() - sh))) //down + return; + + mouseBtDn = TRUE; // mouse button down + int item = itemAtPos( e->pos() ); + if ( item == -1 ) { + if ( !rect().contains(e->pos()) && !tryMenuBar(e) ) { + byeMenuBar(); + } + return; + } + register TQMenuItem *mi = mitems->at(item); + if ( item != actItem ) // new item activated + setActiveItem( item ); + + TQPopupMenu *popup = mi->popup(); + if ( popup ) { + if ( popup->isVisible() ) { // sub menu already open + int pactItem = popup->actItem; + popup->actItem = -1; + popup->hidePopups(); + popup->updateRow( pactItem ); + } else { // open sub menu + hidePopups(); + popupSubMenuLater( 20, this ); + } + } else { + hidePopups(); + } +} + +/*! + \reimp +*/ + +void TQPopupMenu::mouseReleaseEvent( TQMouseEvent *e ) +{ + // do not hide a standalone context menu on press-release, unless + // the user moved the mouse significantly + if ( !parentMenu && !mouseBtDn && actItem < 0 && motion < 6 ) + return; + + mouseBtDn = FALSE; + + // if the user released the mouse outside the menu, pass control + // to the menubar or our parent menu + int sh = style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if ( !rect().contains( e->pos() ) && tryMenuBar(e) ) + return; + else if((d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) || //up + (d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + e->pos().y() >= contentsRect().height() - sh)) //down + return; + + if ( actItem < 0 ) { // we do not have an active item + // if the release is inside without motion (happens with + // oversized popup menus on small screens), ignore it + if ( rect().contains( e->pos() ) && motion < 6 ) + return; + else + byeMenuBar(); + } else { // selected menu item! + register TQMenuItem *mi = mitems->at(actItem); + if ( mi ->widget() ) { + TQWidget* widgetAt = TQApplication::widgetAt( e->globalPos(), TRUE ); + if ( widgetAt && widgetAt != this ) { + TQMouseEvent me( e->type(), widgetAt->mapFromGlobal( e->globalPos() ), + e->globalPos(), e->button(), e->state() ); + TQApplication::sendEvent( widgetAt, &me ); + } + } + TQPopupMenu *popup = mi->popup(); +#ifndef QT_NO_WHATSTHIS + bool b = TQWhatsThis::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; + TQGuardedPtr<TQSignal> signal = mi->signal(); + actSig( mi->id(), b ); + if ( signal && !b ) + signal->activate(); + active_popup_menu = 0; + } + } + } +} + +/*! + \reimp +*/ + +void TQPopupMenu::mouseMoveEvent( TQMouseEvent *e ) +{ + if( e->globalPos() == d->ignoremousepos ) { + return; + } + d->ignoremousepos = TQPoint(); + + motion++; + + if ( parentMenu && parentMenu->isPopupMenu ) { + TQPopupMenu* p = (TQPopupMenu*)parentMenu; + int myIndex; + + p->findPopup( this, &myIndex ); + TQPoint pPos = p->mapFromParent( e->globalPos() ); + if ( p->actItem != myIndex && !p->rect().contains( pPos ) ) + p->setActiveItem( myIndex ); + + if ( style().styleHint(TQStyle::SH_PopupMenu_SloppySubMenus, this )) { + p->d->mouseMoveBuffer = TQRegion(); +#ifdef DEBUG_SLOPPY_SUBMENU + p->repaint(); +#endif + } + } + + if ( (e->state() & TQt::MouseButtonMask) == 0 && + !hasMouseTracking() ) + return; + + if(d->scroll.scrollable && e->pos().x() >= rect().x() && e->pos().x() <= rect().width()) { + int sh = style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if((d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) || + (d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && e->pos().y() >= height()-sh)) { + if(!d->scroll.scrolltimer) { + d->scroll.scrolltimer = new TQTimer(this, "popup scroll timer"); + TQObject::connect( d->scroll.scrolltimer, 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(TQStyle::SH_PopupMenu_SubMenuPopupDelay, + this), this); + } + } else { // mouse on valid item + // but did not register mouse press + d->hasmouse = 1; + if ( (e->state() & TQt::MouseButtonMask) && !mouseBtDn ) + mouseBtDn = TRUE; // so mouseReleaseEvent will pop down + + register TQMenuItem *mi = mitems->at( item ); + + if ( mi->widget() ) { + TQWidget* widgetAt = TQApplication::widgetAt( e->globalPos(), TRUE ); + if ( widgetAt && widgetAt != this ) { + TQMouseEvent me( e->type(), widgetAt->mapFromGlobal( e->globalPos() ), + e->globalPos(), e->button(), e->state() ); + TQApplication::sendEvent( widgetAt, &me ); + } + } + + if ( actItem == item ) + return; + + if ( style().styleHint(TQStyle::SH_PopupMenu_SloppySubMenus, this) && + d->mouseMoveBuffer.contains( e->pos() ) ) { + actItem = item; + popupSubMenuLater( style().styleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, this) * 6, + this ); + return; + } + + if ( mi->popup() || ( popupActive >= 0 && popupActive != item )) + popupSubMenuLater( style().styleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, this), + this ); + else if ( singleSingleShot ) + singleSingleShot->stop(); + + if ( item != actItem ) + setActiveItem( item ); + } +} + + +/*! + \reimp +*/ + +void TQPopupMenu::keyPressEvent( TQKeyEvent *e ) +{ + /* + I get nothing but complaints about this. -Brad + + - if (mouseBtDn && actItem >= 0) { + - if (e->key() == Key_Shift || + - e->key() == Key_Control || + - e->key() == Key_Alt) + - return; + - + - TQMenuItem *mi = mitems->at(actItem); + - int modifier = (((e->state() & ShiftButton) ? SHIFT : 0) | + - ((e->state() & ControlButton) ? CTRL : 0) | + - ((e->state() & AltButton) ? ALT : 0)); + - + - #ifndef QT_NO_ACCEL + - if (mi) + - setAccel(modifier + e->key(), mi->id()); + - #endif + - return; + - } + */ + + TQMenuItem *mi = 0; + TQPopupMenu *popup; + int dy = 0; + bool ok_key = TRUE; + + int key = e->key(); + if ( TQApplication::reverseLayout() ) { + // in reverse mode opening and closing keys for submenues are reversed + if ( key == 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(TQStyle::SH_MenuBar_AltKeyNavigation, this) ) + byeMenuBar(); + break; + + case Key_Escape: + if ( tornOff ) { + close(); + return; + } + // just hide one + { + TQMenuData* p = parentMenu; + hide(); +#ifndef QT_NO_MENUBAR + if ( p && p->isMenuBar ) + ((TQMenuBar*) p)->goodbye( TRUE ); +#endif + } + break; + + case Key_Left: + if ( ncols > 1 && actItem >= 0 ) { + TQRect r( itemGeometry( actItem ) ); + int newActItem = itemAtPos( TQPoint( r.left() - 1, r.center().y() ) ); + if ( newActItem >= 0 ) { + setActiveItem( newActItem ); + break; + } + } + if ( parentMenu && parentMenu->isPopupMenu ) { + ((TQPopupMenu *)parentMenu)->hidePopups(); + if ( singleSingleShot ) + singleSingleShot->stop(); + break; + } + + ok_key = FALSE; + break; + + case Key_Right: + if ( actItem >= 0 && ( mi=mitems->at(actItem) )->isEnabledAndVisible() && (popup=mi->popup()) ) { + hidePopups(); + if ( singleSingleShot ) + singleSingleShot->stop(); + // ### The next two lines were switched to fix the problem with the first item of the + // submenu not being highlighted...any reason why they should have been the other way?? + subMenuTimer(); + popup->setFirstItemActive(); + break; + } else if ( actItem == -1 && ( parentMenu && !parentMenu->isMenuBar )) { + dy = 1; + break; + } + if ( ncols > 1 && actItem >= 0 ) { + TQRect r( itemGeometry( actItem ) ); + int newActItem = itemAtPos( TQPoint( r.right() + 1, r.center().y() ) ); + if ( newActItem >= 0 ) { + setActiveItem( newActItem ); + break; + } + } + ok_key = FALSE; + break; + + case Key_Space: + if (! style().styleHint(TQStyle::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 = TQWhatsThis::inWhatsThisMode(); +#else + const bool b = FALSE; +#endif + mi = mitems->at( actItem ); + if ( !mi->isEnabled() && !b ) + break; + popup = mi->popup(); + if ( popup ) { + hidePopups(); + popupSubMenuLater( 20, this ); + popup->setFirstItemActive(); + } else { + actItem = -1; + updateItem( mi->id() ); + byeMenuBar(); + if ( mi->isEnabledAndVisible() || b ) { + active_popup_menu = this; + TQGuardedPtr<TQSignal> signal = mi->signal(); + actSig( mi->id(), b ); + if ( signal && !b ) + signal->activate(); + active_popup_menu = 0; + } + } + } + break; +#ifndef QT_NO_WHATSTHIS + case Key_F1: + if ( actItem < 0 || e->state() != ShiftButton) + break; + mi = mitems->at( actItem ); + if ( !mi->whatsThis().isNull() ){ + if ( !TQWhatsThis::inWhatsThisMode() ) + TQWhatsThis::enterWhatsThisMode(); + TQRect r( itemGeometry( actItem) ); + TQWhatsThis::leaveWhatsThisMode( mi->whatsThis(), mapToGlobal( r.bottomLeft()) ); + } + //fall-through! +#endif + default: + ok_key = FALSE; + + } + if ( !ok_key && + ( !e->state() || e->state() == AltButton || e->state() == ShiftButton ) && + e->text().length()==1 ) { + TQChar c = e->text()[0].upper(); + + TQMenuItemListIt it(*mitems); + TQMenuItem* first = 0; + TQMenuItem* currentSelected = 0; + TQMenuItem* firstAfterCurrent = 0; + + register TQMenuItem *m; + mi = 0; + int indx = 0; + int clashCount = 0; + while ( (m=it.current()) ) { + ++it; + TQString s = m->text(); + if ( !s.isEmpty() ) { + int i = s.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 = TQWhatsThis::inWhatsThisMode(); +#else + const bool b = FALSE; +#endif + if ( mi->isEnabledAndVisible() || b ) { + active_popup_menu = this; + TQGuardedPtr<TQSignal> signal = mi->signal(); + actSig( mi->id(), b ); + if ( signal && !b ) + signal->activate(); + active_popup_menu = 0; + } + } + } else if ( clashCount > 1 ) { // Clashes, highlight next... + // If there's clashes and no one is selected, use first one + // or if there is no clashes _after_ current, use first one + if ( !currentSelected || (currentSelected && !firstAfterCurrent)) + dy = indexOf( first->id() ) - actItem; + else + dy = indexOf( firstAfterCurrent->id() ) - actItem; + } + } +#ifndef QT_NO_MENUBAR + if ( !ok_key ) { // send to menu bar + register TQMenuData *top = this; // find top level + while ( top->parentMenu ) + top = top->parentMenu; + if ( top->isMenuBar ) { + int beforeId = top->actItem; + ((TQMenuBar*)top)->tryKeyEvent( this, e ); + if ( beforeId != top->actItem ) + ok_key = TRUE; + } + } +#endif + if ( actItem < 0 ) { + if ( dy > 0 ) { + setFirstItemActive(); + } else if ( dy < 0 ) { + TQMenuItemListIt it(*mitems); + it.toLast(); + register TQMenuItem *mi; + int ai = count() - 1; + while ( (mi=it.current()) ) { + --it; + if ( !mi->isSeparator() && mi->id() != TQMenuData::d->aInt ) { + setActiveItem( ai ); + return; + } + ai--; + } + actItem = -1; + } + return; + } + + if ( dy ) { // highlight next/prev + register int i = actItem; + int c = mitems->count(); + for(int n = c; n; n--) { + i = i + dy; + if(d->scroll.scrollable) { + if(d->scroll.scrolltimer) + d->scroll.scrolltimer->stop(); + if(i < 0) + i = 0; + else if(i >= c) + i = c - 1; + } else { + if ( i == c ) + i = 0; + else if ( i < 0 ) + i = c - 1; + } + mi = mitems->at( i ); + if ( !mi || !mi->isVisible() ) + continue; + + if ( !mi->isSeparator() && + ( style().styleHint(TQStyle::SH_PopupMenu_AllowActiveAndDisabled, this) + || mi->isEnabledAndVisible() ) ) + break; + } + if ( i != actItem ) + setActiveItem( i ); + if(d->scroll.scrollable) { //need to scroll to make it visible? + TQRect r = itemGeometry(actItem); + if(r.isNull() || r.height() < itemHeight(mitems->at(actItem))) { + bool refresh = FALSE; + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp && dy == -1) { //up + if(d->scroll.topScrollableIndex >= 0) { + d->scroll.topScrollableIndex--; + refresh = TRUE; + } + } else if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown) { //down + TQMenuItemListIt it(*mitems); + int sh = style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + for(int i = 0, y = ((d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp) ? sh : 0); it.current(); i++, ++it) { + if(i >= d->scroll.topScrollableIndex) { + int itemh = itemHeight(it.current()); + TQSize sz = style().sizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemh), + TQStyleOption(it.current(),maxPMWidth,0)); + y += sz.height(); + if(y > (contentsRect().height()-sh)) { + if(sz.height() > sh || !it.atLast()) + d->scroll.topScrollableIndex++; + refresh = TRUE; + break; + } + } + } + } + if(refresh) { + updateScrollerState(); + update(); + } + } + } + } + +#ifdef 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 TQPopupMenu::timerEvent( TQTimerEvent *e ) +{ + TQFrame::timerEvent( e ); +} + +/*! + \reimp +*/ +void TQPopupMenu::leaveEvent( TQEvent * ) +{ + d->hasmouse = 0; + if ( testWFlags( WStyle_Tool ) && style().styleHint(TQStyle::SH_PopupMenu_MouseTracking, this) ) { + int lastActItem = actItem; + actItem = -1; + if ( lastActItem >= 0 ) + updateRow( lastActItem ); + } +} + +/*! + \reimp +*/ +void TQPopupMenu::styleChange( TQStyle& old ) +{ + TQFrame::styleChange( old ); + setMouseTracking(style().styleHint(TQStyle::SH_PopupMenu_MouseTracking, this)); + style().polishPopupMenu( this ); + updateSize(TRUE); +} + +/*!\reimp + */ +void TQPopupMenu::enabledChange( bool ) +{ + if ( TQMenuData::d->aWidget ) // torn-off menu + TQMenuData::d->aWidget->setEnabled( isEnabled() ); +} + + +/*! + If a popup menu does not fit on the screen it lays itself out so + that it does fit. It is style dependent what layout means (for + example, on Windows it will use multiple columns). + + This functions returns the number of columns necessary. + + \sa sizeHint() +*/ +int TQPopupMenu::columns() const +{ + return ncols; +} + +/* This private slot handles the scrolling popupmenu */ +void TQPopupMenu::subScrollTimer() { + TQPoint pos = TQCursor::pos(); + if(!d->scroll.scrollable || !isVisible()) { + if(d->scroll.scrolltimer) + d->scroll.scrolltimer->stop(); + return; + } else if(pos.x() > x() + width() || pos.x() < x()) { + return; + } + int sh = style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if(!d->scroll.lastScroll.isValid()) { + d->scroll.lastScroll = TQTime::currentTime(); + } else { + int factor=0; + if(pos.y() < y()) + factor = y() - pos.y(); + else if(pos.y() > y() + height()) + factor = pos.y() - (y() + height()); + int msecs = 250 - ((factor / 10) * 40); + if(d->scroll.lastScroll.msecsTo(TQTime::currentTime()) < TQMAX(0, msecs)) + return; + d->scroll.lastScroll = TQTime::currentTime(); + } + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp && pos.y() <= y() + sh) { //up + if(d->scroll.topScrollableIndex > 0) { + d->scroll.topScrollableIndex--; + updateScrollerState(); + update(contentsRect()); + } + } else if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + pos.y() >= (y() + contentsRect().height()) - sh) { //down + TQMenuItemListIt it(*mitems); + for(int i = 0, y = contentsRect().y() + sh; it.current(); i++, ++it) { + if(i >= d->scroll.topScrollableIndex) { + int itemh = itemHeight(it.current()); + TQSize sz = style().sizeFromContents(TQStyle::CT_PopupMenuItem, this, TQSize(0, itemh), + TQStyleOption(it.current(),maxPMWidth,0)); + y += sz.height(); + if(y > contentsRect().height() - sh) { + d->scroll.topScrollableIndex++; + updateScrollerState(); + update(contentsRect()); + break; + } + } + } + } +} + +/* This private slot handles the delayed submenu effects */ + +void TQPopupMenu::subMenuTimer() { + + if ( !isVisible() || (actItem < 0 && popupActive < 0) || actItem == popupActive ) + return; + + if ( popupActive >= 0 ) { + hidePopups(); + popupActive = -1; + } + + // hidePopups() may change actItem etc. + if ( !isVisible() || actItem < 0 || actItem == popupActive ) + return; + + TQMenuItem *mi = mitems->at(actItem); + if ( !mi || !mi->isEnabledAndVisible() ) + return; + + TQPopupMenu *popup = mi->popup(); + if ( !popup || !popup->isEnabled() ) + return; + + //avoid circularity + if ( popup->isVisible() ) + return; + + Q_ASSERT( popup->parentMenu == 0 ); + popup->parentMenu = this; // set parent menu + + emit popup->aboutToShow(); + supressAboutToShow = TRUE; + + + TQRect r( itemGeometry( actItem ) ); + TQPoint p; + TQSize ps = popup->sizeHint(); + // GUI Style + int gs = style().styleHint(TQStyle::SH_GUIStyle); + int arrowHMargin, arrowVMargin; + if (gs == GtkStyle) { + arrowHMargin = gtkArrowHMargin; + arrowVMargin = gtkArrowVMargin; + } else { + arrowHMargin = motifArrowHMargin; + arrowVMargin = motifArrowVMargin; + } + if( TQApplication::reverseLayout() ) { + p = TQPoint( r.left() + arrowHMargin - ps.width(), r.top() + arrowVMargin ); + p = mapToGlobal( p ); + + bool right = FALSE; + if ( ( parentMenu && parentMenu->isPopupMenu && + ((TQPopupMenu*)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 = TQPoint( r.right() - arrowHMargin, r.top() + arrowVMargin ); + p = mapToGlobal( p ); + + bool left = FALSE; + if ( ( parentMenu && parentMenu->isPopupMenu && + ((TQPopupMenu*)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() ); + } + TQRect pr = popup->itemGeometry(popup->count() - 1); + if (p.y() + ps.height() > screenRect( p ).bottom() && + p.y() - ps.height() + (TQCOORD) pr.height() >= screenRect( p ).top()) + p.setY( p.y() - ps.height() + (TQCOORD) pr.height()); + + if ( style().styleHint(TQStyle::SH_PopupMenu_SloppySubMenus, this )) { + TQPoint cur = TQCursor::pos(); + if ( r.contains( mapFromGlobal( cur ) ) ) { + TQPoint pts[4]; + pts[0] = TQPoint( cur.x(), cur.y() - 2 ); + pts[3] = TQPoint( cur.x(), cur.y() + 2 ); + if ( p.x() >= cur.x() ) { + pts[1] = TQPoint( geometry().right(), p.y() ); + pts[2] = TQPoint( geometry().right(), p.y() + ps.height() ); + } else { + pts[1] = TQPoint( p.x() + ps.width(), p.y() ); + pts[2] = TQPoint( p.x() + ps.width(), p.y() + ps.height() ); + } + TQPointArray points( 4 ); + for( int i = 0; i < 4; i++ ) + points.setPoint( i, mapFromGlobal( pts[i] ) ); + d->mouseMoveBuffer = TQRegion( points ); + repaint(); + } + } + + popupActive = actItem; + popup->popup( p ); +} + +void TQPopupMenu::allowAnimation() +{ + preventAnimation = FALSE; +} + +void TQPopupMenu::updateRow( int row ) +{ + if ( !isVisible() ) + return; + + if ( badSize ) { + updateSize(); + update(); + return; + } + updateSize(); + TQRect r = itemGeometry( row ); + if ( !r.isNull() ) // can happen via the scroller + 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 + TQWidget::mapToGlobal(). + + The return code is the id of the selected item in either the popup + menu or one of its submenus, or -1 if no item is selected + (normally because the user pressed Esc). + + Note that all signals are emitted as usual. If you connect a menu + item to a slot and call the menu's exec(), you get the result both + via the signal-slot connection and in the return value of exec(). + + Common usage is to position the popup at the current mouse + position: + \code + exec( TQCursor::pos() ); + \endcode + or aligned to a widget: + \code + exec( somewidget.mapToGlobal(TQPoint(0, 0)) ); + \endcode + + When positioning a popup with exec() or popup(), bear in mind that + you cannot rely on the popup menu's current size(). For + performance reasons, the popup adapts its size only when + necessary. So in many cases, the size before and after the show is + different. Instead, use sizeHint(). It calculates the proper size + depending on the menu's current contents. + + \sa popup(), sizeHint() +*/ + +int TQPopupMenu::exec( const TQPoint & pos, int indexAtPoint ) +{ + snapToMouse = TRUE; + if ( !qApp ) + return -1; + + TQPopupMenu* priorSyncMenu = syncMenu; + + syncMenu = this; + syncMenuId = -1; + + TQGuardedPtr<TQPopupMenu> 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 TQPopupMenu::connectModal( TQPopupMenu* 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)) ); + + TQMenuItemListIt it(*mitems); + register TQMenuItem *mi; + while ( (mi=it.current()) ) { + ++it; + if ( mi->popup() && mi->popup() != receiver + && (bool)(mi->popup()->connectModalRecursionSafety) != doConnect ) + mi->popup()->connectModal( receiver, doConnect ); //avoid circular + } +} + + +/*! + Executes this popup synchronously. + + This is equivalent to \c{exec(mapToGlobal(TQPoint(0,0)))}. In most + situations you'll want to specify the position yourself, for + example at the current mouse position: + \code + exec(TQCursor::pos()); + \endcode + or aligned to a widget: + \code + exec(somewidget.mapToGlobal(TQPoint(0,0))); + \endcode +*/ + +int TQPopupMenu::exec() +{ + return exec(mapToGlobal(TQPoint(0,0))); +} + + +/* Internal slot used for exec(). */ + +void TQPopupMenu::modalActivation( int id ) +{ + syncMenuId = id; +} + + +/*! + Sets the currently active item to index \a i and repaints as necessary. +*/ + +void TQPopupMenu::setActiveItem( int i ) +{ + int lastActItem = actItem; + actItem = i; + if ( lastActItem >= 0 ) + updateRow( lastActItem ); + if ( i >= 0 && i != lastActItem ) + updateRow( i ); + TQMenuItem *mi = mitems->at( actItem ); + if ( !mi ) + return; + + if ( mi->widget() && mi->widget()->isFocusEnabled() ) { + mi->widget()->setFocus(); + } else { + setFocus(); + TQRect mfrect = itemGeometry( actItem ); + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); + } + if ( mi->id() != -1 ) + hilitSig( mi->id() ); +#ifndef QT_NO_WHATSTHIS + if (whatsThisItem && whatsThisItem != mi) { + qWhatsThisBDH(); + } + whatsThisItem = mi; +#endif +} + + +/*! + \reimp +*/ +TQSize TQPopupMenu::sizeHint() const +{ + constPolish(); + TQPopupMenu* that = (TQPopupMenu*) this; + //We do not need a resize here, just the sizeHint.. + return that->updateSize(FALSE).expandedTo( TQApplication::globalStrut() ); +} + + +/*! + \overload + + Returns the id of the item at \a pos, or -1 if there is no item + there or if it is a separator. +*/ +int TQPopupMenu::idAt( const TQPoint& pos ) const +{ + return idAt( itemAtPos( pos ) ); +} + + +/*! + \fn int TQPopupMenu::idAt( int index ) const + + Returns the identifier of the menu item at position \a index in + the internal list, or -1 if \a index is out of range. + + \sa TQMenuData::setId(), TQMenuData::indexOf() +*/ + + +/*! + \reimp + */ +bool TQPopupMenu::customWhatsThis() const +{ + return TRUE; +} + + +/*! + \reimp + */ +bool TQPopupMenu::focusNextPrevChild( bool next ) +{ + register TQMenuItem *mi; + int dy = next? 1 : -1; + if ( dy && actItem < 0 ) { + setFirstItemActive(); + } else if ( dy ) { // highlight next/prev + register int i = actItem; + int c = mitems->count(); + int n = c; + while ( n-- ) { + i = i + dy; + if ( i == c ) + i = 0; + else if ( i < 0 ) + i = c - 1; + mi = mitems->at( i ); + if ( mi && !mi->isSeparator() && + ( ( style().styleHint(TQStyle::SH_PopupMenu_AllowActiveAndDisabled, this) + && mi->isVisible() ) + || mi->isEnabledAndVisible() ) ) + break; + } + if ( i != actItem ) + setActiveItem( i ); + } + return TRUE; +} + + +/*! + \reimp + */ +void TQPopupMenu::focusInEvent( TQFocusEvent * ) +{ +} + +/*! + \reimp + */ +void TQPopupMenu::focusOutEvent( TQFocusEvent * ) +{ +} + + +class TQTearOffMenuItem : public TQCustomMenuItem +{ +public: + TQTearOffMenuItem() + { + } + ~TQTearOffMenuItem() + { + } + void paint( TQPainter* p, const TQColorGroup& cg, bool /* act*/, + bool /*enabled*/, int x, int y, int w, int h ) + { + p->setPen( TQPen( cg.dark(), 1, DashLine ) ); + p->drawLine( x+2, y+h/2-1, x+w-4, y+h/2-1 ); + p->setPen( TQPen( cg.light(), 1, DashLine ) ); + p->drawLine( x+2, y+h/2, x+w-4, y+h/2 ); + } + bool fullSpan() const + { + return TRUE; + } + + TQSize sizeHint() + { + return TQSize( 20, 6 ); + } +}; + + + +/*! + Inserts a tear-off handle into the menu. A tear-off handle is a + special menu item that creates a copy of the menu when the menu is + selected. This "torn-off" copy lives in a separate window. It + 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 TQPopupMenu::insertTearOffHandle( int id, int index ) +{ + int myid = insertItem( new TQTearOffMenuItem, id, index ); + connectItem( myid, this, SLOT( toggleTearOff() ) ); + TQMenuData::d->aInt = myid; + return myid; +} + + +/*!\internal + + implements tear-off menus + */ +void TQPopupMenu::toggleTearOff() +{ + if ( active_popup_menu && active_popup_menu->tornOff ) { + active_popup_menu->close(); + } else if (TQMenuData::d->aWidget ) { + delete (TQWidget*) TQMenuData::d->aWidget; // delete the old one + } else { + // create a tear off menu + TQPopupMenu* p = new TQPopupMenu( parentWidget(), "tear off menu" ); + connect( p, 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 ( TQMenuItemListIt it( *mitems ); it.current(); ++it ) { + if ( it.current()->id() != TQMenuData::d->aInt && !it.current()->widget() ) + p->mitems->append( it.current() ); + } + p->show(); + TQMenuData::d->aWidget = p; + } +} + +/*! + \reimp + */ +void TQPopupMenu::activateItemAt( int index ) +{ + if ( index >= 0 && index < (int) mitems->count() ) { + TQMenuItem *mi = mitems->at( index ); + if ( index != actItem ) // new item activated + setActiveItem( index ); + TQPopupMenu *popup = mi->popup(); + if ( popup ) { + if ( popup->isVisible() ) { // sub menu already open + int pactItem = popup->actItem; + popup->actItem = -1; + popup->hidePopups(); + popup->updateRow( pactItem ); + } else { // open sub menu + hidePopups(); + actItem = index; + subMenuTimer(); + popup->setFirstItemActive(); + } + } else { + byeMenuBar(); // deactivate menu bar + +#ifndef QT_NO_WHATSTHIS + bool b = TQWhatsThis::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; + TQGuardedPtr<TQSignal> signal = mi->signal(); + actSig( mi->id(), b ); + if ( signal && !b ) + signal->activate(); + active_popup_menu = 0; + } + } + } + } else { + if ( tornOff ) { + close(); + } else { + TQMenuData* p = parentMenu; + hide(); +#ifndef QT_NO_MENUBAR + if ( p && p->isMenuBar ) + ((TQMenuBar*) p)->goodbye( TRUE ); +#endif + } + } + +} + +/*! \internal + This private function is to update the scroll states in styles that support scrolling. */ +void +TQPopupMenu::updateScrollerState() +{ + uint old_scrollable = d->scroll.scrollable; + d->scroll.scrollable = TQPopupMenuPrivate::Scroll::ScrollNone; + if(!style().styleHint(TQStyle::SH_PopupMenu_Scrollable, this)) + return; + + TQMenuItem *mi; + TQMenuItemListIt it( *mitems ); + if(d->scroll.topScrollableIndex) { + for(int row = 0; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++) + ++it; + if(!mi) + it.toFirst(); + } + int y = 0, sh = style().pixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if(!it.atFirst()) { + // can't use |= because of a bug/feature in IBM xlC 5.0.2 + d->scroll.scrollable = d->scroll.scrollable | TQPopupMenuPrivate::Scroll::ScrollUp; + y += sh; + } + while ( (mi=it.current()) ) { + ++it; + int myheight = contentsRect().height(); + TQSize sz = style().sizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemHeight( mi )), + TQStyleOption(mi,maxPMWidth)); + if(y + sz.height() >= myheight) { + d->scroll.scrollable = d->scroll.scrollable | TQPopupMenuPrivate::Scroll::ScrollDown; + break; + } + y += sz.height(); + } + if((d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp) && + !(old_scrollable & TQPopupMenuPrivate::Scroll::ScrollUp)) + d->scroll.topScrollableIndex++; +} + +#endif // QT_NO_POPUPMENU + diff --git a/src/widgets/qpopupmenu.h b/src/widgets/qpopupmenu.h new file mode 100644 index 000000000..c35bbea75 --- /dev/null +++ b/src/widgets/qpopupmenu.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Definition of TQPopupMenu class +** +** Created : 941128 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQPOPUPMENU_H +#define TQPOPUPMENU_H + +#ifndef QT_H +#include "qframe.h" +#include "qmenudata.h" +#endif // QT_H + +#ifndef QT_NO_POPUPMENU +class TQPopupMenuPrivate; + +class Q_EXPORT TQPopupMenu : public TQFrame, public TQMenuData +{ + Q_OBJECT + Q_PROPERTY( bool checkable READ isCheckable WRITE setCheckable ) +public: + TQPopupMenu( TQWidget* parent=0, const char* name=0 ); + ~TQPopupMenu(); + + void popup( const TQPoint & pos, int indexAtPoint = -1 ); // open + void updateItem( int id ); + + virtual void setCheckable( bool ); + bool isCheckable() const; + + void setFont( const TQFont & ); + void show(); + void hide(); + + int exec(); + int exec( const TQPoint & pos, int indexAtPoint = 0 ); // modal + + virtual void setActiveItem( int ); + TQSize sizeHint() const; + + int idAt( int index ) const { return TQMenuData::idAt( index ); } + int idAt( const TQPoint& pos ) const; + + bool customWhatsThis() const; + + int insertTearOffHandle( int id=-1, int index=-1 ); + + void activateItemAt( int index ); + TQRect 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( TQMenuItem* mi ) const; + void drawItem( TQPainter* p, int tab, TQMenuItem* mi, + bool act, int x, int y, int w, int h); + + void drawContents( TQPainter * ); + + void closeEvent( TQCloseEvent *e ); + void paintEvent( TQPaintEvent * ); + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void keyPressEvent( TQKeyEvent * ); + void focusInEvent( TQFocusEvent * ); + void focusOutEvent( TQFocusEvent * ); + void timerEvent( TQTimerEvent * ); + void leaveEvent( TQEvent * ); + void styleChange( TQStyle& ); + void enabledChange( bool ); + int columns() const; + + bool focusNextPrevChild( bool next ); + + int itemAtPos( const TQPoint &, 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( TQObject* ); + 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( TQPopupMenu * ); + void menuDelPopup( TQPopupMenu * ); + void frameChanged(); + + void actSig( int, bool = FALSE ); + void hilitSig( int ); + virtual void setFirstItemActive(); + void hideAllPopups(); + void hidePopups(); + bool tryMenuBar( TQMouseEvent * ); + void byeMenuBar(); + + TQSize updateSize(bool force_recalc=FALSE, bool do_resize=TRUE); + void updateRow( int row ); + TQRect screenRect(const TQPoint& pos); +#ifndef QT_NO_ACCEL + void updateAccel( TQWidget * ); + void enableAccel( bool ); +#endif + TQPopupMenuPrivate *d; +#ifndef QT_NO_ACCEL + TQAccel *autoaccel; +#endif + +#if defined(Q_WS_MAC) && !defined(TQMAC_QMENUBAR_NO_NATIVE) + bool macPopupMenu(const TQPoint &, 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( TQPopupMenu *, TQMouseEvent * ); + + friend class TQMenuData; + friend class TQMenuBar; + + void connectModal(TQPopupMenu* receiver, bool doConnect); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQPopupMenu( const TQPopupMenu & ); + TQPopupMenu &operator=( const TQPopupMenu & ); +#endif +}; + + +#endif // QT_NO_POPUPMENU + +#endif // TQPOPUPMENU_H diff --git a/src/widgets/qprogressbar.cpp b/src/widgets/qprogressbar.cpp new file mode 100644 index 000000000..ed3f8b7b4 --- /dev/null +++ b/src/widgets/qprogressbar.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Implementation of TQProgressBar class +** +** Created : 970521 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQProgressBar qprogressbar.h + \brief The TQProgressBar 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 TQFtp or TQHttp to download items when they are unable to + determine the size of the item being downloaded. + + \sa TQProgressDialog + + <img src=qprogbar-m.png> <img src=qprogbar-w.png> + + \sa TQProgressDialog + \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 TQFrame::TQFrame() constructor. + + \sa setTotalSteps() +*/ + +TQProgressBar::TQProgressBar( TQWidget *parent, const char *name, WFlags f ) + : TQFrame( parent, name, f | WNoAutoErase ), + total_steps( 100 ), + progress_val( -1 ), + percentage( -1 ), + center_indicator( TRUE ), + auto_indicator( TRUE ), + percentage_visible( TRUE ), + d( 0 ) +{ + setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::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 + TQFrame::TQFrame() constructor. + + \sa setTotalSteps(), setProgress() +*/ + +TQProgressBar::TQProgressBar( int totalSteps, + TQWidget *parent, const char *name, WFlags f ) + : TQFrame( parent, name, f | WNoAutoErase ), + total_steps( totalSteps ), + progress_val( -1 ), + percentage( -1 ), + center_indicator( TRUE ), + auto_indicator( TRUE ), + percentage_visible( TRUE ), + d( 0 ) +{ + setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed ) ); + initFrame(); +} + + +/*! + Reset the progress bar. The progress bar "rewinds" and shows no + progress. +*/ + +void TQProgressBar::reset() +{ + progress_val = -1; + percentage = -1; + setIndicator(progress_str, progress_val, total_steps); + repaint( FALSE ); +} + + +/*! + \property TQProgressBar::totalSteps + \brief The total number of steps. + + If totalSteps is 0, the progress bar will display a busy + indicator. + + \sa totalSteps() +*/ + +void TQProgressBar::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 TQProgressBar::progress + \brief The current amount of progress + + This property is -1 if progress counting has not started. +*/ + +void TQProgressBar::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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif +} + +/*! + \overload + + Sets the amount of progress to \a progress and the total number of + steps to \a totalSteps. + + \sa setTotalSteps() +*/ + +void TQProgressBar::setProgress( int progress, int totalSteps ) +{ + if ( total_steps != totalSteps ) + setTotalSteps( totalSteps ); + setProgress( progress ); +} + +/*! + \property TQProgressBar::progressString + \brief the amount of progress as a string + + This property is TQString::null if progress counting has not started. +*/ + + +/*! + \reimp +*/ +TQSize TQProgressBar::sizeHint() const +{ + constPolish(); + TQFontMetrics fm = fontMetrics(); + int cw = style().pixelMetric(TQStyle::PM_ProgressBarChunkWidth, this); + return style().sizeFromContents(TQStyle::CT_ProgressBar, this, + TQSize( cw * 7 + fm.width( '0' ) * 4, + fm.height() + 8)); +} + + +/*! + \reimp +*/ +TQSize TQProgressBar::minimumSizeHint() const +{ + return sizeHint(); +} + +/*! + \property TQProgressBar::centerIndicator + \brief whether the indicator string should be centered + + Changing this property sets \l TQProgressBar::indicatorFollowsStyle + to FALSE. The default is TRUE. +*/ + +void TQProgressBar::setCenterIndicator( bool on ) +{ + if ( !auto_indicator && on == center_indicator ) + return; + auto_indicator = FALSE; + center_indicator = on; + repaint( FALSE ); +} + +/*! + \property TQProgressBar::indicatorFollowsStyle + \brief whether the display of the indicator string should follow the GUI style + + The default is TRUE. + + \sa centerIndicator +*/ + +void TQProgressBar::setIndicatorFollowsStyle( bool on ) +{ + if ( on == auto_indicator ) + return; + auto_indicator = on; + repaint( FALSE ); +} + +/*! + \property TQProgressBar::percentageVisible + \brief whether the current progress value is displayed + + The default is TRUE. + + \sa centerIndicator, indicatorFollowsStyle +*/ +void TQProgressBar::setPercentageVisible( bool on ) +{ + if ( on == percentage_visible ) + return; + percentage_visible = on; + repaint( FALSE ); +} + +/*! + \reimp +*/ +void TQProgressBar::show() +{ + setIndicator( progress_str, progress_val, total_steps ); + TQFrame::show(); +} + +void TQProgressBar::initFrame() +{ + setFrameStyle(TQFrame::NoFrame); +} + +/*! + \reimp +*/ +void TQProgressBar::styleChange( TQStyle& old ) +{ + initFrame(); + TQFrame::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 TQProgressBar::setIndicator( TQString & indicator, int progress, + int totalSteps ) +{ + if ( !totalSteps ) + return FALSE; + if ( progress < 0 ) { + indicator = TQString::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 TQProgressBar::drawContents( TQPainter *p ) +{ + const TQRect bar = contentsRect(); + + TQSharedDoubleBuffer buffer( p, bar.x(), bar.y(), bar.width(), bar.height() ); + + TQPoint pn = backgroundOffset(); + buffer.painter()->setBrushOrigin( -pn.x(), -pn.y() ); + + const TQPixmap *bpm = paletteBackgroundPixmap(); + if ( bpm ) + buffer.painter()->fillRect( bar, TQBrush( paletteBackgroundColor(), *bpm ) ); + else + buffer.painter()->fillRect( bar, paletteBackgroundColor() ); + buffer.painter()->setFont( p->font() ); + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + + style().drawControl(TQStyle::CE_ProgressBarGroove, buffer.painter(), this, + TQStyle::visualRect(style().subRect(TQStyle::SR_ProgressBarGroove, this), this ), + colorGroup(), flags); + + style().drawControl(TQStyle::CE_ProgressBarContents, buffer.painter(), this, + TQStyle::visualRect(style().subRect(TQStyle::SR_ProgressBarContents, this), this ), + colorGroup(), flags); + + if (percentageVisible()) + style().drawControl(TQStyle::CE_ProgressBarLabel, buffer.painter(), this, + TQStyle::visualRect(style().subRect(TQStyle::SR_ProgressBarLabel, this), this ), + colorGroup(), flags); +} + +#endif diff --git a/src/widgets/qprogressbar.h b/src/widgets/qprogressbar.h new file mode 100644 index 000000000..277c8c933 --- /dev/null +++ b/src/widgets/qprogressbar.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Definition of TQProgressBar class +** +** Created : 970520 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQPROGRESSBAR_H +#define TQPROGRESSBAR_H + +#ifndef QT_H +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_PROGRESSBAR + + +class TQProgressBarPrivate; + + +class Q_EXPORT TQProgressBar : public TQFrame +{ + Q_OBJECT + Q_PROPERTY( int totalSteps READ totalSteps WRITE setTotalSteps ) + Q_PROPERTY( int progress READ progress WRITE setProgress ) + Q_PROPERTY( TQString 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: + TQProgressBar( TQWidget* parent=0, const char* name=0, WFlags f=0 ); + TQProgressBar( int totalSteps, TQWidget* parent=0, const char* name=0, WFlags f=0 ); + + int totalSteps() const; + int progress() const; + const TQString &progressString() const; + + TQSize sizeHint() const; + TQSize 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( TQPainter * ); + virtual bool setIndicator( TQString & progress_str, int progress, + int totalSteps ); + void styleChange( TQStyle& ); + +private: + int total_steps; + int progress_val; + int percentage; + TQString progress_str; + bool center_indicator : 1; + bool auto_indicator : 1; + bool percentage_visible : 1; + TQProgressBarPrivate * d; + void initFrame(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQProgressBar( const TQProgressBar & ); + TQProgressBar &operator=( const TQProgressBar & ); +#endif +}; + + +inline int TQProgressBar::totalSteps() const +{ + return total_steps; +} + +inline int TQProgressBar::progress() const +{ + return progress_val; +} + +inline const TQString &TQProgressBar::progressString() const +{ + return progress_str; +} + +inline bool TQProgressBar::centerIndicator() const +{ + return center_indicator; +} + +inline bool TQProgressBar::indicatorFollowsStyle() const +{ + return auto_indicator; +} + +inline bool TQProgressBar::percentageVisible() const +{ + return percentage_visible; +} + +#endif // QT_NO_PROGRESSBAR + +#endif // TQPROGRESSBAR_H diff --git a/src/widgets/qpushbutton.cpp b/src/widgets/qpushbutton.cpp new file mode 100644 index 000000000..32e25e764 --- /dev/null +++ b/src/widgets/qpushbutton.cpp @@ -0,0 +1,760 @@ +/**************************************************************************** +** +** Implementation of TQPushButton class +** +** Created : 940221 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQPushButton qpushbutton.h + \brief The TQPushButton 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 + TQPushButton *pb = new TQPushButton( "&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 TQFileDialog) are not command buttons, + but tool buttons. TQt provides a special class (TQToolButton) 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 TQRadioButton) and + check boxes (see TQCheckBox). + + <img src="qpushbt-m.png"> <img src="qpushbt-w.png"> + + In TQt, the TQButton abstract base class provides most of the modes + and other API, and TQPushButton provides GUI logic. See TQButton 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 TQToolButton, TQRadioButton TQCheckBox + \link guibooks.html#fowler GUI Design Handbook: Push Button\endlink +*/ + +/*! + \property TQPushButton::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. TQt 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 TQDialog + parent; otherwise it defaults to FALSE. + + See the \l default property for details of how \l default and + auto-default interact. +*/ + +/*! + \property TQPushButton::autoMask + \brief whether the button is automatically masked + + \sa TQWidget::setAutoMask() +*/ + +/*! + \property TQPushButton::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 TQPushButton::flat + \brief whether the border is disabled + + This property's default is FALSE. +*/ + +/*! + \property TQPushButton::iconSet + \brief the icon set on the push button + + This property will return 0 if the push button has no iconset. +*/ + +/*! + \property TQPushButton::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 TQPushButton::toggleButton + \brief whether the button is a toggle button + + Toggle buttons have an on/off state similar to \link TQCheckBox + check boxes. \endlink A push button is initially not a toggle + button. + + \sa setOn(), toggle(), isToggleButton() toggled() +*/ + +/*! \property TQPushButton::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 TQPushButtonPrivate +{ +public: + TQPushButtonPrivate() + :iconset( 0 ) + {} + ~TQPushButtonPrivate() + { +#ifndef QT_NO_ICONSET + delete iconset; +#endif + } +#ifndef QT_NO_POPUPMENU + TQGuardedPtr<TQPopupMenu> popup; +#endif + TQIconSet* iconset; +}; + + +/*! + Constructs a push button with no text. + + The \a parent and \a name arguments are sent on to the TQWidget + constructor. +*/ + +TQPushButton::TQPushButton( TQWidget *parent, const char *name ) + : TQButton( parent, name ) +{ + init(); +} + +/*! + Constructs a push button called \a name with the parent \a parent + and the text \a text. +*/ + +TQPushButton::TQPushButton( const TQString &text, TQWidget *parent, + const char *name ) + : TQButton( parent, name ) +{ + init(); + setText( text ); +} + + +/*! + Constructs a push button with an \a icon and a \a text. + + Note that you can also pass a TQPixmap object as an icon (thanks to + the implicit type conversion provided by C++). + + The \a parent and \a name arguments are sent to the TQWidget + constructor. +*/ +#ifndef QT_NO_ICONSET +TQPushButton::TQPushButton( const TQIconSet& icon, const TQString &text, + TQWidget *parent, const char *name ) + : TQButton( parent, name ) +{ + init(); + setText( text ); + setIconSet( icon ); +} +#endif + + +/*! + Destroys the push button. +*/ +TQPushButton::~TQPushButton() +{ + delete d; +} + +void TQPushButton::init() +{ + d = 0; + defButton = FALSE; + lastEnabled = FALSE; + hasMenuArrow = FALSE; + flt = FALSE; +#ifndef QT_NO_DIALOG + autoDefButton = ::qt_cast<TQDialog*>(topLevelWidget()) != 0; +#else + autoDefButton = FALSE; +#endif + setBackgroundMode( PaletteButton ); + setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::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 TQCheckBox check + boxes. \endlink A push button is initially not a toggle button. + + \sa setOn(), toggle(), isToggleButton() toggled() +*/ + +void TQPushButton::setToggleButton( bool enable ) +{ + TQButton::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 TQPushButton::setOn( bool enable ) +{ + if ( !isToggleButton() ) + return; + TQButton::setOn( enable ); +} + +void TQPushButton::setAutoDefault( bool enable ) +{ + if ( (bool)autoDefButton == enable ) + return; + autoDefButton = enable; + update(); + updateGeometry(); +} + + +void TQPushButton::setDefault( bool enable ) +{ + if ( (bool)defButton == enable ) + return; // no change + defButton = enable; +#ifndef QT_NO_DIALOG + if ( defButton && ::qt_cast<TQDialog*>(topLevelWidget()) ) + ((TQDialog*)topLevelWidget())->setMainDefault( this ); +#endif + update(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::StateChanged ); +#endif +} + + +/*! + \reimp +*/ +TQSize TQPushButton::sizeHint() const +{ + constPolish(); + + int w = 0, h = 0; + + // calculate contents size... +#ifndef QT_NO_ICONSET + if ( iconSet() && !iconSet()->isNull() ) { + int iw = iconSet()->pixmap( TQIconSet::Small, TQIconSet::Normal ).width() + 4; + int ih = iconSet()->pixmap( TQIconSet::Small, TQIconSet::Normal ).height(); + w += iw; + h = TQMAX( h, ih ); + } +#endif + if ( isMenuButton() ) + w += style().pixelMetric(TQStyle::PM_MenuButtonIndicator, this); + + if ( pixmap() ) { + TQPixmap *pm = (TQPixmap *)pixmap(); + w += pm->width(); + h += pm->height(); + } else { + TQString s( text() ); + bool empty = s.isEmpty(); + if ( empty ) + s = TQString::fromLatin1("XXXX"); + TQFontMetrics fm = fontMetrics(); + TQSize sz = fm.size( ShowPrefix, s ); + if(!empty || !w) + w += sz.width(); + if(!empty || !h) + h = TQMAX(h, sz.height()); + } + + return (style().sizeFromContents(TQStyle::CT_PushButton, this, TQSize(w, h)). + expandedTo(TQApplication::globalStrut())); +} + + +/*! + \reimp +*/ +void TQPushButton::move( int x, int y ) +{ + TQWidget::move( x, y ); +} + +/*! + \reimp +*/ +void TQPushButton::move( const TQPoint &p ) +{ + move( p.x(), p.y() ); +} + +/*! + \reimp +*/ +void TQPushButton::resize( int w, int h ) +{ + TQWidget::resize( w, h ); +} + +/*! + \reimp +*/ +void TQPushButton::resize( const TQSize &s ) +{ + resize( s.width(), s.height() ); +} + +/*! + \reimp +*/ +void TQPushButton::setGeometry( int x, int y, int w, int h ) +{ + TQWidget::setGeometry( x, y, w, h ); +} + +/*! + \reimp +*/ +void TQPushButton::setGeometry( const TQRect &r ) +{ + TQWidget::setGeometry( r ); +} + +/*! + \reimp + */ +void TQPushButton::resizeEvent( TQResizeEvent * ) +{ + if ( autoMask() ) + updateMask(); +} + +/*! + \reimp +*/ +void TQPushButton::drawButton( TQPainter *paint ) +{ + int diw = 0; + if ( isDefault() || autoDefault() ) { + diw = style().pixelMetric(TQStyle::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(TQColorGroup::Background) ); + paint->fillRect( 0, 0, diw, height(), + colorGroup().brush(TQColorGroup::Background) ); + paint->fillRect( 0, height()-diw, width(), diw, + colorGroup().brush(TQColorGroup::Background) ); + paint->fillRect( width()-diw, 0, diw, height(), + colorGroup().brush(TQColorGroup::Background) ); + } + + } + } + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + if (isDown()) + flags |= TQStyle::Style_Down; + if (isOn()) + flags |= TQStyle::Style_On; + if (! isFlat() && ! isDown()) + flags |= TQStyle::Style_Raised; + if (isDefault()) + flags |= TQStyle::Style_ButtonDefault; + + style().drawControl(TQStyle::CE_PushButton, paint, this, rect(), colorGroup(), flags); + drawButtonLabel( paint ); + + lastEnabled = isEnabled(); +} + + +/*! + \reimp +*/ +void TQPushButton::drawButtonLabel( TQPainter *paint ) +{ + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + if (isDown()) + flags |= TQStyle::Style_Down; + if (isOn()) + flags |= TQStyle::Style_On; + if (! isFlat() && ! isDown()) + flags |= TQStyle::Style_Raised; + if (isDefault()) + flags |= TQStyle::Style_ButtonDefault; + + style().drawControl(TQStyle::CE_PushButtonLabel, paint, this, + style().subRect(TQStyle::SR_PushButtonContents, this), + colorGroup(), flags); +} + + +/*! + \reimp + */ +void TQPushButton::updateMask() +{ + TQBitmap bm( size() ); + bm.fill( color0 ); + + { + TQPainter p( &bm, this ); + style().drawControlMask(TQStyle::CE_PushButton, &p, this, rect()); + } + + setMask( bm ); +} + +/*! + \reimp +*/ +void TQPushButton::focusInEvent( TQFocusEvent *e ) +{ + if (autoDefButton && !defButton) { + defButton = TRUE; +#ifndef QT_NO_DIALOG + if ( defButton && ::qt_cast<TQDialog*>(topLevelWidget()) ) + ((TQDialog*)topLevelWidget())->setDefault( this ); +#endif + } + TQButton::focusInEvent( e ); +} + +/*! + \reimp +*/ +void TQPushButton::focusOutEvent( TQFocusEvent *e ) +{ +#ifndef QT_NO_DIALOG + if ( defButton && autoDefButton ) { + if ( ::qt_cast<TQDialog*>(topLevelWidget()) ) + ((TQDialog*)topLevelWidget())->setDefault( 0 ); + } +#endif + + TQButton::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 TQPushButton::setPopup( TQPopupMenu* popup ) +{ + if ( !d ) + d = new TQPushButtonPrivate; + 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 TQPushButton::setIconSet( const TQIconSet& icon ) +{ + if ( !d ) + d = new TQPushButtonPrivate; + if ( !icon.isNull() ) { + if ( d->iconset ) + *d->iconset = icon; + else + d->iconset = new TQIconSet( icon ); + } else if ( d->iconset) { + delete d->iconset; + d->iconset = 0; + } + + update(); + updateGeometry(); +} + + +TQIconSet* TQPushButton::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() +*/ +TQPopupMenu* TQPushButton::popup() const +{ + return d ? (TQPopupMenu*)d->popup : 0; +} + +void TQPushButton::popupPressed() +{ + TQPopupMenu* popup = d ? (TQPopupMenu*) d->popup : 0; + TQGuardedPtr<TQPushButton> that = this; + if ( isDown() && popup ) { + bool horizontal = TRUE; + bool topLeft = TRUE; // ### always TRUE +#ifndef QT_NO_TOOLBAR + TQToolBar *tb = ::qt_cast<TQToolBar*>(parentWidget()); + if ( tb && tb->orientation() == Vertical ) + horizontal = FALSE; +#endif + if ( horizontal ) { + if ( topLeft ) { + if ( mapToGlobal( TQPoint( 0, rect().bottom() ) ).y() + popup->sizeHint().height() <= qApp->desktop()->height() ) + popup->exec( mapToGlobal( rect().bottomLeft() ) ); + else + popup->exec( mapToGlobal( rect().topLeft() - TQPoint( 0, popup->sizeHint().height() ) ) ); + } else { + TQSize sz( popup->sizeHint() ); + TQPoint p = mapToGlobal( rect().topLeft() ); + p.ry() -= sz.height(); + popup->exec( p ); + } + } else { + if ( topLeft ) { + if ( mapToGlobal( TQPoint( rect().right(), 0 ) ).x() + popup->sizeHint().width() <= qApp->desktop()->width() ) + popup->exec( mapToGlobal( rect().topRight() ) ); + else + popup->exec( mapToGlobal( rect().topLeft() - TQPoint( popup->sizeHint().width(), 0 ) ) ); + } else { + TQSize sz( popup->sizeHint() ); + TQPoint p = mapToGlobal( rect().topLeft() ); + p.rx() -= sz.width(); + popup->exec( p ); + } + } + if (that) + setDown( FALSE ); + } +} +#endif + +void TQPushButton::setFlat( bool f ) +{ + flt = f; + update(); +} + +bool TQPushButton::isFlat() const +{ + return flt; +} + +/*! + \obsolete + \fn virtual void TQPushButton::setIsMenuButton( bool enable ) +*/ + +#endif diff --git a/src/widgets/qpushbutton.h b/src/widgets/qpushbutton.h new file mode 100644 index 000000000..625fbc0eb --- /dev/null +++ b/src/widgets/qpushbutton.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Definition of TQPushButton class +** +** Created : 940221 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQPUSHBUTTON_H +#define TQPUSHBUTTON_H + +#ifndef QT_H +#include "qbutton.h" +#include "qiconset.h" +#endif // QT_H + +#ifndef QT_NO_PUSHBUTTON +class TQPushButtonPrivate; +class TQPopupMenu; + +class Q_EXPORT TQPushButton : public TQButton +{ + 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( TQIconSet 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: + TQPushButton( TQWidget *parent, const char* name=0 ); + TQPushButton( const TQString &text, TQWidget *parent, const char* name=0 ); +#ifndef QT_NO_ICONSET + TQPushButton( const TQIconSet& icon, const TQString &text, TQWidget *parent, const char* name=0 ); +#endif + ~TQPushButton(); + + TQSize sizeHint() const; + + void move( int x, int y ); + void move( const TQPoint &p ); + void resize( int w, int h ); + void resize( const TQSize & ); + void setGeometry( int x, int y, int w, int h ); + + void setGeometry( const TQRect & ); + + 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( TQPopupMenu* popup ); + TQPopupMenu* popup() const; +#endif +#ifndef QT_NO_ICONSET + void setIconSet( const TQIconSet& ); + TQIconSet* iconSet() const; +#endif + void setFlat( bool ); + bool isFlat() const; + +public slots: + virtual void setOn( bool ); + +protected: + void drawButton( TQPainter * ); + void drawButtonLabel( TQPainter * ); + void focusInEvent( TQFocusEvent * ); + void focusOutEvent( TQFocusEvent * ); + void resizeEvent( TQResizeEvent * ); + 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; + + TQPushButtonPrivate* d; + + friend class TQDialog; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQPushButton( const TQPushButton & ); + TQPushButton &operator=( const TQPushButton & ); +#endif +}; + + +#endif // QT_NO_PUSHBUTTON + +#endif // TQPUSHBUTTON_H diff --git a/src/widgets/qradiobutton.cpp b/src/widgets/qradiobutton.cpp new file mode 100644 index 000000000..98956c93a --- /dev/null +++ b/src/widgets/qradiobutton.cpp @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** Implementation of TQRadioButton class +** +** Created : 940222 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQRadioButton qradiobutton.h + \brief The TQRadioButton widget provides a radio button with a text or pixmap label. + + \ingroup basic + \mainclass + + TQRadioButton and TQCheckBox 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 TQButtonGroup. + + 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 TQPushButton, 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 TQPushButton TQToolButton + \link guibooks.html#fowler GUI Design Handbook: Radio Button\endlink +*/ + +/*! + \property TQRadioButton::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 TQButtonGroup. The default value is + FALSE (unchecked). +*/ + +/*! + \property TQRadioButton::autoMask \brief whether the radio button + is automatically masked + + \sa TQWidget::setAutoMask() +*/ + +/*! + Constructs a radio button with no text. + + The \a parent and \a name arguments are sent on to the TQWidget + constructor. +*/ + +TQRadioButton::TQRadioButton( TQWidget *parent, const char *name ) + : TQButton( 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 TQWidget + constructor. +*/ + +TQRadioButton::TQRadioButton( const TQString &text, TQWidget *parent, + const char *name ) + : TQButton( parent, name, WNoAutoErase | WMouseNoMask ) +{ + init(); + setText( text ); +} + + +/* + Initializes the radio button. +*/ + +void TQRadioButton::init() +{ + setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ) ); + setToggleButton( TRUE ); +#ifndef QT_NO_BUTTONGROUP + TQButtonGroup *bgrp = ::qt_cast<TQButtonGroup*>(parentWidget()); + if ( bgrp ) + bgrp->setRadioButtonExclusive( TRUE ); +#endif +} + +void TQRadioButton::setChecked( bool check ) +{ + setOn( check ); +} + + + + +/*! + \reimp +*/ +TQSize TQRadioButton::sizeHint() const +{ + // Any more complex, and we will use style().itemRect() + // NB: TQCheckBox::sizeHint() is similar + constPolish(); + + TQPainter p(this); + TQSize sz = style().itemRect(&p, TQRect(0, 0, 1, 1), ShowPrefix, FALSE, + pixmap(), text()).size(); + + return (style().sizeFromContents(TQStyle::CT_RadioButton, this, sz). + expandedTo(TQApplication::globalStrut())); +} + + +/*! + \reimp +*/ +bool TQRadioButton::hitButton( const TQPoint &pos ) const +{ + TQRect r = + TQStyle::visualRect( style().subRect( TQStyle::SR_RadioButtonFocusRect, + this ), this ); + if ( qApp->reverseLayout() ) { + r.setRight( width() ); + } else { + r.setLeft( 0 ); + } + return r.contains( pos ); +} + + +/*! + \reimp +*/ +void TQRadioButton::drawButton( TQPainter *paint ) +{ + TQPainter *p = paint; + TQRect irect = TQStyle::visualRect( style().subRect(TQStyle::SR_RadioButtonIndicator, this), this ); + const TQColorGroup &cg = colorGroup(); + +#if !defined( QT_NO_TEXTSTREAM ) && !defined( Q_WS_MACX ) +# define SAVE_RADIOBUTTON_PIXMAPS +#endif +#if defined(SAVE_RADIOBUTTON_PIXMAPS) + TQString 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; + + TQTextOStream os(&pmkey); + os << "$qt_radio_" << style().className() << "_" + << palette().serialNumber() << "_" << irect.width() << "x" << irect.height() << "_" << kf; + TQPixmap *pm = TQPixmapCache::find( pmkey ); + if ( pm ) { // pixmap exists + drawButtonLabel( p ); + p->drawPixmap( irect.topLeft(), *pm ); + return; + } + bool use_pm = TRUE; + TQPainter pmpaint; + int wx, wy; + if ( use_pm ) { + pm = new TQPixmap( irect.size() ); // create new pixmap + Q_CHECK_PTR( pm ); + pm->fill(paletteBackgroundColor()); + TQPainter::redirect(this, pm); + pmpaint.begin(this); + p = &pmpaint; // draw in pixmap + wx = irect.x(); // save x,y coords + wy = irect.y(); + irect.moveTopLeft(TQPoint(0, 0)); + p->setBackgroundColor(paletteBackgroundColor()); + } +#endif + + TQStyle::SFlags flags = TQStyle::Style_Default; + if ( isEnabled() ) + flags |= TQStyle::Style_Enabled; + if ( hasFocus() ) + flags |= TQStyle::Style_HasFocus; + if ( isDown() ) + flags |= TQStyle::Style_Down; + if ( hasMouse() ) + flags |= TQStyle::Style_MouseOver; + if ( state() == TQButton::On ) + flags |= TQStyle::Style_On; + else if ( state() == TQButton::Off ) + flags |= TQStyle::Style_Off; + + style().drawControl(TQStyle::CE_RadioButton, p, this, irect, cg, flags); + +#if defined(SAVE_RADIOBUTTON_PIXMAPS) + if ( use_pm ) { + pmpaint.end(); + TQPainter::redirect(this, NULL); + if ( backgroundPixmap() || backgroundMode() == X11ParentRelative ) { + TQBitmap bm( pm->size() ); + bm.fill( color0 ); + pmpaint.begin( &bm ); + style().drawControlMask(TQStyle::CE_RadioButton, &pmpaint, this, irect); + pmpaint.end(); + pm->setMask( bm ); + } + p = paint; // draw in default device + p->drawPixmap( wx, wy, *pm ); + if (!TQPixmapCache::insert(pmkey, pm) ) // save in cache + delete pm; + } +#endif + + drawButtonLabel( p ); +} + + + +/*! + \reimp +*/ +void TQRadioButton::drawButtonLabel( TQPainter *p ) +{ + TQRect r = + TQStyle::visualRect( style().subRect(TQStyle::SR_RadioButtonContents, + this), this ); + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + if (isDown()) + flags |= TQStyle::Style_Down; + if (state() == TQButton::On) + flags |= TQStyle::Style_On; + else if (state() == TQButton::Off) + flags |= TQStyle::Style_Off; + + style().drawControl(TQStyle::CE_RadioButtonLabel, p, this, r, colorGroup(), flags); +} + + +/*! + \reimp +*/ +void TQRadioButton::resizeEvent( TQResizeEvent* e ) +{ + TQButton::resizeEvent(e); + if ( isVisible() ) { + TQPainter p(this); + TQSize isz = style().itemRect(&p, TQRect(0, 0, 1, 1), ShowPrefix, FALSE, + pixmap(), text()).size(); + TQSize wsz = (style().sizeFromContents(TQStyle::CT_RadioButton, this, isz). + expandedTo(TQApplication::globalStrut())); + + update(wsz.width(), isz.width(), 0, wsz.height()); + } + if (autoMask()) + updateMask(); +} + +/*! + \reimp +*/ +void TQRadioButton::updateMask() +{ + TQRect irect = + TQStyle::visualRect( style().subRect( TQStyle::SR_RadioButtonIndicator, + this ), this ); + + TQBitmap bm(width(), height()); + bm.fill(color0); + + TQPainter p( &bm, this ); + style().drawControlMask(TQStyle::CE_RadioButton, &p, this, irect); + if ( ! text().isNull() || ( pixmap() && ! pixmap()->isNull() ) ) { + TQRect crect = + TQStyle::visualRect( style().subRect( TQStyle::SR_RadioButtonContents, + this ), this ); + TQRect frect = + TQStyle::visualRect( style().subRect( TQStyle::SR_RadioButtonFocusRect, + this ), this ); + TQRect 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 000000000..386a66716 --- /dev/null +++ b/src/widgets/qradiobutton.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Definition of TQRadioButton class +** +** Created : 940222 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQRADIOBUTTON_H +#define TQRADIOBUTTON_H + +#ifndef QT_H +#include "qbutton.h" +#endif // QT_H + +#ifndef QT_NO_RADIOBUTTON + +class Q_EXPORT TQRadioButton : public TQButton +{ + Q_OBJECT + Q_PROPERTY( bool checked READ isChecked WRITE setChecked ) + Q_OVERRIDE( bool autoMask DESIGNABLE true SCRIPTABLE true ) + +public: + TQRadioButton( TQWidget *parent, const char* name=0 ); + TQRadioButton( const TQString &text, TQWidget *parent, const char* name=0 ); + + bool isChecked() const; + + TQSize sizeHint() const; + +public slots: + virtual void setChecked( bool check ); + +protected: + bool hitButton( const TQPoint & ) const; + void drawButton( TQPainter * ); + void drawButtonLabel( TQPainter * ); + void updateMask(); + + void resizeEvent( TQResizeEvent* ); + +private: + void init(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQRadioButton( const TQRadioButton & ); + TQRadioButton &operator=( const TQRadioButton & ); +#endif +}; + + +inline bool TQRadioButton::isChecked() const +{ return isOn(); } + +#endif // QT_NO_RADIOBUTTON + +#endif // TQRADIOBUTTON_H diff --git a/src/widgets/qrangecontrol.cpp b/src/widgets/qrangecontrol.cpp new file mode 100644 index 000000000..9c4371715 --- /dev/null +++ b/src/widgets/qrangecontrol.cpp @@ -0,0 +1,565 @@ +/**************************************************************************** +** +** Implementation of TQRangeControl class +** +** Created : 940427 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qrangecontrol.h" +#ifndef QT_NO_RANGECONTROL +#include "qglobal.h" +#include <limits.h> + +static bool sumOutOfRange(int current, int add); + + +/*! + \class TQRangeControl qrangecontrol.h + \brief The TQRangeControl class provides an integer value within a range. + + \ingroup misc + + Although originally designed for the TQScrollBar widget, the + TQRangeControl can also be used in conjunction with other widgets + such as TQSlider and TQSpinBox. Here are the five main concepts in + the class: + + \list 1 + + \i \e{Current value} The bounded integer that + TQRangeControl 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 + TQRangeControl 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 + TQRangeControl 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(). + + TQRangeControl provides three virtual functions that are well + suited for updating the on-screen representation of range controls + and emitting signals: valueChange(), rangeChange() and + stepChange(). + + TQRangeControl 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 TQRangeControl 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 TQRangeControl because TQRangeControl is + not derived from TQWidget. +*/ + + +/*! + 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. +*/ + +TQRangeControl::TQRangeControl() +{ + 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()). +*/ + +TQRangeControl::TQRangeControl( int minValue, int maxValue, + int lineStep, int pageStep, + int value ) +{ + minVal = minValue; + maxVal = maxValue; + line = TQABS( lineStep ); + page = TQABS( pageStep ); + prevVal = minVal - 1; + val = bound( value ); + d = 0; +} + +/*! + Destroys the range control +*/ + +TQRangeControl::~TQRangeControl() +{ +} + + +/*! + \fn int TQRangeControl::value() const + + Returns the current range control value. This is guaranteed to be + within the range [minValue(), maxValue()]. + + \sa setValue() prevValue() +*/ + +/*! + \fn int TQRangeControl::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 TQRangeControl::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 TQRangeControl::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 TQRangeControl::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 TQRangeControl::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 TQRangeControl::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 TQRangeControl::subtractLine() +{ + if (!sumOutOfRange(value(), -lineStep())) + setValue(value() - lineStep()); +} + + +/*! + \fn int TQRangeControl::minValue() const + + Returns the minimum value of the range. + + \sa setMinValue() setRange() maxValue() +*/ + +/*! + \fn int TQRangeControl::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 TQRangeControl::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 TQRangeControl::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 TQRangeControl::setRange( int minValue, int maxValue ) +{ + if ( minValue > maxValue ) { +#if defined(QT_CHECK_RANGE) + qWarning( "TQRangeControl::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 TQRangeControl::lineStep() const + + Returns the line step. + + \sa setSteps() pageStep() +*/ + +/*! + \fn int TQRangeControl::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 TQRangeControl::setSteps( int lineStep, int pageStep ) +{ + if ( lineStep != line || pageStep != page ) { + line = TQABS( lineStep ); + page = TQABS( 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 TQRangeControl::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 TQRangeControl::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 TQRangeControl::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 TQRangeControl itself. + + \sa setValue() value() minValue() maxValue() +*/ + +int TQRangeControl::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 TQScrollBar on-screen. + + \sa valueFromPosition() +*/ + +int TQRangeControl::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 TQScrollBar and want to handle mouse press + events. This function then maps screen coordinates to the logical + values. + + \sa positionFromValue() +*/ + +int TQRangeControl::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 000000000..c683d367c --- /dev/null +++ b/src/widgets/qrangecontrol.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Definition of TQRangeControl class +** +** Created : 940427 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQRANGECONTROL_H +#define TQRANGECONTROL_H + +#ifndef QT_H +#include "qglobal.h" +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_RANGECONTROL + + +class TQRangeControlPrivate; + + +class Q_EXPORT TQRangeControl +{ +public: + TQRangeControl(); + TQRangeControl( int minValue, int maxValue, + int lineStep, int pageStep, int value ); + virtual ~TQRangeControl(); + 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; + + TQRangeControlPrivate * d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQRangeControl( const TQRangeControl & ); + TQRangeControl &operator=( const TQRangeControl & ); +#endif +}; + + +inline int TQRangeControl::value() const +{ return val; } + +inline int TQRangeControl::prevValue() const +{ return prevVal; } + +inline int TQRangeControl::minValue() const +{ return minVal; } + +inline int TQRangeControl::maxValue() const +{ return maxVal; } + +inline int TQRangeControl::lineStep() const +{ return line; } + +inline int TQRangeControl::pageStep() const +{ return page; } + + +#endif // QT_NO_RANGECONTROL + +#ifndef QT_NO_SPINWIDGET + +class TQSpinWidgetPrivate; +class Q_EXPORT TQSpinWidget : public TQWidget +{ + Q_OBJECT +public: + TQSpinWidget( TQWidget* parent=0, const char* name=0 ); + ~TQSpinWidget(); + + void setEditWidget( TQWidget * widget ); + TQWidget * editWidget(); + + TQRect upRect() const; + TQRect 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( TQMouseEvent *e ); + void resizeEvent( TQResizeEvent* ev ); + void mouseReleaseEvent( TQMouseEvent *e ); + void mouseMoveEvent( TQMouseEvent *e ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( TQWheelEvent * ); +#endif + void styleChange( TQStyle& ); + void paintEvent( TQPaintEvent * ); + void enableChanged( bool old ); + void windowActivationChange( bool ); + +private slots: + void timerDone(); + void timerDoneEx(); + +private: + TQSpinWidgetPrivate * d; + + void updateDisplay(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQSpinWidget( const TQSpinWidget& ); + TQSpinWidget& operator=( const TQSpinWidget& ); +#endif +}; + +#endif // QT_NO_SPINWIDGET + +#endif // TQRANGECONTROL_H diff --git a/src/widgets/qscrollbar.cpp b/src/widgets/qscrollbar.cpp new file mode 100644 index 000000000..1a2a11c7a --- /dev/null +++ b/src/widgets/qscrollbar.cpp @@ -0,0 +1,1072 @@ +/**************************************************************************** +** +** Implementation of TQScrollBar class +** +** Created : 940427 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQScrollBar + \brief The TQScrollBar 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 TQRangeControl 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 + + TQScrollBar has very few of its own functions; it mostly relies on + TQRangeControl. 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. TQScrollBar 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 TQt), also use the pageStep() value to calculate the + size of the slider. + + In addition to the access functions from TQRangeControl, TQScrollBar + 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 TQRangeControl. + \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 + + TQScrollBar only provides integer ranges. Note that although + TQScrollBar 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 + TQScrollView class, which encapsulates the common uses for scroll + bars. + + <img src=qscrbar-m.png> <img src=qscrbar-w.png> + + \sa TQSlider TQSpinBox TQScrollView + \link guibooks.html#fowler GUI Design Handbook: Scroll Bar\endlink +*/ + + +/*! + \fn void TQScrollBar::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 TQScrollBar::sliderPressed() + + This signal is emitted when the user presses the slider with the + mouse. +*/ + +/*! + \fn void TQScrollBar::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 TQScrollBar::sliderReleased() + + This signal is emitted when the user releases the slider with the + mouse. +*/ + +/*! + \fn void TQScrollBar::nextLine() + + This signal is emitted when the scroll bar scrolls one line down + or right. +*/ + +/*! + \fn void TQScrollBar::prevLine() + + This signal is emitted when the scroll bar scrolls one line up or + left. +*/ + +/*! + \fn void TQScrollBar::nextPage() + + This signal is emitted when the scroll bar scrolls one page down + or right. +*/ + +/*! + \fn void TQScrollBar::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 TQWidget + 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. +*/ + +TQScrollBar::TQScrollBar( TQWidget *parent, const char *name ) + : TQWidget( parent, name ), orient( Vertical ) +{ + init(); +} + +/*! + Constructs a scroll bar. + + The \a orientation must be \c TQt::Vertical or \c TQt::Horizontal. + + The \a parent and \a name arguments are sent on to the TQWidget + 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. +*/ + +TQScrollBar::TQScrollBar( Orientation orientation, TQWidget *parent, + const char *name ) + : TQWidget( 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 TQWidget + constructor. +*/ + +TQScrollBar::TQScrollBar( int minValue, int maxValue, int lineStep, int pageStep, + int value, Orientation orientation, + TQWidget *parent, const char *name ) + : TQWidget( parent, name ), + TQRangeControl( minValue, maxValue, lineStep, pageStep, value ), + orient( orientation ) +{ + init(); +} + +/*! + Destructor. +*/ +TQScrollBar::~TQScrollBar() +{ +} + +void TQScrollBar::init() +{ + track = TRUE; + sliderPos = 0; + pressedControl = TQStyle::SC_None; + clickedAt = FALSE; + setFocusPolicy( NoFocus ); + + repeater = 0; + + setBackgroundMode((TQt::BackgroundMode) + style().styleHint(TQStyle::SH_ScrollBar_BackgroundMode)); + + TQSizePolicy sp( TQSizePolicy::Minimum, TQSizePolicy::Fixed ); + if ( orient == Vertical ) + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); +} + + +/*! + \property TQScrollBar::orientation + \brief the orientation of the scroll bar + + The orientation must be \l TQt::Vertical (the default) or \l + TQt::Horizontal. +*/ + +void TQScrollBar::setOrientation( Orientation orientation ) +{ + if ( orientation == orient ) + return; + if ( !testWState( WState_OwnSizePolicy ) ) { + TQSizePolicy sp = sizePolicy(); + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); + } + + orient = orientation; + + positionSliderFromValue(); + update(); + updateGeometry(); +} + +/*! + \property TQScrollBar::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 TQScrollBar::draggingSlider + \brief whether the user has clicked the mouse on the slider and is currently dragging it +*/ + +bool TQScrollBar::draggingSlider() const +{ + return pressedControl == TQStyle::SC_ScrollBarSlider; +} + + +/*! + Reimplements the virtual function TQWidget::setPalette(). + + Sets the background color to the mid color for Motif style scroll + bars using palette \a p. +*/ + +void TQScrollBar::setPalette( const TQPalette &p ) +{ + TQWidget::setPalette( p ); + setBackgroundMode((TQt::BackgroundMode) + style().styleHint(TQStyle::SH_ScrollBar_BackgroundMode)); +} + + +/*! \reimp */ +TQSize TQScrollBar::sizeHint() const +{ + constPolish(); + int sbextent = style().pixelMetric(TQStyle::PM_ScrollBarExtent, this); + + if ( orient == Horizontal ) { + return TQSize( 30, sbextent ); + } else { + return TQSize( sbextent, 30 ); + } +} + +/*! \fn void TQScrollBar::setSizePolicy( TQSizePolicy::SizeType, TQSizePolicy::SizeType, bool ) + \reimp +*/ + +/*! \reimp */ +void TQScrollBar::setSizePolicy( TQSizePolicy sp ) +{ + //## remove 4.0 + TQWidget::setSizePolicy( sp ); +} + +/*! + \internal + Implements the virtual TQRangeControl function. +*/ + +void TQScrollBar::valueChange() +{ + int tmp = sliderPos; + positionSliderFromValue(); + if ( tmp != sliderPos && isVisible() ) + drawControls(TQStyle::SC_ScrollBarAddPage | + TQStyle::SC_ScrollBarSubPage | + TQStyle::SC_ScrollBarSlider, + pressedControl ); + emit valueChanged(value()); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif +} + +/*! + \internal + Implements the virtual TQRangeControl function. +*/ + +void TQScrollBar::stepChange() +{ + rangeChange(); +} + +/*! + \internal + Implements the virtual TQRangeControl function. +*/ + +void TQScrollBar::rangeChange() +{ + positionSliderFromValue(); + + if ( isVisible() ) + drawControls(TQStyle::SC_ScrollBarAddLine | + TQStyle::SC_ScrollBarSubLine | + TQStyle::SC_ScrollBarAddPage | + TQStyle::SC_ScrollBarSubPage | + TQStyle::SC_ScrollBarFirst | + TQStyle::SC_ScrollBarLast | + TQStyle::SC_ScrollBarSlider, + pressedControl ); +} + + +/*! + Handles timer events for the scroll bar. +*/ + +void TQScrollBar::doAutoRepeat() +{ + bool sendRepeat = clickedAt; +#if !defined( QT_NO_CURSOR ) && !defined( QT_NO_STYLE ) + if(sendRepeat && (pressedControl == TQStyle::SC_ScrollBarAddPage || + pressedControl == TQStyle::SC_ScrollBarSubPage) && + style().styleHint(TQStyle::SH_ScrollBar_StopMouseOverSlider, this) && + style().querySubControl(TQStyle::CC_ScrollBar, this, + mapFromGlobal(TQCursor::pos()) ) == TQStyle::SC_ScrollBarSlider) + sendRepeat = FALSE; +#endif + if ( sendRepeat ){ + if ( repeater ) + repeater->changeInterval( repeatTime ); + action( (TQStyle::SubControl) pressedControl ); + TQApplication::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 TQScrollBar::startAutoRepeat() +{ + if ( !repeater ) { + repeater = new TQTimer( this, "auto-repeat timer" ); + connect( repeater, SIGNAL(timeout()), + this, SLOT(doAutoRepeat()) ); + } + repeater->start( thresholdTime, FALSE ); +} + + +/*! + Stops the auto-repeat logic. +*/ + +void TQScrollBar::stopAutoRepeat() +{ + delete repeater; + repeater = 0; +} + + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void TQScrollBar::wheelEvent( TQWheelEvent *e ) +{ + static float offset = 0; + static TQScrollBar* 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 = TQMIN( TQApplication::wheelScrollLines()*lineStep(), + pageStep() ); + if ( ( e->state() & ControlButton ) || ( e->state() & ShiftButton ) ) + step = pageStep(); + offset += -e->delta()*step/120; + if (TQABS(offset)<1) + return; + setValue( value() + int(offset) ); + offset -= int(offset); +} +#endif + +/*! + \reimp +*/ +void TQScrollBar::keyPressEvent( TQKeyEvent *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 TQScrollBar::resizeEvent( TQResizeEvent * ) +{ + positionSliderFromValue(); +} + + +/*! + \reimp +*/ +void TQScrollBar::paintEvent( TQPaintEvent * ) +{ + TQPainter p( this ); + drawControls(TQStyle::SC_ScrollBarAddLine | + TQStyle::SC_ScrollBarSubLine | + TQStyle::SC_ScrollBarAddPage | + TQStyle::SC_ScrollBarSubPage | + TQStyle::SC_ScrollBarFirst | + TQStyle::SC_ScrollBarLast | + TQStyle::SC_ScrollBarSlider, + pressedControl, &p ); +} + +static TQCOORD sliderStartPos = 0; + +/*! + \reimp + */ +void TQScrollBar::contextMenuEvent( TQContextMenuEvent *e ) +{ + if(clickedAt) + e->consume(); + else + e->ignore(); +} + +/*! + \reimp +*/ +void TQScrollBar::mousePressEvent( TQMouseEvent *e ) +{ + bool midButtonAbsPos = + style().styleHint(TQStyle::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(TQStyle::CC_ScrollBar, this, e->pos() ); + + if ( (pressedControl == TQStyle::SC_ScrollBarAddPage || + pressedControl == TQStyle::SC_ScrollBarSubPage || + pressedControl == TQStyle::SC_ScrollBarSlider ) && + ((midButtonAbsPos && e->button() == MidButton) || + style().styleHint(TQStyle::SH_ScrollBar_LeftClickAbsolutePosition) && e->button() == LeftButton)) { + + TQRect sr = style().querySubControlMetrics(TQStyle::CC_ScrollBar, this, + TQStyle::SC_ScrollBarSlider ), + gr = style().querySubControlMetrics(TQStyle::CC_ScrollBar, this, + TQStyle::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 = TQMIN( newSliderPos, sliderMax - sliderLength ); + newSliderPos = TQMAX( newSliderPos, sliderMin ); + setValue( sliderPosToRangeValue(newSliderPos) ); + sliderPos = newSliderPos; + pressedControl = TQStyle::SC_ScrollBarSlider; + } + + if ( pressedControl == TQStyle::SC_ScrollBarSlider ) { + clickOffset = (TQCOORD)( (HORIZONTAL ? e->pos().x() : e->pos().y()) + - sliderPos ); + slidePrevVal = value(); + sliderStartPos = sliderPos; + drawControls( pressedControl, pressedControl ); + emit sliderPressed(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ScrollingStart ); +#endif + } else if ( pressedControl != TQStyle::SC_None ) { + drawControls( pressedControl, pressedControl ); + action( (TQStyle::SubControl) pressedControl ); + startAutoRepeat(); + } +} + + +/*! + \reimp +*/ +void TQScrollBar::mouseReleaseEvent( TQMouseEvent *e ) +{ + if ( !clickedAt ) + return; + + if ( e->stateAfter() & MouseButtonMask ) // some other button is still pressed + return; + + TQStyle::SubControl tmp = (TQStyle::SubControl) pressedControl; + clickedAt = FALSE; + stopAutoRepeat(); + mouseMoveEvent( e ); // Might have moved since last mouse move event. + pressedControl = TQStyle::SC_None; + + if (tmp == TQStyle::SC_ScrollBarSlider) { + directSetValue( calculateValueFromSlider() ); + emit sliderReleased(); + if ( value() != prevValue() ) { + emit valueChanged( value() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif + } +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ScrollingEnd ); +#endif + } + drawControls( tmp, pressedControl ); + if ( e->button() == MidButton ) + repaint( FALSE ); +} + + +/*! + \reimp +*/ +void TQScrollBar::mouseMoveEvent( TQMouseEvent *e ) +{ + if ( !isVisible() ) { + clickedAt = FALSE; + return; + } + + bool mcab = style().styleHint(TQStyle::SH_ScrollBar_MiddleClickAbsolutePosition, + this); + if ( ! clickedAt || ! (e->state() & LeftButton || + ((e->state() & MidButton) && mcab))) + return; + + int newSliderPos; + if ( pressedControl == TQStyle::SC_ScrollBarSlider ) { + TQRect gr = style().querySubControlMetrics(TQStyle::CC_ScrollBar, this, + TQStyle::SC_ScrollBarGroove ), + sr = style().querySubControlMetrics(TQStyle::CC_ScrollBar, this, + TQStyle::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; + } + + TQRect r = rect(); + int m = style().pixelMetric(TQStyle::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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif + } + slidePrevVal = newVal; + sliderPos = (TQCOORD)newSliderPos; + drawControls( TQStyle::SC_ScrollBarAddPage | + TQStyle::SC_ScrollBarSlider | + TQStyle::SC_ScrollBarSubPage, + pressedControl ); + } else if (! style().styleHint(TQStyle::SH_ScrollBar_ScrollWhenPointerLeavesControl)) { + // stop scrolling when the mouse pointer leaves a control + // similar to push buttons + if ( pressedControl != (uint)style().querySubControl(TQStyle::CC_ScrollBar, this, e->pos() ) ) { + drawControls( pressedControl, TQStyle::SC_None ); + stopAutoRepeat(); + } else if ( !repeater ) { + drawControls( pressedControl, pressedControl ); + action( (TQStyle::SubControl) pressedControl ); + startAutoRepeat(); + } + } +} + + +/*! + \fn int TQScrollBar::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() +*/ + +TQRect TQScrollBar::sliderRect() const +{ + return style().querySubControlMetrics(TQStyle::CC_ScrollBar, this, + TQStyle::SC_ScrollBarSlider ); +} + +void TQScrollBar::positionSliderFromValue() +{ + sliderPos = (TQCOORD)rangeValueToSliderPos( value() ); +} + +int TQScrollBar::calculateValueFromSlider() const +{ + return sliderPosToRangeValue( sliderPos ); +} + +int TQScrollBar::rangeValueToSliderPos( int v ) const +{ + TQRect gr = style().querySubControlMetrics(TQStyle::CC_ScrollBar, this, + TQStyle::SC_ScrollBarGroove ); + TQRect sr = style().querySubControlMetrics(TQStyle::CC_ScrollBar, this, + TQStyle::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 TQScrollBar::sliderPosToRangeValue( int pos ) const +{ + TQRect gr = style().querySubControlMetrics(TQStyle::CC_ScrollBar, this, + TQStyle::SC_ScrollBarGroove ); + TQRect sr = style().querySubControlMetrics(TQStyle::CC_ScrollBar, this, + TQStyle::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 TQScrollBar::action( int control ) +{ + switch( control ) { + case TQStyle::SC_ScrollBarAddLine: + addLine(); + emit nextLine(); + break; + case TQStyle::SC_ScrollBarSubLine: + subtractLine(); + emit prevLine(); + break; + case TQStyle::SC_ScrollBarAddPage: + addPage(); + emit nextPage(); + break; + case TQStyle::SC_ScrollBarSubPage: + subtractPage(); + emit prevPage(); + break; + case TQStyle::SC_ScrollBarFirst: + setValue( minValue() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif + emit valueChanged( minValue() ); + break; + case TQStyle::SC_ScrollBarLast: + setValue( maxValue() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif + emit valueChanged( maxValue() ); + break; + default: + break; + } +} + + +void TQScrollBar::drawControls( uint controls, uint activeControl ) const +{ + TQPainter p ( this ); + drawControls( controls, activeControl, &p ); +} + + +void TQScrollBar::drawControls( uint controls, uint activeControl, + TQPainter *p ) const +{ + if ( !isUpdatesEnabled() ) + return; + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + if ( orientation() == Horizontal ) + flags |= TQStyle::Style_Horizontal; + + style().drawComplexControl(TQStyle::CC_ScrollBar, p, this, rect(), colorGroup(), + flags, (TQStyle::SubControl) controls, + (TQStyle::SubControl) activeControl ); +} + +/*! + \reimp +*/ +void TQScrollBar::styleChange( TQStyle& old ) +{ + positionSliderFromValue(); + setBackgroundMode((TQt::BackgroundMode) + style().styleHint(TQStyle::SH_ScrollBar_BackgroundMode)); + TQWidget::styleChange( old ); +} + +/*! + \property TQScrollBar::minValue + \brief the scroll bar's minimum value + + When setting this property, the \l TQScrollBar::maxValue is + adjusted if necessary to ensure that the range remains valid. + + \sa setRange() +*/ +int TQScrollBar::minValue() const +{ + return TQRangeControl::minValue(); +} + +void TQScrollBar::setMinValue( int minVal ) +{ + TQRangeControl::setMinValue( minVal ); +} + +/*! + \property TQScrollBar::maxValue + \brief the scroll bar's maximum value + + When setting this property, the \l TQScrollBar::minValue is + adjusted if necessary to ensure that the range remains valid. + + \sa setRange() +*/ +int TQScrollBar::maxValue() const +{ + return TQRangeControl::maxValue(); +} + +void TQScrollBar::setMaxValue( int maxVal ) +{ + TQRangeControl::setMaxValue( maxVal ); +} + +/*! + \property TQScrollBar::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() TQRangeControl::pageStep() setRange() +*/ + +int TQScrollBar::lineStep() const +{ + return TQRangeControl::lineStep(); +} + +/*! + \property TQScrollBar::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 TQRangeControl::setSteps() setLineStep() setRange() +*/ + +int TQScrollBar::pageStep() const +{ + return TQRangeControl::pageStep(); +} + +void TQScrollBar::setLineStep( int i ) +{ + setSteps( i, pageStep() ); +} + +void TQScrollBar::setPageStep( int i ) +{ + setSteps( lineStep(), i ); +} + +/*! + \property TQScrollBar::value + \brief the scroll bar's value + + \sa TQRangeControl::value() prevValue() +*/ + +int TQScrollBar::value() const +{ + return TQRangeControl::value(); +} + +void TQScrollBar::setValue( int i ) +{ + TQRangeControl::setValue( i ); +} + + +/*! + This function is called when the scrollbar is hidden. +*/ +void TQScrollBar::hideEvent( TQHideEvent* ) +{ + pressedControl = TQStyle::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 000000000..08cf46707 --- /dev/null +++ b/src/widgets/qscrollbar.h @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Definition of TQScrollBar class +** +** Created : 940427 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSCROLLBAR_H +#define TQSCROLLBAR_H + +class TQTimer; + +#ifndef QT_H +#include "qwidget.h" +#include "qrangecontrol.h" +#endif // QT_H + +#ifndef QT_NO_SCROLLBAR + +class Q_EXPORT TQScrollBar : public TQWidget, public TQRangeControl +{ + 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: + TQScrollBar( TQWidget *parent, const char* name = 0 ); + TQScrollBar( Orientation, TQWidget *parent, const char* name = 0 ); + TQScrollBar( int minValue, int maxValue, int lineStep, int pageStep, + int value, Orientation, TQWidget *parent, const char* name = 0 ); + ~TQScrollBar(); + + virtual void setOrientation( Orientation ); + Orientation orientation() const; + virtual void setTracking( bool enable ); + bool tracking() const; + bool draggingSlider() const; + + virtual void setPalette( const TQPalette & ); + virtual TQSize sizeHint() const; + virtual void setSizePolicy( TQSizePolicy sp ); + void setSizePolicy( TQSizePolicy::SizeType hor, TQSizePolicy::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; + TQRect 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( TQWheelEvent * ); +#endif + void keyPressEvent( TQKeyEvent * ); + void resizeEvent( TQResizeEvent * ); + void paintEvent( TQPaintEvent * ); + + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void contextMenuEvent( TQContextMenuEvent * ); + void hideEvent( TQHideEvent* ); + + void valueChange(); + void stepChange(); + void rangeChange(); + + void styleChange( TQStyle& ); + +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, + TQPainter *p ) const; + + uint pressedControl; + bool track; + bool clickedAt; + Orientation orient; + + int slidePrevVal; + TQCOORD sliderPos; + TQCOORD clickOffset; + + TQTimer * repeater; + void * d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQScrollBar( const TQScrollBar & ); + TQScrollBar &operator=( const TQScrollBar & ); +#endif +}; + + +inline void TQScrollBar::setTracking( bool t ) +{ + track = t; +} + +inline bool TQScrollBar::tracking() const +{ + return track; +} + +inline TQScrollBar::Orientation TQScrollBar::orientation() const +{ + return orient; +} + +inline int TQScrollBar::sliderStart() const +{ + return sliderPos; +} + +inline void TQScrollBar::setSizePolicy( TQSizePolicy::SizeType hor, TQSizePolicy::SizeType ver, bool hfw ) +{ + TQWidget::setSizePolicy( hor, ver, hfw ); +} + + +#endif // QT_NO_SCROLLBAR + +#endif // TQSCROLLBAR_H diff --git a/src/widgets/qscrollview.cpp b/src/widgets/qscrollview.cpp new file mode 100644 index 000000000..44b6347a9 --- /dev/null +++ b/src/widgets/qscrollview.cpp @@ -0,0 +1,2854 @@ +/**************************************************************************** +** +** Implementation of TQScrollView class +** +** Created : 950524 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQSVChildRec { + TQSVChildRec(TQWidget* c, int xx, int yy) : + child(c), + x(xx), y(yy) + { + } + + void hideOrShow(TQScrollView* sv, TQWidget* clipped_viewport); + void moveTo(TQScrollView* sv, int xx, int yy, TQWidget* clipped_viewport) + { + if ( x != xx || y != yy ) { + x = xx; + y = yy; + hideOrShow(sv,clipped_viewport); + } + } + TQWidget* child; + int x, y; +}; + +void TQSVChildRec::hideOrShow(TQScrollView* sv, TQWidget* 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 TQViewportWidget : public TQWidget +{ + Q_OBJECT + +public: + TQViewportWidget( TQScrollView* parent=0, const char* name=0, WFlags f = 0 ) + : TQWidget( parent, name, f ) {} +}; + +class TQClipperWidget : public TQWidget +{ + Q_OBJECT + +public: + TQClipperWidget( TQWidget * parent=0, const char * name=0, WFlags f=0 ) + : TQWidget ( parent,name,f) {} +}; + +#include "qscrollview.moc" + +class TQScrollViewData { +public: + TQScrollViewData(TQScrollView* parent, int vpwflags) : + hbar( new TQScrollBar( TQScrollBar::Horizontal, parent, "qt_hbar" ) ), + vbar( new TQScrollBar( TQScrollBar::Vertical, parent, "qt_vbar" ) ), + viewport( new TQViewportWidget( 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( TQWidget::PaletteDark ); + viewport->setBackgroundOrigin( TQWidget::WidgetOrigin ); + vMode = TQScrollView::Auto; + hMode = TQScrollView::Auto; + corner = 0; + defaultCorner = new TQWidget( parent, "qt_default_corner" ); + defaultCorner->hide(); + vbar->setSteps( 20, 1/*set later*/ ); + hbar->setSteps( 20, 1/*set later*/ ); + policy = TQScrollView::Default; + signal_choke = FALSE; + static_bg = FALSE; + fake_scroll = FALSE; + hbarPressed = FALSE; + vbarPressed = FALSE; + } + ~TQScrollViewData(); + + TQSVChildRec* rec(TQWidget* w) { return childDict.find(w); } + TQSVChildRec* ancestorRec(TQWidget* w); + TQSVChildRec* addChildRec(TQWidget* w, int x, int y ) + { + TQSVChildRec *r = new TQSVChildRec(w,x,y); + children.append(r); + childDict.insert(w, r); + return r; + } + void deleteChildRec(TQSVChildRec* r) + { + childDict.remove(r->child); + children.removeRef(r); + delete r; + } + + void hideOrShowAll(TQScrollView* sv, bool isScroll = FALSE ); + void moveAllBy(int dx, int dy); + bool anyVisibleChildren(); + void autoMove(TQScrollView* sv); + void autoResize(TQScrollView* sv); + void autoResizeHint(TQScrollView* sv); + void viewportResized( int w, int h ); + + TQScrollBar* hbar; + TQScrollBar* vbar; + bool hbarPressed; + bool vbarPressed; + TQViewportWidget* viewport; + TQClipperWidget* clipped_viewport; + int flags; + TQPtrList<TQSVChildRec> children; + TQPtrDict<TQSVChildRec> childDict; + TQWidget* corner, *defaultCorner; + int vx, vy, vwidth, vheight; // for drawContents-style usage + int l_marg, r_marg, t_marg, b_marg; + TQScrollView::ResizePolicy policy; + TQScrollView::ScrollBarMode vMode; + TQScrollView::ScrollBarMode hMode; +#ifndef QT_NO_DRAGANDDROP + TQPoint cpDragStart; + TQTimer autoscroll_timer; + int autoscroll_time; + int autoscroll_accel; + bool drag_autoscroll; +#endif + TQTimer 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; + TQSize cachedSizeHint; + + inline int contentsX() const { return -vx; } + inline int contentsY() const { return -vy; } + inline int contentsWidth() const { return vwidth; } +}; + +inline TQScrollViewData::~TQScrollViewData() +{ + children.setAutoDelete( TRUE ); +} + +TQSVChildRec* TQScrollViewData::ancestorRec(TQWidget* 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 TQScrollViewData::hideOrShowAll(TQScrollView* 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( TQt::WStaticContents) ) + || static_bg ) + TQApplication::postEvent( clipped_viewport, + new TQPaintEvent( clipped_viewport->clipRegion(), + !clipped_viewport->testWFlags(TQt::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 (TQSVChildRec *r = children.first(); r; r=children.next()) { + r->hideOrShow(sv, clipped_viewport); + } +} + +void TQScrollViewData::moveAllBy(int dx, int dy) +{ + if ( clipped_viewport && !static_bg ) { + clipped_viewport->move( clipped_viewport->x()+dx, + clipped_viewport->y()+dy ); + } else { + for (TQSVChildRec *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 TQScrollViewData::anyVisibleChildren() +{ + for (TQSVChildRec *r = children.first(); r; r=children.next()) { + if (r->child->isVisible()) return TRUE; + } + return FALSE; +} + +void TQScrollViewData::autoMove(TQScrollView* sv) +{ + if ( policy == TQScrollView::AutoOne ) { + TQSVChildRec* r = children.first(); + if (r) + sv->setContentsPos(-r->child->x(),-r->child->y()); + } +} + +void TQScrollViewData::autoResize(TQScrollView* sv) +{ + if ( policy == TQScrollView::AutoOne ) { + TQSVChildRec* r = children.first(); + if (r) + sv->resizeContents(r->child->width(),r->child->height()); + } +} + +void TQScrollViewData::autoResizeHint(TQScrollView* sv) +{ + if ( policy == TQScrollView::AutoOne ) { + TQSVChildRec* r = children.first(); + if (r) { + TQSize s = r->child->sizeHint(); + if ( s.isValid() ) + r->child->resize(s); + } + } else if ( policy == TQScrollView::AutoOneFit ) { + TQSVChildRec* r = children.first(); + if (r) { + TQSize sh = r->child->sizeHint(); + sh = sh.boundedTo( r->child->maximumSize() ); + sv->resizeContents( sh.width(), sh.height() ); + } + } +} + +void TQScrollViewData::viewportResized( int w, int h ) +{ + if ( policy == TQScrollView::AutoOneFit ) { + TQSVChildRec* r = children.first(); + if (r) { + TQSize sh = r->child->sizeHint(); + sh = sh.boundedTo( r->child->maximumSize() ); + r->child->resize( TQMAX(w,sh.width()), TQMAX(h,sh.height()) ); + } + + } +} + + +/*! + \class TQScrollView qscrollview.h + \brief The TQScrollView widget provides a scrolling area with on-demand scroll bars. + + \ingroup abstractwidgets + \mainclass + + The TQScrollView is a large canvas - potentially larger than the + coordinate system normally supported by the underlying window + system. This is important because it is tquite easy to go beyond + these limitations (e.g. many web pages are more than 32000 pixels + high). Additionally, the TQScrollView can have TQWidgets 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 TQScrollView, + 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 TQScrollView 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 TQScrollView (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 TQScrollView. The child should be a child of the + viewport() of the scrollview and be added with addChild(): + \code + TQScrollView* sv = new TQScrollView(...); + TQVBox* big_box = new TQVBox(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 + TQLabel* child1 = new TQLabel("CHILD", big_box); + TQLabel* child2 = new TQLabel("CHILD", big_box); + TQLabel* child3 = new TQLabel("CHILD", big_box); + ... + \endcode + + Here the TQScrollView has four children: the viewport(), the + verticalScrollBar(), the horizontalScrollBar() and a small + cornerWidget(). The viewport() has one child: the big TQVBox. The + TQVBox has the three TQLabel objects as child widgets. When the view + is scrolled, the TQVBox 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 TQScrollView (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 + TQScrollView* sv = new TQScrollView(...); + TQLabel* child1 = new TQLabel("CHILD", sv->viewport()); + sv->addChild(child1); + TQLabel* child2 = new TQLabel("CHILD", sv->viewport()); + sv->addChild(child2); + TQLabel* child3 = new TQLabel("CHILD", sv->viewport()); + sv->addChild(child3); + \endcode + Here, the TQScrollView has the same four children: the viewport(), + the verticalScrollBar(), the horizontalScrollBar() and a small + cornerWidget(). The viewport() has the three TQLabel 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 TQScrollView (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 + TQScrollView* sv = new TQScrollView(...); + sv->enableClipper(TRUE); + TQLabel* child1 = new TQLabel("CHILD", sv->viewport()); + sv->addChild(child1); + TQLabel* child2 = new TQLabel("CHILD", sv->viewport()); + sv->addChild(child2); + TQLabel* child3 = new TQLabel("CHILD", sv->viewport()); + sv->addChild(child3); + \endcode + + Here, the TQScrollView 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 TQScrollView itself. So to turn mouse + tracking on, for example, use viewport()->setMouseTracking(TRUE). + + To enable drag-and-drop, you would setAcceptDrops(TRUE) on the + TQScrollView (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 + TQScrollView 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 TQScrollView 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 TQScrollView, some of the widget flags apply + to the viewport() instead of being sent to the TQWidget constructor + for the TQScrollView. This applies to \c WNoAutoErase, \c + WStaticContents, and \c WPaintClever. See \l TQt::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 TQScrollView 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 TQt + and we recommend specifying the WNoAutoErase flag explicitly. + + <img src=qscrollview-m.png> <img src=qscrollview-w.png> +*/ + + +/*! + \enum TQScrollView::ResizePolicy + + This enum type is used to control a TQScrollView's reaction to + resize events. + + \value Default the TQScrollView selects one of the other settings + automatically when it has to. In this version of TQt, TQScrollView + 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 TQScrollView 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. +*/ + +TQScrollView::TQScrollView( TQWidget *parent, const char *name, WFlags f ) : + TQFrame( parent, name, f & (~WStaticContents) & (~WResizeNoErase) ) +{ + WFlags flags = WResizeNoErase | (f&WPaintClever) | (f&WRepaintNoErase) | (f&WStaticContents); + d = new TQScrollViewData( 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( TQFrame::StyledPanel | TQFrame::Sunken ); + setLineWidth( style().pixelMetric(TQStyle::PM_DefaultFrameWidth, this) ); + setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Expanding ) ); +} + + +/*! + Destroys the TQScrollView. Any children added with addChild() will + be deleted. +*/ +TQScrollView::~TQScrollView() +{ + // 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 + // ~TQWidget 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 TQScrollView::horizontalSliderPressed() + + This signal is emitted whenever the user presses the horizontal slider. +*/ +/*! + \fn void TQScrollView::horizontalSliderReleased() + + This signal is emitted whenever the user releases the horizontal slider. +*/ +/*! + \fn void TQScrollView::verticalSliderPressed() + + This signal is emitted whenever the user presses the vertical slider. +*/ +/*! + \fn void TQScrollView::verticalSliderReleased() + + This signal is emitted whenever the user releases the vertical slider. +*/ +void TQScrollView::hbarIsPressed() +{ + d->hbarPressed = TRUE; + emit( horizontalSliderPressed() ); +} + +void TQScrollView::hbarIsReleased() +{ + d->hbarPressed = FALSE; + emit( horizontalSliderReleased() ); +} + +/*! + Returns TRUE if horizontal slider is pressed by user; otherwise returns FALSE. +*/ +bool TQScrollView::isHorizontalSliderPressed() +{ + return d->hbarPressed; +} + +void TQScrollView::vbarIsPressed() +{ + d->vbarPressed = TRUE; + emit( verticalSliderPressed() ); +} + +void TQScrollView::vbarIsReleased() +{ + d->vbarPressed = FALSE; + emit( verticalSliderReleased() ); +} + +/*! + Returns TRUE if vertical slider is pressed by user; otherwise returns FALSE. +*/ +bool TQScrollView::isVerticalSliderPressed() +{ + return d->vbarPressed; +} + +/*! + \reimp +*/ +void TQScrollView::styleChange( TQStyle& old ) +{ + TQWidget::styleChange( old ); + updateScrollBars(); + d->cachedSizeHint = TQSize(); +} + +/*! + \reimp +*/ +void TQScrollView::fontChange( const TQFont &old ) +{ + TQWidget::fontChange( old ); + updateScrollBars(); + d->cachedSizeHint = TQSize(); +} + +void TQScrollView::hslide( int pos ) +{ + if ( !d->signal_choke ) { + moveContents( -pos, -d->contentsY() ); + TQApplication::syncX(); + } +} + +void TQScrollView::vslide( int pos ) +{ + if ( !d->signal_choke ) { + moveContents( -d->contentsX(), -pos ); + TQApplication::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 TQScrollView::setHBarGeometry(TQScrollBar& 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 TQScrollView::setVBarGeometry( TQScrollBar& 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. +*/ + +TQSize TQScrollView::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 TQSize( 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 TQScrollView::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(); + + TQSize 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 { + TQWidget *tlw = topLevelWidget(); + TQPoint tlw_br = TQPoint(tlw->width(), tlw->height()), + my_br = posInWindow(this) + TQPoint(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( TQScrollView::d->vbar->lineStep(), porth ); + } else { + d->vbar->setRange( 0, 0 ); + } + if ( needh ) { + d->hbar->setRange( 0, TQMAX(0, d->contentsWidth()-portw) ); + d->hbar->setSteps( TQScrollView::d->hbar->lineStep(), portw ); + } else { + d->hbar->setRange( 0, 0 ); + } + + // Position the scrollbars, viewport and corner widget. + int bottom; + bool reverse = TQApplication::reverseLayout(); + int xoffset = ( reverse && (showv || cornerWidget() )) ? vsbExt : 0; + int xpos = reverse ? 0 : w - vsbExt; + bool frameContentsOnly = + style().styleHint(TQStyle::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(TQRect(0, 0, w, h) ); + else + changeFrameRect(TQRect(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(TQRect(0, 0, w, h)); + else + changeFrameRect(TQRect(0, 0, w, bottom)); + clipper()->setGeometry( lmarg, tmarg, + w-lmarg-rmarg, bottom-tmarg-bmarg ); + d->viewportResized( w-lmarg-rmarg, bottom-tmarg-bmarg ); + } + + TQWidget *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 =TQMIN(0,d->contentsWidth()-visibleWidth()); + else +#endif + x =TQMAX(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=TQMAX(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; + + TQSize newVisibleSize( visibleWidth(), visibleHeight() ); + if ( d->clipped_viewport && oldVisibleSize != newVisibleSize ) { + TQResizeEvent e( newVisibleSize, oldVisibleSize ); + viewportResizeEvent( &e ); + } +} + + +/*! + \reimp +*/ +void TQScrollView::show() +{ + if ( isVisible() ) + return; + TQWidget::show(); + updateScrollBars(); + d->hideOrShowAll(this); +} + +/*! + \reimp + */ +void TQScrollView::resize( int w, int h ) +{ + TQWidget::resize( w, h ); +} + +/*! + \reimp +*/ +void TQScrollView::resize( const TQSize& s ) +{ + resize( s.width(), s.height() ); +} + +/*! + \reimp +*/ +void TQScrollView::resizeEvent( TQResizeEvent* event ) +{ + TQFrame::resizeEvent( event ); + +#if 0 + if ( TQApplication::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 TQScrollView::mousePressEvent( TQMouseEvent * e) //#### remove for 4.0 +{ + e->ignore(); +} + +/*! + \reimp +*/ +void TQScrollView::mouseReleaseEvent( TQMouseEvent *e ) //#### remove for 4.0 +{ + e->ignore(); +} + + +/*! + \reimp +*/ +void TQScrollView::mouseDoubleClickEvent( TQMouseEvent *e ) //#### remove for 4.0 +{ + e->ignore(); +} + +/*! + \reimp +*/ +void TQScrollView::mouseMoveEvent( TQMouseEvent *e ) //#### remove for 4.0 +{ + e->ignore(); +} + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void TQScrollView::wheelEvent( TQWheelEvent *e ) +{ + TQWheelEvent 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() ) + TQApplication::sendEvent( horizontalScrollBar(), e); + else if (e->orientation() == Vertical && verticalScrollBar() && verticalScrollBar()->isEnabled() ) + TQApplication::sendEvent( verticalScrollBar(), e); + } else { + e->accept(); + } +} +#endif + +/*! + \reimp +*/ +void TQScrollView::contextMenuEvent( TQContextMenuEvent *e ) +{ + if ( e->reason() != TQContextMenuEvent::Keyboard ) { + e->ignore(); + return; + } + + TQContextMenuEvent ce( e->reason(), viewport()->mapFromGlobal( e->globalPos() ), + e->globalPos(), e->state() ); + viewportContextMenuEvent( &ce ); + if ( ce.isAccepted() ) + e->accept(); + else + e->ignore(); +} + +TQScrollView::ScrollBarMode TQScrollView::vScrollBarMode() const +{ + return d->vMode; +} + + +/*! + \enum TQScrollView::ScrollBarMode + + This enum type describes the various modes of TQScrollView's scroll + bars. + + \value Auto TQScrollView shows a scroll bar when the content is + too large to fit and not otherwise. This is the default. + + \value AlwaysOff TQScrollView never shows a scroll bar. + + \value AlwaysOn TQScrollView always shows a scroll bar. + + (The modes for the horizontal and vertical scroll bars are + independent.) +*/ + + +/*! + \property TQScrollView::vScrollBarMode + \brief the mode for the vertical scroll bar + + The default mode is \c TQScrollView::Auto. + + \sa hScrollBarMode +*/ +void TQScrollView::setVScrollBarMode( ScrollBarMode mode ) +{ + if (d->vMode != mode) { + d->vMode = mode; + updateScrollBars(); + } +} + + +/*! + \property TQScrollView::hScrollBarMode + \brief the mode for the horizontal scroll bar + + The default mode is \c TQScrollView::Auto. + + \sa vScrollBarMode +*/ +TQScrollView::ScrollBarMode TQScrollView::hScrollBarMode() const +{ + return d->hMode; +} + +void TQScrollView::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. +*/ +TQWidget* TQScrollView::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 TQScrollView 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 TQScrollView::setCornerWidget(TQWidget* corner) +{ + TQWidget* 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, (((TQScrollView*)corner))->getWFlags(), + TQPoint(0,0), FALSE ); + } + + updateScrollBars(); + if ( corner ) corner->show(); + } +} + + +void TQScrollView::setResizePolicy( ResizePolicy r ) +{ + d->policy = r; +} + +/*! + \property TQScrollView::resizePolicy + \brief the resize policy + + The default is \c Default. + + \sa ResizePolicy +*/ +TQScrollView::ResizePolicy TQScrollView::resizePolicy() const +{ + return d->policy; +} + +/*! + \reimp +*/ +void TQScrollView::setEnabled( bool enable ) +{ + TQFrame::setEnabled( enable ); +} + +/*! + Removes the \a child widget from the scrolled area. Note that this + happens automatically if the \a child is deleted. +*/ +void TQScrollView::removeChild(TQWidget* child) +{ + if ( !d || !child ) // First check in case we are destructing + return; + + TQSVChildRec *r = d->rec(child); + if ( r ) d->deleteChildRec( r ); +} + +/*! + \reimp +*/ +void TQScrollView::removeChild(TQObject* child) +{ + TQFrame::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 TQScrollView::addChild(TQWidget* child, int x, int y) +{ + if ( !child ) { +#if defined(QT_CHECK_NULL) + qWarning( "TQScrollView::addChild(): Cannot add null child" ); +#endif + return; + } + child->polish(); + child->setBackgroundOrigin(WidgetOrigin); + + if ( child->parentWidget() == viewport() ) { + // May already be there + TQSVChildRec *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, TQPoint(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 TQScrollView::moveChild(TQWidget* child, int x, int y) +{ + addChild(child,x,y); +} + +/*! + Returns the X position of the given \a child widget. Use this + rather than TQWidget::x() for widgets added to the view. + + This function returns 0 if \a child has not been added to the view. +*/ +int TQScrollView::childX(TQWidget* child) +{ + TQSVChildRec *r = d->rec(child); + return r ? r->x : 0; +} + +/*! + Returns the Y position of the given \a child widget. Use this + rather than TQWidget::y() for widgets added to the view. + + This function returns 0 if \a child has not been added to the view. +*/ +int TQScrollView::childY(TQWidget* child) +{ + TQSVChildRec *r = d->rec(child); + return r ? r->y : 0; +} + +/*! \fn bool TQScrollView::childIsVisible(TQWidget*) + \obsolete + + Returns TRUE if \a child is visible. This is equivalent + to child->isVisible(). +*/ + +/*! \fn void TQScrollView::showChild(TQWidget* child, bool y) + \obsolete + + Sets the visibility of \a child. Equivalent to + TQWidget::show() or TQWidget::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 TQScrollView. The event is in \a e and + the object is in \a obj. +*/ + +bool TQScrollView::eventFilter( TQObject *obj, TQEvent *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 TQEvent::Paint: + viewportPaintEvent( (TQPaintEvent*)e ); + break; + case TQEvent::Resize: + if ( !d->clipped_viewport ) + viewportResizeEvent( (TQResizeEvent *)e ); + break; + case TQEvent::MouseButtonPress: + viewportMousePressEvent( (TQMouseEvent*)e ); + if ( ((TQMouseEvent*)e)->isAccepted() ) + return TRUE; + break; + case TQEvent::MouseButtonRelease: + viewportMouseReleaseEvent( (TQMouseEvent*)e ); + if ( ((TQMouseEvent*)e)->isAccepted() ) + return TRUE; + break; + case TQEvent::MouseButtonDblClick: + viewportMouseDoubleClickEvent( (TQMouseEvent*)e ); + if ( ((TQMouseEvent*)e)->isAccepted() ) + return TRUE; + break; + case TQEvent::MouseMove: + viewportMouseMoveEvent( (TQMouseEvent*)e ); + if ( ((TQMouseEvent*)e)->isAccepted() ) + return TRUE; + break; +#ifndef QT_NO_DRAGANDDROP + case TQEvent::DragEnter: + viewportDragEnterEvent( (TQDragEnterEvent*)e ); + break; + case TQEvent::DragMove: { + if ( d->drag_autoscroll ) { + TQPoint vp = ((TQDragMoveEvent*) e)->pos(); + TQRect inside_margin( autoscroll_margin, autoscroll_margin, + visibleWidth() - autoscroll_margin * 2, + visibleHeight() - autoscroll_margin * 2 ); + if ( !inside_margin.contains( vp ) ) { + startDragAutoScroll(); + // Keep sending move events + ( (TQDragMoveEvent*)e )->accept( TQRect(0,0,0,0) ); + } + } + viewportDragMoveEvent( (TQDragMoveEvent*)e ); + } break; + case TQEvent::DragLeave: + stopDragAutoScroll(); + viewportDragLeaveEvent( (TQDragLeaveEvent*)e ); + break; + case TQEvent::Drop: + stopDragAutoScroll(); + viewportDropEvent( (TQDropEvent*)e ); + break; +#endif // QT_NO_DRAGANDDROP + case TQEvent::ContextMenu: + viewportContextMenuEvent( (TQContextMenuEvent*)e ); + if ( ((TQContextMenuEvent*)e)->isAccepted() ) + return TRUE; + break; + case TQEvent::ChildRemoved: + removeChild((TQWidget*)((TQChildEvent*)e)->child()); + break; + case TQEvent::LayoutHint: + d->autoResizeHint(this); + break; + case TQEvent::WindowActivate: + case TQEvent::WindowDeactivate: + return TRUE; + default: + break; + } + } else if ( d && d->rec((TQWidget*)obj) ) { // must be a child + if ( e->type() == TQEvent::Resize ) + d->autoResize(this); + else if ( e->type() == TQEvent::Move ) + d->autoMove(this); + } + return TQFrame::eventFilter( obj, e ); // always continue with standard event processing +} + +/*! + This event handler is called whenever the TQScrollView receives a + mousePressEvent(): the press position in \a e is translated to be a point + on the contents. +*/ +void TQScrollView::contentsMousePressEvent( TQMouseEvent* e ) +{ + e->ignore(); +} + +/*! + This event handler is called whenever the TQScrollView receives a + mouseReleaseEvent(): the release position in \a e is translated to be a + point on the contents. +*/ +void TQScrollView::contentsMouseReleaseEvent( TQMouseEvent* e ) +{ + e->ignore(); +} + +/*! + This event handler is called whenever the TQScrollView 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 TQScrollView::contentsMouseDoubleClickEvent( TQMouseEvent* e ) +{ + contentsMousePressEvent(e); // try mouse press event +} + +/*! + This event handler is called whenever the TQScrollView receives a + mouseMoveEvent(): the mouse position in \a e is translated to be a point + on the contents. +*/ +void TQScrollView::contentsMouseMoveEvent( TQMouseEvent* e ) +{ + e->ignore(); +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + This event handler is called whenever the TQScrollView receives a + dragEnterEvent(): the drag position is translated to be a point + on the contents. +*/ +void TQScrollView::contentsDragEnterEvent( TQDragEnterEvent * ) +{ +} + +/*! + This event handler is called whenever the TQScrollView receives a + dragMoveEvent(): the drag position is translated to be a point on + the contents. +*/ +void TQScrollView::contentsDragMoveEvent( TQDragMoveEvent * ) +{ +} + +/*! + This event handler is called whenever the TQScrollView receives a + dragLeaveEvent(): the drag position is translated to be a point + on the contents. +*/ +void TQScrollView::contentsDragLeaveEvent( TQDragLeaveEvent * ) +{ +} + +/*! + This event handler is called whenever the TQScrollView receives a + dropEvent(): the drop position is translated to be a point on the + contents. +*/ +void TQScrollView::contentsDropEvent( TQDropEvent * ) +{ +} + +#endif // QT_NO_DRAGANDDROP + +/*! + This event handler is called whenever the TQScrollView receives a + wheelEvent() in \a{e}: the mouse position is translated to be a + point on the contents. +*/ +#ifndef QT_NO_WHEELEVENT +void TQScrollView::contentsWheelEvent( TQWheelEvent * e ) +{ + e->ignore(); +} +#endif +/*! + This event handler is called whenever the TQScrollView receives a + contextMenuEvent() in \a{e}: the mouse position is translated to + be a point on the contents. +*/ +void TQScrollView::contentsContextMenuEvent( TQContextMenuEvent *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 TQPainter on the + viewport). The paint event is passed in \a pe. +*/ +void TQScrollView::viewportPaintEvent( TQPaintEvent* pe ) +{ + TQWidget* vp = viewport(); + + TQPainter p(vp); + TQRect r = pe->rect(); + + if ( d->clipped_viewport ) { + TQRect 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 TQWidget::resizeEvent() +*/ +void TQScrollView::viewportResizeEvent( TQResizeEvent* ) +{ +} + +/*! \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(), TQWidget::mousePressEvent() +*/ +void TQScrollView::viewportMousePressEvent( TQMouseEvent* e ) +{ + TQMouseEvent 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 TQWidget::mouseReleaseEvent() +*/ +void TQScrollView::viewportMouseReleaseEvent( TQMouseEvent* e ) +{ + TQMouseEvent 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 TQWidget::mouseDoubleClickEvent() +*/ +void TQScrollView::viewportMouseDoubleClickEvent( TQMouseEvent* e ) +{ + TQMouseEvent 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 TQWidget::mouseMoveEvent() +*/ +void TQScrollView::viewportMouseMoveEvent( TQMouseEvent* e ) +{ + TQMouseEvent 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 TQWidget::dragEnterEvent() +*/ +void TQScrollView::viewportDragEnterEvent( TQDragEnterEvent* 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 TQWidget::dragMoveEvent() +*/ +void TQScrollView::viewportDragMoveEvent( TQDragMoveEvent* 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 TQWidget::dragLeaveEvent() +*/ +void TQScrollView::viewportDragLeaveEvent( TQDragLeaveEvent* 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 TQWidget::dropEvent() +*/ +void TQScrollView::viewportDropEvent( TQDropEvent* 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 TQWidget::wheelEvent() +*/ +#ifndef QT_NO_WHEELEVENT +void TQScrollView::viewportWheelEvent( TQWheelEvent* 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. + */ + TQWheelEvent 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 TQScrollView::viewportContextMenuEvent( TQContextMenuEvent *e ) +{ + TQContextMenuEvent 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. +*/ +TQScrollBar* TQScrollView::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. +*/ +TQScrollBar* TQScrollView::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 TQScrollView::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 TQScrollView::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 TQScrollView::setContentsPos( int x, int y ) +{ +#if 0 + // bounds checking... + if ( TQApplication::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 TQScrollView::scrollBy( int dx, int dy ) +{ + setContentsPos( TQMAX( d->contentsX()+dx, 0 ), TQMAX( d->contentsY()+dy, 0 ) ); +} + +/*! + Scrolls the content so that the point \a (x, y) is in the center + of visible area. +*/ +void TQScrollView::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 TQScrollView::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 TQScrollView::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 TQScrollView::moveContents(int x, int y) +{ + if ( -x+visibleWidth() > d->contentsWidth() ) +#if 0 + if( TQApplication::reverseLayout() ) + x=TQMAX(0,-d->contentsWidth()+visibleWidth()); + else +#endif + x=TQMIN(0,-d->contentsWidth()+visibleWidth()); + if ( -y+visibleHeight() > contentsHeight() ) + y=TQMIN(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 ||*/ + ( TQABS(dy) * 5 > visibleHeight() * 4 ) || + ( TQABS(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 TQScrollView::contentsX + \brief the X coordinate of the contents that are at the left edge of + the viewport. +*/ +int TQScrollView::contentsX() const +{ + return d->contentsX(); +} + +/*! + \property TQScrollView::contentsY + \brief the Y coordinate of the contents that are at the top edge of + the viewport. +*/ +int TQScrollView::contentsY() const +{ + return d->contentsY(); +} + +/*! + \property TQScrollView::contentsWidth + \brief the width of the contents area +*/ +int TQScrollView::contentsWidth() const +{ + return d->contentsWidth(); +} + +/*! + \property TQScrollView::contentsHeight + \brief the height of the contents area +*/ +int TQScrollView::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 TQScrollView::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 TQScrollView::updateContents( int x, int y, int w, int h ) +{ + if ( testWState(WState_Visible|WState_BlockUpdates) != WState_Visible ) + return; + + TQWidget* vp = viewport(); + + // Translate + x -= d->contentsX(); + y -= d->contentsY(); + + // Clip to TQCOORD 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 TQScrollView::updateContents( const TQRect& r ) +{ + updateContents(r.x(), r.y(), r.width(), r.height()); +} + +/*! + \overload +*/ +void TQScrollView::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 TQScrollView::repaintContents( const TQRect& 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 TQScrollView::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 TQScrollView::repaintContents( int x, int y, int w, int h, bool erase ) +{ + if ( testWState(WState_Visible|WState_BlockUpdates) != WState_Visible ) + return; + + TQWidget* vp = viewport(); + + // Translate logical to clipper() + x -= d->contentsX(); + y -= d->contentsY(); + + // Clip to TQCOORD 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(TQPainter*,int,int,int,int). + + The default implementation translates the painter appropriately + and calls drawContents(TQPainter*,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 TQScrollView::drawContentsOffset(TQPainter* 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 TQScrollView::drawContents(TQPainter* 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 TQScrollView::drawContents(TQPainter*, int, int, int, int) +{ +} + + +/*! + \reimp +*/ +void TQScrollView::frameChanged() +{ + // slight ugle-hack - the listview header needs readjusting when + // changing the frame + if (TQListView *lv = ::qt_cast<TQListView *>(this)) + lv->triggerUpdate(); + TQFrame::frameChanged(); + updateScrollBars(); +} + + +/*! + Returns the viewport widget of the scrollview. This is the widget + containing the contents widget or which is the drawing area. +*/ +TQWidget* TQScrollView::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() +*/ +TQWidget* TQScrollView::clipper() const +{ + return d->viewport; +} + +/*! + \property TQScrollView::visibleWidth + \brief the horizontal amount of the content that is visible +*/ +int TQScrollView::visibleWidth() const +{ + return clipper()->width(); +} + +/*! + \property TQScrollView::visibleHeight + \brief the vertical amount of the content that is visible +*/ +int TQScrollView::visibleHeight() const +{ + return clipper()->height(); +} + + +void TQScrollView::changeFrameRect(const TQRect& r) +{ + TQRect oldr = frameRect(); + if (oldr != r) { + TQRect cr = contentsRect(); + TQRegion 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() ) + TQApplication::postEvent( this, new TQPaintEvent( 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 TQScrollView::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 TQScrollView::leftMargin() const +{ + return d->l_marg; +} + + +/*! + Returns the top margin. + + \sa setMargins() +*/ +int TQScrollView::topMargin() const +{ + return d->t_marg; +} + + +/*! + Returns the right margin. + + \sa setMargins() +*/ +int TQScrollView::rightMargin() const +{ + return d->r_marg; +} + + +/*! + Returns the bottom margin. + + \sa setMargins() +*/ +int TQScrollView::bottomMargin() const +{ + return d->b_marg; +} + +/*! + \reimp +*/ +bool TQScrollView::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 + TQFocusData *f = focusData(); + TQWidget *startingPoint = f->home(); + TQWidget *candidate = 0; + TQWidget *w = next ? f->next() : f->prev(); + TQSVChildRec *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 TQFrame::focusNextPrevChild( next ); + + // we've found one. + r = d->ancestorRec( candidate ); + if ( r && ( r->child == candidate || + candidate->isVisibleTo( r->child ) ) ) { + TQPoint cp = r->child->mapToGlobal(TQPoint(0,0)); + TQPoint cr = candidate->mapToGlobal(TQPoint(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 TQScrollView::enableClipper(bool y) +{ + if ( !d->clipped_viewport == !y ) + return; + if ( d->children.count() ) + qFatal("May only call TQScrollView::enableClipper() before adding widgets"); + if ( y ) { + d->clipped_viewport = new TQClipperWidget(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 tquite slow, as a full repaint of the + visible area has to be triggered on every contents move. + + \sa hasStaticBackground() +*/ +void TQScrollView::setStaticBackground(bool y) +{ + d->static_bg = y; +} + +/*! + Returns TRUE if TQScrollView uses a static background; otherwise + returns FALSE. + + \sa setStaticBackground() +*/ +bool TQScrollView::hasStaticBackground() const +{ + return d->static_bg; +} + +/*! + \overload + + Returns the point \a p translated to a point on the viewport() + widget. +*/ +TQPoint TQScrollView::contentsToViewport( const TQPoint& p ) const +{ + if ( d->clipped_viewport ) { + return TQPoint( p.x() - d->contentsX() - d->clipped_viewport->x(), + p.y() - d->contentsY() - d->clipped_viewport->y() ); + } else { + return TQPoint( p.x() - d->contentsX(), + p.y() - d->contentsY() ); + } +} + +/*! + \overload + + Returns the point on the viewport \a vp translated to a point in + the contents. +*/ +TQPoint TQScrollView::viewportToContents( const TQPoint& vp ) const +{ + if ( d->clipped_viewport ) { + return TQPoint( vp.x() + d->contentsX() + d->clipped_viewport->x(), + vp.y() + d->contentsY() + d->clipped_viewport->y() ); + } else { + return TQPoint( 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 TQScrollView::contentsToViewport( int x, int y, int& vx, int& vy ) const +{ + const TQPoint v = contentsToViewport(TQPoint(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 TQScrollView::viewportToContents( int vx, int vy, int& x, int& y ) const +{ + const TQPoint c = viewportToContents(TQPoint(vx,vy)); + x = c.x(); + y = c.y(); +} + +/*! + \reimp +*/ +TQSize TQScrollView::sizeHint() const +{ + if ( d->use_cached_size_hint && d->cachedSizeHint.isValid() ) + return d->cachedSizeHint; + + constPolish(); + int f = 2 * frameWidth(); + int h = fontMetrics().height(); + TQSize sz( f, f ); + if ( d->policy > Manual ) { + TQSVChildRec *r = d->children.first(); + if ( r ) { + TQSize cs = r->child->sizeHint(); + if ( cs.isValid() ) + sz += cs.boundedTo( r->child->maximumSize() ); + else + sz += r->child->size(); + } + } else { + sz += TQSize( 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( TQSize(12 * h, 8 * h) ) + .boundedTo( TQSize(36 * h, 24 * h) ); +} + + +/*! + \reimp +*/ +TQSize TQScrollView::minimumSizeHint() const +{ + int h = fontMetrics().height(); + if ( h < 10 ) + h = 10; + int f = 2 * frameWidth(); + return TQSize( (6 * h) + f, (4 * h) + f ); +} + + +/*! + \reimp + + (Implemented to get rid of a compiler warning.) +*/ +void TQScrollView::drawContents( TQPainter * ) +{ +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + \internal +*/ +void TQScrollView::startDragAutoScroll() +{ + if ( !d->autoscroll_timer.isActive() ) { + d->autoscroll_time = initialScrollTime; + d->autoscroll_accel = initialScrollAccel; + d->autoscroll_timer.start( d->autoscroll_time ); + } +} + + +/*! + \internal +*/ +void TQScrollView::stopDragAutoScroll() +{ + d->autoscroll_timer.stop(); +} + + +/*! + \internal +*/ +void TQScrollView::doDragAutoScroll() +{ + TQPoint p = d->viewport->mapFromGlobal( TQCursor::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 = TQMAX( 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 TQScrollView::dragAutoScroll + \brief whether autoscrolling in drag move events is enabled + + If this property is set to TRUE (the default), the TQScrollView + 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 TQScrollView, you might need to call TQDragEvent::ignore() on + the event in the dragEnterEvent() and dragMoveEvent() + reimplementations. +*/ + +void TQScrollView::setDragAutoScroll( bool b ) +{ + d->drag_autoscroll = b; +} + +bool TQScrollView::dragAutoScroll() const +{ + return d->drag_autoscroll; +} + +#endif // QT_NO_DRAGANDDROP + +/*!\internal + */ +void TQScrollView::setCachedSizeHint( const TQSize &sh ) const +{ + if ( isVisible() && !d->cachedSizeHint.isValid() ) + d->cachedSizeHint = sh; +} + +/*!\internal + */ +void TQScrollView::disableSizeHintCaching() +{ + d->use_cached_size_hint = FALSE; +} + +/*!\internal + */ +TQSize TQScrollView::cachedSizeHint() const +{ + return d->use_cached_size_hint ? d->cachedSizeHint : TQSize(); +} + +#endif // QT_NO_SCROLLVIEW diff --git a/src/widgets/qscrollview.h b/src/widgets/qscrollview.h new file mode 100644 index 000000000..80b9bbf1c --- /dev/null +++ b/src/widgets/qscrollview.h @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Definition of TQScrollView class +** +** Created : 970523 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ +#ifndef TQSCROLLVIEW_H +#define TQSCROLLVIEW_H + +#ifndef QT_H +#include "qframe.h" +#include "qscrollbar.h" +#endif // QT_H + +#ifndef QT_NO_SCROLLVIEW + +class TQScrollViewData; + +class Q_EXPORT TQScrollView : public TQFrame +{ + 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: + TQScrollView(TQWidget* parent=0, const char* name=0, WFlags f=0); + ~TQScrollView(); + + enum ResizePolicy { Default, Manual, AutoOne, AutoOneFit }; + virtual void setResizePolicy( ResizePolicy ); + ResizePolicy resizePolicy() const; + + void styleChange( TQStyle & ); + void removeChild(TQWidget* child); + virtual void addChild( TQWidget* child, int x=0, int y=0 ); + virtual void moveChild( TQWidget* child, int x, int y ); + int childX(TQWidget* child); + int childY(TQWidget* child); + bool childIsVisible(TQWidget* child) { return child->isVisible(); } // obsolete functions + void showChild(TQWidget* 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 ); + + TQWidget* cornerWidget() const; + virtual void setCornerWidget(TQWidget*); + + // ### 4.0: Consider providing a factory function for scrollbars + // (e.g. make the two following functions virtual) + TQScrollBar* horizontalScrollBar() const; + TQScrollBar* verticalScrollBar() const; + TQWidget* viewport() const; + TQWidget* 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 TQSize& ); + void show(); + + void updateContents( int x, int y, int w, int h ); + void updateContents( const TQRect& r ); + void updateContents(); + void repaintContents( int x, int y, int w, int h, bool erase=TRUE ); + void repaintContents( const TQRect& 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; + TQPoint contentsToViewport( const TQPoint& ) const; + TQPoint viewportToContents( const TQPoint& ) const; + void enableClipper( bool y ); + + void setStaticBackground( bool y ); + bool hasStaticBackground() const; + + TQSize viewportSize( int, int ) const; + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + + void removeChild(TQObject* 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(TQPainter*, int cx, int cy, int cw, int ch); + virtual void drawContentsOffset(TQPainter*, int ox, int oy, + int cx, int cy, int cw, int ch); + + + virtual void contentsMousePressEvent( TQMouseEvent* ); + virtual void contentsMouseReleaseEvent( TQMouseEvent* ); + virtual void contentsMouseDoubleClickEvent( TQMouseEvent* ); + virtual void contentsMouseMoveEvent( TQMouseEvent* ); +#ifndef QT_NO_DRAGANDDROP + virtual void contentsDragEnterEvent( TQDragEnterEvent * ); + virtual void contentsDragMoveEvent( TQDragMoveEvent * ); + virtual void contentsDragLeaveEvent( TQDragLeaveEvent * ); + virtual void contentsDropEvent( TQDropEvent * ); +#endif +#ifndef QT_NO_WHEELEVENT + virtual void contentsWheelEvent( TQWheelEvent * ); +#endif + virtual void contentsContextMenuEvent( TQContextMenuEvent * ); + + + virtual void viewportPaintEvent( TQPaintEvent* ); + virtual void viewportResizeEvent( TQResizeEvent* ); + virtual void viewportMousePressEvent( TQMouseEvent* ); + virtual void viewportMouseReleaseEvent( TQMouseEvent* ); + virtual void viewportMouseDoubleClickEvent( TQMouseEvent* ); + virtual void viewportMouseMoveEvent( TQMouseEvent* ); +#ifndef QT_NO_DRAGANDDROP + virtual void viewportDragEnterEvent( TQDragEnterEvent * ); + virtual void viewportDragMoveEvent( TQDragMoveEvent * ); + virtual void viewportDragLeaveEvent( TQDragLeaveEvent * ); + virtual void viewportDropEvent( TQDropEvent * ); +#endif +#ifndef QT_NO_WHEELEVENT + virtual void viewportWheelEvent( TQWheelEvent * ); +#endif + virtual void viewportContextMenuEvent( TQContextMenuEvent * ); + + 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(TQScrollBar& hbar, int x, int y, int w, int h); + virtual void setVBarGeometry(TQScrollBar& vbar, int x, int y, int w, int h); + + void resizeEvent(TQResizeEvent*); + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseDoubleClickEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( TQWheelEvent * ); +#endif + void contextMenuEvent( TQContextMenuEvent * ); + bool eventFilter( TQObject *, TQEvent *e ); + + void setCachedSizeHint( const TQSize &sh ) const; + TQSize cachedSizeHint() const; + void fontChange( const TQFont & ); + +private: + void drawContents( TQPainter* ); + void moveContents(int x, int y); + + TQScrollViewData* 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) + TQScrollView( const TQScrollView & ); + TQScrollView &operator=( const TQScrollView & ); +#endif + void changeFrameRect(const TQRect&); + +public: + void disableSizeHintCaching(); + +}; + +#endif // QT_NO_SCROLLVIEW + +#endif // TQSCROLLVIEW_H diff --git a/src/widgets/qslider.cpp b/src/widgets/qslider.cpp new file mode 100644 index 000000000..eaa18d5ee --- /dev/null +++ b/src/widgets/qslider.cpp @@ -0,0 +1,921 @@ +/**************************************************************************** +** +** Implementation of TQSlider class +** +** Created : 961019 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQSliderPrivate +{ + // ### move these to TQSlider in TQt 4.0 + int sliderStartVal; + TQSliderPrivate() : sliderStartVal( 0 ) { } +}; + + +/*! + \class TQSlider + \brief The TQSlider 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. + + TQSlider inherits TQRangeControl, which provides the "integer" side + of the slider. setRange() and value() are likely to be used by + practically all slider users; see the \l TQRangeControl + 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 TQScrollBar TQSpinBox + \link guibooks.html#fowler GUI Design Handbook: Slider\endlink +*/ + + +/*! + \enum TQSlider::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 TQWidget + constructor. +*/ + +TQSlider::TQSlider( TQWidget *parent, const char *name ) + : TQWidget( parent, name ) +{ + orient = Vertical; + init(); +} + +/*! + Constructs a slider. + + The \a orientation must be \l TQt::Vertical or \l TQt::Horizontal. + + The \a parent and \a name arguments are sent on to the TQWidget + constructor. +*/ + +TQSlider::TQSlider( Orientation orientation, TQWidget *parent, const char *name ) + : TQWidget( 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 TQt::Vertical the slider is vertical and if it + is \c TQt::Horizontal the slider is horizontal. + + The \a parent and \a name arguments are sent on to the TQWidget + constructor. +*/ + +TQSlider::TQSlider( int minValue, int maxValue, int pageStep, + int value, Orientation orientation, + TQWidget *parent, const char *name ) + : TQWidget( parent, name ), + TQRangeControl( minValue, maxValue, 1, pageStep, value ) +{ + orient = orientation; + init(); + sliderVal = value; +} + +/*! + Destructor. +*/ +TQSlider::~TQSlider() +{ + delete d; +} + +void TQSlider::init() +{ + d = new TQSliderPrivate; + timer = 0; + sliderPos = 0; + sliderVal = 0; + clickOffset = 0; + state = Idle; + track = TRUE; + ticks = NoMarks; + tickInt = 0; + setFocusPolicy( TabFocus ); + initTicks(); + + TQSizePolicy sp( TQSizePolicy::Expanding, TQSizePolicy::Fixed ); + if ( orient == Vertical ) + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); +} + + +/* + Does what's needed when someone changes the tickmark status. +*/ + +void TQSlider::initTicks() +{ + tickOffset = style().pixelMetric( TQStyle::PM_SliderTickmarkOffset, this ); +} + + +/*! + \property TQSlider::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 TQSlider::setTracking( bool enable ) +{ + track = enable; +} + + +/*! + \fn void TQSlider::valueChanged( int value ) + + This signal is emitted when the slider value is changed, with the + new slider \a value as its argument. +*/ + +/*! + \fn void TQSlider::sliderPressed() + + This signal is emitted when the user presses the slider with the + mouse. +*/ + +/*! + \fn void TQSlider::sliderMoved( int value ) + + This signal is emitted when the slider is dragged, with the new + slider \a value as its argument. +*/ + +/*! + \fn void TQSlider::sliderReleased() + + This signal is emitted when the user releases the slider with the mouse. +*/ + +/* + Calculates slider position corresponding to value \a v. +*/ + +int TQSlider::positionFromValue( int v ) const +{ + int a = available(); + int x = TQRangeControl::positionFromValue( v, a ); + if ( orient == Horizontal && TQApplication::reverseLayout() ) + x = a - x; + return x; +} + +/* + Returns the available space in which the slider can move. +*/ + +int TQSlider::available() const +{ + return style().pixelMetric( TQStyle::PM_SliderSpaceAvailable, this ); +} + +/* + Calculates a value corresponding to slider position \a p. +*/ + +int TQSlider::valueFromPosition( int p ) const +{ + int a = available(); + int x = TQRangeControl::valueFromPosition( p, a ); + if ( orient == Horizontal && TQApplication::reverseLayout() ) + x = maxValue() + minValue() - x; + return x; +} + +/*! + Implements the virtual TQRangeControl function. +*/ + +void TQSlider::rangeChange() +{ + int newPos = positionFromValue( value() ); + if ( newPos != sliderPos ) { + reallyMoveSlider( newPos ); + } +} + +/*! + Implements the virtual TQRangeControl function. +*/ + +void TQSlider::valueChange() +{ + if ( sliderVal != value() ) { + int newPos = positionFromValue( value() ); + sliderVal = value(); + reallyMoveSlider( newPos ); + } + emit valueChanged(value()); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif +} + + +/*! + \reimp +*/ +void TQSlider::resizeEvent( TQResizeEvent * ) +{ + rangeChange(); + initTicks(); +} + + +/*! + Reimplements the virtual function TQWidget::setPalette(). + + Sets the background color to the mid color for Motif style sliders + using palette \a p. +*/ + +void TQSlider::setPalette( const TQPalette &p ) +{ + TQWidget::setPalette( p ); +} + + + +/*! + \property TQSlider::orientation + \brief the slider's orientation + + The orientation must be \l TQt::Vertical (the default) or \l + TQt::Horizontal. +*/ + +void TQSlider::setOrientation( Orientation orientation ) +{ + if ( orientation == orient ) + return; + + if ( !testWState( WState_OwnSizePolicy ) ) { + TQSizePolicy sp = sizePolicy(); + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); + } + + orient = orientation; + + rangeChange(); + update(); +} + +/*! + \fn int TQSlider::sliderStart() const + + Returns the start position of the slider. +*/ + + +/*! + Returns the slider handle rectangle. (This is the visual marker + that the user can move.) +*/ + +TQRect TQSlider::sliderRect() const +{ + return style().querySubControlMetrics( TQStyle::CC_Slider, this, + TQStyle::SC_SliderHandle ); +} + +/* + Performs the actual moving of the slider. +*/ + +void TQSlider::reallyMoveSlider( int newPos ) +{ + TQRegion oldR(sliderRect()); + sliderPos = newPos; + TQRegion 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 TQSlider::paintEvent( TQPaintEvent * ) +{ + TQPainter p( this ); + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + + TQStyle::SCFlags sub = TQStyle::SC_SliderGroove | TQStyle::SC_SliderHandle; + if ( tickmarks() != NoMarks ) + sub |= TQStyle::SC_SliderTickmarks; + + style().drawComplexControl( TQStyle::CC_Slider, &p, this, rect(), colorGroup(), + flags, sub, state == Dragging ? TQStyle::SC_SliderHandle : TQStyle::SC_None ); +} + + +/*! + \reimp +*/ +void TQSlider::mousePressEvent( TQMouseEvent *e ) +{ + int slideLength = style().pixelMetric( TQStyle::PM_SliderLength, this ); + resetState(); + d->sliderStartVal = sliderVal; + TQRect r = sliderRect(); + + if ( e->button() == RightButton ) + return; + + if ( r.contains( e->pos() ) ) { + state = Dragging; + clickOffset = (TQCOORD)( 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 && TQApplication::reverseLayout() ) { + state = TimingUp; + addPage(); + } else { + state = TimingDown; + subtractPage(); + } + if ( !timer ) + timer = new TQTimer( 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 && TQApplication::reverseLayout() ) { + state = TimingDown; + subtractPage(); + } else { + state = TimingUp; + addPage(); + } + if ( !timer ) + timer = new TQTimer( this ); + connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) ); + timer->start( thresholdTime, TRUE ); + } + update( sliderRect() ); +} + +/*! + \reimp +*/ +void TQSlider::mouseMoveEvent( TQMouseEvent *e ) +{ + if ( state != Dragging ) + return; + + TQRect r = rect(); + int m = style().pixelMetric( TQStyle::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 TQSlider::wheelEvent( TQWheelEvent * e ) +{ + if ( e->orientation() != orientation() && !rect().contains(e->pos()) ) + return; + + static float offset = 0; + static TQSlider* offset_owner = 0; + if (offset_owner != this){ + offset_owner = this; + offset = 0; + } + offset += -e->delta()*TQMAX(pageStep(),lineStep())/120; + if (TQABS(offset)<1) + return; + setValue( value() + int(offset) ); + offset -= int(offset); + e->accept(); +} +#endif + +/*! + \reimp +*/ +void TQSlider::mouseReleaseEvent( TQMouseEvent * ) +{ + resetState(); + update( sliderRect() ); +} + +/*! + \reimp +*/ +void TQSlider::focusInEvent( TQFocusEvent * e) +{ + TQWidget::focusInEvent( e ); +} + +/*! + \reimp +*/ +void TQSlider::focusOutEvent( TQFocusEvent * e ) +{ + TQWidget::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 TQSlider::moveSlider( int pos ) +{ + int a = available(); + int newPos = TQMIN( a, TQMAX( 0, pos ) ); + int newVal = valueFromPosition( newPos ); + if (style().styleHint(TQStyle::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 TQSlider::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("TQSlider: (%s) in wrong state", name( "unnamed" ) ); + } + state = Idle; +} + + +/*! + \reimp +*/ +void TQSlider::keyPressEvent( TQKeyEvent *e ) +{ + bool sloppy = bool(style().styleHint(TQStyle::SH_Slider_SloppyKeyEvents, this)); + switch ( e->key() ) { + case Key_Left: + if ( sloppy || orient == Horizontal ) { + if (TQApplication::reverseLayout()) + addLine(); + else + subtractLine(); + } + break; + case Key_Right: + if ( sloppy || orient == Horizontal ) { + if (TQApplication::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 TQSlider::setValue( int value ) +{ + TQRangeControl::setValue( value ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif +} + + +/*! \reimp +*/ + +void TQSlider::addLine() +{ + TQRangeControl::addLine(); +} + +/*! \reimp +*/ + +void TQSlider::subtractLine() +{ + TQRangeControl::subtractLine(); +} + +/*! + Moves the slider one pageStep() up or right. +*/ + +void TQSlider::addStep() +{ + addPage(); +} + + +/*! + Moves the slider one pageStep() down or left. +*/ + +void TQSlider::subtractStep() +{ + subtractPage(); +} + + +/* + Waits for autorepeat. +*/ + +void TQSlider::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 TQSlider::goodPart( const TQPoint &p ) const +{ + return (orient == Horizontal) ? p.x() : p.y(); +} + +/*! + \reimp +*/ +TQSize TQSlider::sizeHint() const +{ + constPolish(); + const int length = 84, tickSpace = 5; + int thick = style().pixelMetric( TQStyle::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(TQStyle::CT_Slider, this, + TQSize(w, h)).expandedTo(TQApplication::globalStrut())); +} + + + +/*! + \reimp +*/ + +TQSize TQSlider::minimumSizeHint() const +{ + TQSize s = sizeHint(); + int length = style().pixelMetric(TQStyle::PM_SliderLength, this); + if ( orient == Horizontal ) + s.setWidth( length ); + else + s.setHeight( length ); + + return s; +} + +/*! \fn void TQSlider::setSizePolicy( TQSizePolicy::SizeType, TQSizePolicy::SizeType, bool ) + \reimp +*/ + +/*! \reimp */ +void TQSlider::setSizePolicy( TQSizePolicy sp ) +{ + // ## remove 4.0 + TQWidget::setSizePolicy( sp ); +} + +/*! \reimp */ +TQSizePolicy TQSlider::sizePolicy() const +{ + // ### 4.0 remove this reimplementation + return TQWidget::sizePolicy(); +} + +/*! + \property TQSlider::tickmarks + \brief the tickmark settings for this slider + + The valid values are in \l{TQSlider::TickSetting}. The default is + \c NoMarks. + + \sa tickInterval +*/ + +void TQSlider::setTickmarks( TickSetting s ) +{ + ticks = s; + initTicks(); + update(); +} + + +/*! + \property TQSlider::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 TQRangeControl::lineStep(), TQRangeControl::pageStep() +*/ + +void TQSlider::setTickInterval( int i ) +{ + tickInt = TQMAX( 0, i ); + update(); +} + + +/*! + \reimp +*/ +void TQSlider::styleChange( TQStyle& old ) +{ + TQWidget::styleChange( old ); +} + +/*! + \property TQSlider::minValue + \brief the current minimum value of the slider + + When setting this property, the \l TQSlider::maxValue is adjusted, + if necessary, to ensure that the range remains valid. + + \sa setRange() +*/ +int TQSlider::minValue() const +{ + return TQRangeControl::minValue(); +} + +/*! + \property TQSlider::maxValue + \brief the current maximum value of the slider + + When setting this property, the \l TQSlider::minValue is adjusted, + if necessary, to ensure that the range remains valid. + + \sa setRange() +*/ +int TQSlider::maxValue() const +{ + return TQRangeControl::maxValue(); +} + +void TQSlider::setMinValue( int minVal ) +{ + TQRangeControl::setMinValue( minVal ); +} + +void TQSlider::setMaxValue( int maxVal ) +{ + TQRangeControl::setMaxValue( maxVal ); +} + +/*! + \property TQSlider::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() TQRangeControl::pageStep() setRange() +*/ +int TQSlider::lineStep() const +{ + return TQRangeControl::lineStep(); +} + +/*! + \property TQSlider::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 TQRangeControl::setSteps() setLineStep() setRange() +*/ + +int TQSlider::pageStep() const +{ + return TQRangeControl::pageStep(); +} + +void TQSlider::setLineStep( int i ) +{ + setSteps( i, pageStep() ); +} + +void TQSlider::setPageStep( int i ) +{ + setSteps( lineStep(), i ); +} + +/*! + \property TQSlider::value + \brief the current slider value + + \sa TQRangeControl::value() prevValue() +*/ + +int TQSlider::value() const +{ + return TQRangeControl::value(); +} + +#endif diff --git a/src/widgets/qslider.h b/src/widgets/qslider.h new file mode 100644 index 000000000..7465a943b --- /dev/null +++ b/src/widgets/qslider.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Definition of TQSlider class +** +** Created : 961019 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSLIDER_H +#define TQSLIDER_H + +#ifndef QT_H +#include "qwidget.h" +#include "qrangecontrol.h" +#endif // QT_H + +#ifndef QT_NO_SLIDER + +struct TQSliderPrivate; + +class TQTimer; + +class Q_EXPORT TQSlider : public TQWidget, public TQRangeControl +{ + 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 }; + + TQSlider( TQWidget *parent, const char* name = 0 ); + TQSlider( Orientation, TQWidget *parent, const char* name = 0 ); + TQSlider( int minValue, int maxValue, int pageStep, int value, Orientation, + TQWidget *parent, const char* name = 0 ); + ~TQSlider(); + + virtual void setOrientation( Orientation ); + Orientation orientation() const; + virtual void setTracking( bool enable ); + bool tracking() const; + virtual void setPalette( const TQPalette & ); + + int sliderStart() const; + TQRect sliderRect() const; + TQSize sizeHint() const; + void setSizePolicy( TQSizePolicy sp ); + void setSizePolicy( TQSizePolicy::SizeType hor, TQSizePolicy::SizeType ver, bool hfw = FALSE ); + + TQSizePolicy sizePolicy() const; + TQSize 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( TQResizeEvent * ); + void paintEvent( TQPaintEvent * ); + + void keyPressEvent( TQKeyEvent * ); + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( TQWheelEvent * ); +#endif + void focusInEvent( TQFocusEvent *e ); + void focusOutEvent( TQFocusEvent *e ); + + void styleChange( TQStyle& ); + + 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 TQPoint& ) const; + void initTicks(); + + TQSliderPrivate *d; + TQTimer *timer; + TQCOORD sliderPos; + int sliderVal; + TQCOORD clickOffset; + State state; + bool track; + TQCOORD tickOffset; + TickSetting ticks; + int tickInt; + Orientation orient; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQSlider( const TQSlider & ); + TQSlider &operator=( const TQSlider & ); +#endif +}; + +inline bool TQSlider::tracking() const +{ + return track; +} + +inline TQSlider::Orientation TQSlider::orientation() const +{ + return orient; +} + +inline int TQSlider::sliderStart() const +{ + return sliderPos; +} + +inline void TQSlider::setSizePolicy( TQSizePolicy::SizeType hor, TQSizePolicy::SizeType ver, bool hfw ) +{ + TQWidget::setSizePolicy( hor, ver, hfw ); +} + +#endif // QT_NO_SLIDER + +#endif // TQSLIDER_H diff --git a/src/widgets/qspinbox.cpp b/src/widgets/qspinbox.cpp new file mode 100644 index 000000000..8bed440fe --- /dev/null +++ b/src/widgets/qspinbox.cpp @@ -0,0 +1,1116 @@ +/**************************************************************************** +** +** Implementation of TQSpinBox widget class +** +** Created : 970101 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQSpinBoxPrivate +{ +public: + TQSpinBoxPrivate() {} + TQSpinWidget* controls; + uint selreq : 1; +}; + +class TQSpinBoxValidator : public TQIntValidator +{ +public: + TQSpinBoxValidator( TQSpinBox *sb, const char *name ) + : TQIntValidator( sb, name ), spinBox( sb ) { } + + virtual State validate( TQString& str, int& pos ) const; + +private: + TQSpinBox *spinBox; +}; + +TQValidator::State TQSpinBoxValidator::validate( TQString& str, int& pos ) const +{ + TQString pref = spinBox->prefix(); + TQString suff = spinBox->suffix(); + TQString suffStriped = suff.stripWhiteSpace(); + uint overhead = pref.length() + suff.length(); + State state = Invalid; + + ((TQIntValidator *) this)->setRange( spinBox->minValue(), + spinBox->maxValue() ); + if ( overhead == 0 ) { + state = TQIntValidator::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(); + TQString core = str.mid( pref.length(), str.length() - overhead ); + int corePos = pos - pref.length(); + state = TQIntValidator::validate( core, corePos ); + pos = corePos + pref.length(); + str.replace( pref.length(), str.length() - overhead, core ); + } else { + state = TQIntValidator::validate( str, pos ); + if ( state == Invalid ) { + // stripWhiteSpace(), cf. TQSpinBox::interpretText() + TQString special = spinBox->specialValueText().stripWhiteSpace(); + TQString candidate = str.stripWhiteSpace(); + + if ( special.startsWith(candidate) ) { + if ( candidate.length() == special.length() ) { + state = Acceptable; + } else { + state = Intermediate; + } + } + } + } + } + return state; +} + +/*! + \class TQSpinBox + \brief The TQSpinBox class provides a spin box widget (spin button). + + \ingroup basic + \mainclass + + TQSpinBox 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 TQSpinBox 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 TQRangeControl 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 TQSpinBox 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 TQSpinBox. + + The default \l TQWidget::focusPolicy() is StrongFocus. + + If using prefix(), suffix() and specialValueText() don't provide + enough control, you can ignore them and subclass TQSpinBox instead. + + TQSpinBox 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 TQSpinBox + { + Q_OBJECT + public: + ... + + TQString mapValueToText( int value ) + { + if ( value == -1 ) // special case + return TQString( "Auto" ); + + return TQString( "%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 TQScrollBar TQSlider + \link guibooks.html#fowler GUI Design Handbook: Spin Box \endlink +*/ + + +/*! + Constructs a spin box with the default TQRangeControl range and + step values. It is called \a name and has parent \a parent. + + \sa minValue(), maxValue(), setRange(), lineStep(), setSteps() +*/ + +TQSpinBox::TQSpinBox( TQWidget * parent , const char *name ) + : TQWidget( parent, name, WNoAutoErase ), + TQRangeControl() +{ + 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() +*/ + +TQSpinBox::TQSpinBox( int minValue, int maxValue, int step, TQWidget* parent, + const char* name ) + : TQWidget( parent, name, WNoAutoErase ), + TQRangeControl( minValue, maxValue, step, step, minValue ) +{ + initSpinBox(); +} + +/* + \internal Initialization. +*/ + +void TQSpinBox::initSpinBox() +{ + d = new TQSpinBoxPrivate; + + d->controls = new TQSpinWidget( 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 TQSpinBoxValidator( this, "validator" ); + vi = new TQLineEdit( this, "qt_spinbox_edit" ); + d->controls->setEditWidget( vi ); + vi->setValidator( validate ); + vi->installEventFilter( this ); + vi->setFrame( FALSE ); + setFocusProxy( vi ); + + setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ) ); + setBackgroundMode( PaletteBackground, PaletteBase ); + + updateDisplay(); + + connect( vi, SIGNAL(textChanged(const TQString&)), SLOT(textChanged()) ); +} + +/*! + Destroys the spin box, freeing all memory and other resources. +*/ + +TQSpinBox::~TQSpinBox() +{ + delete d; +} + + +/*! + \property TQSpinBox::text + \brief the spin box's text, including any prefix() and suffix() + + There is no default text. + + \sa value() +*/ + +TQString TQSpinBox::text() const +{ + return vi->text(); +} + + + +/*! + \property TQSpinBox::cleanText + \brief the spin box's text with no prefix(), suffix() or leading + or trailing whitespace. + + \sa text, prefix, suffix +*/ + +TQString TQSpinBox::cleanText() const +{ + TQString s = TQString(text()).stripWhiteSpace(); + if ( !prefix().isEmpty() ) { + TQString px = TQString(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() ) { + TQString sx = TQString(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 TQSpinBox::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 + TQSpinBox 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 + TQString::null. +*/ + +void TQSpinBox::setSpecialValueText( const TQString &text ) +{ + specText = text; + updateDisplay(); +} + + +TQString TQSpinBox::specialValueText() const +{ + if ( specText.isEmpty() ) + return TQString::null; + else + return specText; +} + + +/*! + \property TQSpinBox::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 TQString::null. + + \sa suffix() +*/ + +void TQSpinBox::setPrefix( const TQString &text ) +{ + pfix = text; + updateDisplay(); +} + + +TQString TQSpinBox::prefix() const +{ + if ( pfix.isEmpty() ) + return TQString::null; + else + return pfix; +} + + +/*! + \property TQSpinBox::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 TQString::null. + + \sa prefix() +*/ + +void TQSpinBox::setSuffix( const TQString &text ) +{ + sfix = text; + updateDisplay(); +} + +TQString TQSpinBox::suffix() const +{ + if ( sfix.isEmpty() ) + return TQString::null; + else + return sfix; +} + + +/*! + \property TQSpinBox::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 TQSpinBox::setWrapping( bool on ) +{ + wrap = on; + updateDisplay(); +} + +bool TQSpinBox::wrapping() const +{ + return wrap; +} + +/*! + \reimp +*/ +TQSize TQSpinBox::sizeHint() const +{ + constPolish(); + TQSize sz = vi->sizeHint(); + int h = sz.height(); + TQFontMetrics fm( font() ); + int w = 35; + int wx = fm.width( ' ' )*2; + TQString s; + s = prefix() + ( (TQSpinBox*)this )->mapValueToText( minValue() ) + suffix(); + w = TQMAX( w, fm.width( s ) + wx); + s = prefix() + ( (TQSpinBox*)this )->mapValueToText( maxValue() ) + suffix(); + w = TQMAX(w, fm.width( s ) + wx ); + if ( !specialValueText().isEmpty() ) { + s = specialValueText(); + w = TQMAX( w, fm.width( s ) + wx ); + } + return style().sizeFromContents(TQStyle::CT_SpinBox, this, + TQSize( w + d->controls->downRect().width(), + h + style().pixelMetric( TQStyle::PM_DefaultFrameWidth ) * 2). + expandedTo( TQApplication::globalStrut() )); +} + + +/*! + \reimp +*/ +TQSize TQSpinBox::minimumSizeHint() const +{ + int w = vi->minimumSizeHint().width() + d->controls->downRect().width(); + int h = TQMAX( vi->minimumSizeHint().height(), d->controls->minimumSizeHint().height() ); + return TQSize( w, h ); +} + +// Does the layout of the lineedit and the buttons + +void TQSpinBox::arrangeWidgets() +{ + d->controls->arrange(); +} + +/*! + \property TQSpinBox::value + \brief the value of the spin box + + \sa TQRangeControl::setValue() +*/ + +void TQSpinBox::setValue( int value ) +{ + edited = FALSE; // we ignore anything entered and not yet interpreted + TQRangeControl::setValue( value ); + updateDisplay(); +} + +int TQSpinBox::value() const +{ + TQSpinBox * that = (TQSpinBox *) this; + if ( edited ) { + that->edited = FALSE; // avoid recursion + that->interpretText(); + } + return TQRangeControl::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 TQSpinBox::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 TQSpinBox::stepDown() +{ + if ( edited ) + interpretText(); + if ( wrapping() && ( value()-lineStep() < minValue() || sumOutOfRange(value(), -lineStep() ) ) ) { + setValue( maxValue() ); + } else { + subtractLine(); + } +} + + +/*! + \fn void TQSpinBox::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 TQSpinBox::valueChanged( const TQString& 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 TQLineEdit + that have special meaning for the TQSpinBox. The object is passed + as \a o and the event is passed as \a ev. +*/ + +bool TQSpinBox::eventFilter( TQObject* o, TQEvent* ev ) +{ + if (o != vi) + return TQWidget::eventFilter(o,ev); + + if ( ev->type() == TQEvent::KeyPress ) { + TQKeyEvent* k = (TQKeyEvent*)ev; + + bool retval = FALSE; // workaround for MSVC++ optimization bug + if( (k->key() == Key_Tab) || (k->key() == Key_BackTab) ){ + if ( k->state() & TQt::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() == TQEvent::FocusOut || ev->type() == TQEvent::Hide ) { + if ( edited ) { + interpretText(); + } + return FALSE; + } + return FALSE; +} + +/*! + \reimp + */ +void TQSpinBox::setEnabled( bool enabled ) +{ + TQWidget::setEnabled( enabled ); + updateDisplay(); +} + +/*! + \reimp +*/ +void TQSpinBox::leaveEvent( TQEvent* ) +{ +} + + +/*! + \reimp +*/ +void TQSpinBox::resizeEvent( TQResizeEvent* ) +{ + d->controls->resize( width(), height() ); +} + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void TQSpinBox::wheelEvent( TQWheelEvent * e ) +{ + e->accept(); + static float offset = 0; + static TQSpinBox* offset_owner = 0; + if (offset_owner != this) { + offset_owner = this; + offset = 0; + } + offset += -e->delta()/120; + if (TQABS(offset) < 1) + return; + int ioff = int(offset); + int i; + for (i=0; i<TQABS(ioff); i++) + offset > 0 ? stepDown() : stepUp(); + offset -= ioff; +} +#endif + +/*! + This virtual function is called by TQRangeControl whenever the + value has changed. The TQSpinBox 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 TQSpinBox::valueChange() +{ + d->selreq = hasFocus(); + updateDisplay(); + d->selreq = FALSE; + emit valueChanged( value() ); + emit valueChanged( currentValueText() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ValueChanged ); +#endif +} + + +/*! + This virtual function is called by TQRangeControl 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 TQSpinBox::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 TQIntValidator. + + Use setValidator(0) to turn off input validation (entered input + will still be kept within the spin box's range). +*/ + +void TQSpinBox::setValidator( const TQValidator* 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() TQValidator +*/ + +const TQValidator * TQSpinBox::validator() const +{ + return vi ? vi->validator() : 0; +} + +/*! + Updates the contents of the embedded TQLineEdit to reflect the + current value using mapValueToText(). Also enables/disables the + up/down push buttons accordingly. + + \sa mapValueToText() +*/ +void TQSpinBox::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 ); +} + + +/*! + TQSpinBox calls this after the user has manually edited the + contents of the spin box (i.e. by typing in the embedded + TQLineEdit, 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 TQSpinBox::interpretText() +{ + bool ok = TRUE; + bool done = FALSE; + int newVal = 0; + if ( !specialValueText().isEmpty() ) { + TQString s = text().stripWhiteSpace(); + TQString 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. +*/ + +TQRect TQSpinBox::upRect() const +{ + return d->controls->upRect(); +} + + +/*! + Returns the geometry of the "down" button. +*/ + +TQRect TQSpinBox::downRect() const +{ + return d->controls->downRect(); +} + + +/*! + Returns a pointer to the embedded TQLineEdit. +*/ + +TQLineEdit* TQSpinBox::editor() const +{ + return vi; +} + + +/*! + This slot is called whenever the user edits the spin box's text. +*/ + +void TQSpinBox::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 TQt 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() +*/ + +TQString TQSpinBox::mapValueToText( int v ) +{ + TQString 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 TQt 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 TQSpinBox::mapTextToValue( bool* ok ) +{ + TQString 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. +*/ + +TQString TQSpinBox::currentValueText() +{ + TQString s; + if ( (value() == minValue()) && !specialValueText().isEmpty() ) { + s = specialValueText(); + } else { + s = prefix(); + s.append( mapValueToText( value() ) ); + s.append( suffix() ); + } + return s; +} + +/*! + \reimp +*/ + +void TQSpinBox::styleChange( TQStyle& old ) +{ + arrangeWidgets(); + TQWidget::styleChange( old ); +} + + +/*! + \enum TQSpinBox::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 TQSpinBox::buttonSymbols +*/ + +/*! + \property TQSpinBox::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 TQSpinBox::setButtonSymbols( ButtonSymbols newSymbols ) +{ + if ( buttonSymbols() == newSymbols ) + return; + + switch ( newSymbols ) { + case UpDownArrows: + d->controls->setButtonSymbols( TQSpinWidget::UpDownArrows ); + break; + case PlusMinus: + d->controls->setButtonSymbols( TQSpinWidget::PlusMinus ); + break; + } + // repaint( FALSE ); +} + +TQSpinBox::ButtonSymbols TQSpinBox::buttonSymbols() const +{ + switch( d->controls->buttonSymbols() ) { + case TQSpinWidget::UpDownArrows: + return UpDownArrows; + case TQSpinWidget::PlusMinus: + return PlusMinus; + } + return UpDownArrows; +} + +/*! + \property TQSpinBox::minValue + + \brief the minimum value of the spin box + + When setting this property, \l TQSpinBox::maxValue is adjusted, if + necessary, to ensure that the range remains valid. + + \sa setRange() setSpecialValueText() +*/ + +int TQSpinBox::minValue() const +{ + return TQRangeControl::minValue(); +} + +void TQSpinBox::setMinValue( int minVal ) +{ + TQRangeControl::setMinValue( minVal ); +} + +/*! + \property TQSpinBox::maxValue + \brief the maximum value of the spin box + + When setting this property, \l TQSpinBox::minValue is adjusted, if + necessary, to ensure that the range remains valid. + + \sa setRange() setSpecialValueText() +*/ + +int TQSpinBox::maxValue() const +{ + return TQRangeControl::maxValue(); +} + +void TQSpinBox::setMaxValue( int maxVal ) +{ + TQRangeControl::setMaxValue( maxVal ); +} + +/*! + \property TQSpinBox::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 TQRangeControl::setSteps() setRange() +*/ + +int TQSpinBox::lineStep() const +{ + return TQRangeControl::lineStep(); +} + +void TQSpinBox::setLineStep( int i ) +{ + setSteps( i, pageStep() ); +} + +/*! + Selects all the text in the spin box's editor. +*/ + +void TQSpinBox::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 000000000..9faa83578 --- /dev/null +++ b/src/widgets/qspinbox.h @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Definition of TQSpinBox widget class +** +** Created : 970101 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSPINBOX_H +#define TQSPINBOX_H + +#ifndef QT_H +#include "qwidget.h" +#include "qrangecontrol.h" +#endif // QT_H + +#ifndef QT_NO_SPINBOX + +class TQLineEdit; +class TQValidator; +class TQSpinBoxPrivate; + +class Q_EXPORT TQSpinBox: public TQWidget, public TQRangeControl +{ + Q_OBJECT + Q_ENUMS( ButtonSymbols ) + Q_PROPERTY( TQString text READ text ) + Q_PROPERTY( TQString prefix READ prefix WRITE setPrefix ) + Q_PROPERTY( TQString suffix READ suffix WRITE setSuffix ) + Q_PROPERTY( TQString cleanText READ cleanText ) + Q_PROPERTY( TQString 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: + TQSpinBox( TQWidget* parent=0, const char* name=0 ); + TQSpinBox( int minValue, int maxValue, int step = 1, + TQWidget* parent=0, const char* name=0 ); + ~TQSpinBox(); + + TQString text() const; + + virtual TQString prefix() const; + virtual TQString suffix() const; + virtual TQString cleanText() const; + + virtual void setSpecialValueText( const TQString &text ); + TQString 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 TQValidator* v ); + const TQValidator * validator() const; + + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + + int minValue() const; + int maxValue() const; + void setMinValue( int ); + void setMaxValue( int ); + int lineStep() const; + void setLineStep( int ); + int value() const; + + TQRect upRect() const; + TQRect downRect() const; + +public slots: + virtual void setValue( int value ); + virtual void setPrefix( const TQString &text ); + virtual void setSuffix( const TQString &text ); + virtual void stepUp(); + virtual void stepDown(); + virtual void setEnabled( bool enabled ); + virtual void selectAll(); + +signals: + void valueChanged( int value ); + void valueChanged( const TQString &valueText ); + +protected: + virtual TQString mapValueToText( int value ); + virtual int mapTextToValue( bool* ok ); + TQString currentValueText(); + + virtual void updateDisplay(); + virtual void interpretText(); + + TQLineEdit* editor() const; + + virtual void valueChange(); + virtual void rangeChange(); + + bool eventFilter( TQObject* obj, TQEvent* ev ); + void resizeEvent( TQResizeEvent* ev ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( TQWheelEvent * ); +#endif + void leaveEvent( TQEvent* ); + + void styleChange( TQStyle& ); + +protected slots: + void textChanged(); + +private: + void initSpinBox(); + TQSpinBoxPrivate* d; + TQLineEdit* vi; + TQValidator* validate; + TQString pfix; + TQString sfix; + TQString specText; + + uint wrap : 1; + uint edited : 1; + + void arrangeWidgets(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQSpinBox( const TQSpinBox& ); + TQSpinBox& operator=( const TQSpinBox& ); +#endif + +}; + +#endif // QT_NO_SPINBOX + +#endif // TQSPINBOX_H diff --git a/src/widgets/qspinwidget.cpp b/src/widgets/qspinwidget.cpp new file mode 100644 index 000000000..cf9608616 --- /dev/null +++ b/src/widgets/qspinwidget.cpp @@ -0,0 +1,465 @@ +/**************************************************************************** +** +** Implementation of TQSpinWidget class +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qrangecontrol.h" + +#ifndef QT_NO_SPINWIDGET + +#include "qrect.h" +#include "qtimer.h" +#include "qstyle.h" +#include "qpainter.h" + +class TQSpinWidgetPrivate +{ +public: + TQSpinWidgetPrivate() + : upEnabled( TRUE ), + downEnabled( TRUE ), + theButton( 0 ), + buttonDown( 0 ), + timerUp( 0 ), + bsyms( TQSpinWidget::UpDownArrows ), + ed ( 0 ) {} + uint upEnabled :1; + uint downEnabled :1; + uint theButton :2; + uint buttonDown :2; + uint timerUp : 1; + TQRect up; + TQRect down; + TQTimer auRepTimer; + TQSpinWidget::ButtonSymbols bsyms; + TQWidget *ed; + void startTimer( int msec ) { auRepTimer.start( msec, TRUE ); } + void startTimer( bool up, int msec ) { timerUp = up; startTimer( msec ); } + void stopTimer() { auRepTimer.stop(); } +}; + +/*! + + \class TQSpinWidget qspinwidget.h + \brief The TQSpinWidget class is an internal range control related class. + + \internal + + Constructs an empty range control widget with parent \a parent + called \a name. + +*/ + +TQSpinWidget::TQSpinWidget( TQWidget* parent, const char* name ) + : TQWidget( parent, name ) +{ + d = new TQSpinWidgetPrivate(); + connect( &d->auRepTimer, SIGNAL( timeout() ), this, SLOT( timerDone() ) ); + setFocusPolicy( StrongFocus ); + + arrange(); + updateDisplay(); +} + + +/*! Destroys the object and frees any allocated resources. + +*/ + +TQSpinWidget::~TQSpinWidget() +{ + delete d; +} + +/*! */ +TQWidget * TQSpinWidget::editWidget() +{ + return d->ed; +} + +/*! + Sets the editing widget to \a w. +*/ +void TQSpinWidget::setEditWidget( TQWidget * w ) +{ + if ( w ) { + if (w->parentWidget() != this) + w->reparent( this, TQPoint( 0, 0 ) ); + setFocusProxy( w ); + } + d->ed = w; + arrange(); + updateDisplay(); +} + +/*! \reimp + +*/ + +void TQSpinWidget::mousePressEvent( TQMouseEvent *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 TQSpinWidget::arrange() +{ + d->up = TQStyle::visualRect( style().querySubControlMetrics( TQStyle::CC_SpinWidget, this, + TQStyle::SC_SpinWidgetUp ), this ); + d->down = TQStyle::visualRect( style().querySubControlMetrics( TQStyle::CC_SpinWidget, this, + TQStyle::SC_SpinWidgetDown ), this ); + if ( d->ed ) { + TQRect r = TQStyle::visualRect( style().querySubControlMetrics( TQStyle::CC_SpinWidget, this, + TQStyle::SC_SpinWidgetEditField ), this ); + d->ed->setGeometry( r ); + } +} + +/*! + +*/ + +void TQSpinWidget::stepUp() +{ + emit stepUpPressed(); +} + +void TQSpinWidget::resizeEvent( TQResizeEvent* ) +{ + arrange(); +} + +/*! + +*/ + +void TQSpinWidget::stepDown() +{ + emit stepDownPressed(); +} + + +void TQSpinWidget::timerDone() +{ + // we use a double timer to make it possible for users to do + // something with 0-timer on valueChanged. + TQTimer::singleShot( 1, this, SLOT( timerDoneEx() ) ); +} + +void TQSpinWidget::timerDoneEx() +{ + if ( !d->buttonDown ) + return; + if ( d->timerUp ) + stepUp(); + else + stepDown(); + d->startTimer( 100 ); +} + + +void TQSpinWidget::windowActivationChange( bool oldActive ) +{ + //was active, but lost focus + if ( oldActive && d->buttonDown ) { + d->stopTimer(); + d->buttonDown = 0; + d->theButton = 0; + } + TQWidget::windowActivationChange( oldActive ); +} + + + +/*! + The event is passed in \a e. +*/ + +void TQSpinWidget::mouseReleaseEvent( TQMouseEvent *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 TQSpinWidget::mouseMoveEvent( TQMouseEvent *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 TQSpinWidget::wheelEvent( TQWheelEvent *e ) +{ + e->accept(); + static float offset = 0; + static TQSpinWidget* offset_owner = 0; + if ( offset_owner != this ) { + offset_owner = this; + offset = 0; + } + offset += -e->delta()/120; + if ( TQABS( offset ) < 1 ) + return; + int ioff = int(offset); + int i; + for( i=0; i < TQABS( ioff ); i++ ) + offset > 0 ? stepDown() : stepUp(); + offset -= ioff; +} +#endif + +/*! + +*/ +void TQSpinWidget::paintEvent( TQPaintEvent * ) +{ + TQPainter p( this ); + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus() || focusProxy() && focusProxy()->hasFocus()) + flags |= TQStyle::Style_HasFocus; + + TQStyle::SCFlags active; + if ( d->theButton & 1 ) + active = TQStyle::SC_SpinWidgetDown; + else if ( d->theButton & 2 ) + active = TQStyle::SC_SpinWidgetUp; + else + active = TQStyle::SC_None; + + TQRect fr = TQStyle::visualRect( + style().querySubControlMetrics( TQStyle::CC_SpinWidget, this, + TQStyle::SC_SpinWidgetFrame ), this ); + style().drawComplexControl( TQStyle::CC_SpinWidget, &p, this, + fr, colorGroup(), + flags, + (uint)TQStyle::SC_All, + active ); +} + + +/*! + The previous style is passed in \a old. +*/ + +void TQSpinWidget::styleChange( TQStyle& old ) +{ + arrange(); + TQWidget::styleChange( old ); +} + +/*! +*/ + +TQRect TQSpinWidget::upRect() const +{ + return d->up; +} + +/*! +*/ + +TQRect TQSpinWidget::downRect() const +{ + return d->down; +} + +/*! +*/ + +void TQSpinWidget::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 TQSpinWidget::enableChanged( bool ) +{ + d->upEnabled = isEnabled(); + d->downEnabled = isEnabled(); + updateDisplay(); +} + + +/*! + Sets up-enabled to \a on. +*/ + +void TQSpinWidget::setUpEnabled( bool on ) +{ + if ( (bool)d->upEnabled != on ) { + d->upEnabled = on; + updateDisplay(); + } +} + +/*! +*/ + +bool TQSpinWidget::isUpEnabled() const +{ + return d->upEnabled; +} + +/*! + Sets down-enabled to \a on. +*/ + +void TQSpinWidget::setDownEnabled( bool on ) +{ + if ( (bool)d->downEnabled != on ) { + d->downEnabled = on; + updateDisplay(); + } +} + +/*! +*/ + +bool TQSpinWidget::isDownEnabled() const +{ + return d->downEnabled; +} + +/*! + Sets the button symbol to \a bs. +*/ + +void TQSpinWidget::setButtonSymbols( ButtonSymbols bs ) +{ + d->bsyms = bs; +} + +/*! +*/ + +TQSpinWidget::ButtonSymbols TQSpinWidget::buttonSymbols() const +{ + return d->bsyms; +} + +#endif diff --git a/src/widgets/qsplashscreen.cpp b/src/widgets/qsplashscreen.cpp new file mode 100644 index 000000000..ab528a7a3 --- /dev/null +++ b/src/widgets/qsplashscreen.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Definition of TQSplashScreen class +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsplashscreen.h" + +#ifndef QT_NO_SPLASHSCREEN + +#include "qapplication.h" +#include "qpainter.h" +#include "qpixmap.h" + +class TQSplashScreenPrivate +{ +public: + TQPixmap pixmap; + TQString currStatus; + TQColor currColor; + int currAlign; +}; + +/*! + \class TQSplashScreen qsplashscreen.h + \brief The TQSplashScreen 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 ) + { + TQApplication app( argc, argv ); + TQPixmap pixmap( "splash.png" ); + TQSplashScreen *splash = new TQSplashScreen( pixmap ); + splash->show(); + TQMainWindow *mainWin = new TQMainWindow; + ... + 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. TQSplashScreen 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 TQSplashScreen 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 TQApplication::processEvents() to receive the mouse clicks. + + \code + TQPixmap pixmap( "splash.png" ); + TQSplashScreen *splash = new TQSplashScreen( 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. +*/ +TQSplashScreen::TQSplashScreen( const TQPixmap &pixmap, WFlags f ) + : TQWidget( 0, 0, WStyle_Customize | WStyle_Splash | f ) +{ + d = new TQSplashScreenPrivate(); + d->pixmap = pixmap; + setPixmap( d->pixmap ); // Does an implicit repaint +} + +/*! + Destructor. +*/ +TQSplashScreen::~TQSplashScreen() +{ + delete d; +} + +/*! + \reimp +*/ +void TQSplashScreen::mousePressEvent( TQMouseEvent * ) +{ + hide(); +} + +/*! + This overrides TQWidget::repaint(). It differs from the standard + repaint function in that it also calls TQApplication::flush() to + ensure the updates are displayed, even when there is no event loop + present. +*/ +void TQSplashScreen::repaint() +{ + drawContents(); + TQWidget::repaint(); + TQApplication::flush(); +} + +/*! + \fn TQSplashScreen::messageChanged( const TQString &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 TQt::AlignmentFlags clear() +*/ +void TQSplashScreen::message( const TQString &message, int alignment, + const TQColor &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 TQSplashScreen::clear() +{ + d->currStatus = TQString::null; + emit messageChanged( d->currStatus ); + repaint(); +} + +/*! + Makes the splash screen wait until the widget \a mainWin is displayed + before calling close() on itself. +*/ +void TQSplashScreen::finish( TQWidget *mainWin ) +{ + if ( mainWin ) { +#if defined(Q_WS_X11) + extern void qt_wait_for_window_manager( TQWidget *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 TQSplashScreen::setPixmap( const TQPixmap &pixmap ) +{ + d->pixmap = pixmap; + TQRect r(0, 0, d->pixmap.size().width(), d->pixmap.size().height()); + resize( d->pixmap.size() ); + move( TQApplication::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. +*/ +TQPixmap* TQSplashScreen::pixmap() const +{ + return &( d->pixmap ); +} + +/*! + \internal +*/ +void TQSplashScreen::drawContents() +{ + TQPixmap textPix = d->pixmap; + TQPainter 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 TQSplashScreen::drawContents( TQPainter *painter ) +{ + painter->setPen( d->currColor ); + TQRect 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 000000000..628c5d35d --- /dev/null +++ b/src/widgets/qsplashscreen.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Definition of TQSplashScreen class +** +** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSPLASHSCREEN_H +#define TQSPLASHSCREEN_H + +#ifndef QT_H +#include "qpixmap.h" +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_SPLASHSCREEN +class TQSplashScreenPrivate; + +class Q_EXPORT TQSplashScreen : public TQWidget +{ + Q_OBJECT +public: + TQSplashScreen( const TQPixmap &pixmap = TQPixmap(), WFlags f = 0 ); + virtual ~TQSplashScreen(); + + void setPixmap( const TQPixmap &pixmap ); + TQPixmap* pixmap() const; + void finish( TQWidget *w ); + void repaint(); + +public slots: + void message( const TQString &str, int flags = AlignLeft, + const TQColor &color = black ); + void clear(); + +signals: + void messageChanged( const TQString &str ); + +protected: + virtual void drawContents( TQPainter *painter ); + void mousePressEvent( TQMouseEvent * ); + +private: + void drawContents(); + + TQSplashScreenPrivate *d; +}; +#endif //QT_NO_SPLASHSCREEN +#endif diff --git a/src/widgets/qsplitter.cpp b/src/widgets/qsplitter.cpp new file mode 100644 index 000000000..90c21b009 --- /dev/null +++ b/src/widgets/qsplitter.cpp @@ -0,0 +1,1368 @@ +/**************************************************************************** +** +** Implementation of TQSplitter class +** +** Created : 980105 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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" + +const uint Default = QT_QSPLITTER_DEFAULT; + +static int mouseOffset; +static int opaqueOldPos = -1; // this assumes that there's only one mouse + +static TQPoint toggle( TQWidget *w, TQPoint pos ) +{ + TQSize minS = qSmartMinSize( w ); + return -pos - TQPoint( minS.width(), minS.height() ); +} + +static bool isCollapsed( TQWidget *w ) +{ + return w->x() < 0 || w->y() < 0; +} + +static TQPoint topLeft( TQWidget *w ) +{ + if ( isCollapsed(w) ) { + return toggle( w, w->pos() ); + } else { + return w->pos(); + } +} + +static TQPoint bottomRight( TQWidget *w ) +{ + if ( isCollapsed(w) ) { + return toggle( w, w->pos() ) - TQPoint( 1, 1 ); + } else { + return w->geometry().bottomRight(); + } +} + +TQSplitterHandle::TQSplitterHandle( Orientation o, TQSplitter *parent, + const char * name ) + : TQWidget( parent, name ) +{ + s = parent; + setOrientation( o ); +} + +TQSize TQSplitterHandle::sizeHint() const +{ + int hw = s->handleWidth(); + return parentWidget()->style().sizeFromContents( TQStyle::CT_Splitter, s, + TQSize(hw, hw) ) + .expandedTo( TQApplication::globalStrut() ); +} + +void TQSplitterHandle::setOrientation( Orientation o ) +{ + orient = o; +#ifndef QT_NO_CURSOR + setCursor( o == TQSplitter::Horizontal ? splitHCursor : splitVCursor ); +#endif +} + +void TQSplitterHandle::mouseMoveEvent( TQMouseEvent *e ) +{ + if ( !(e->state()&LeftButton) ) + return; + TQCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) ) + - mouseOffset; + if ( opaque() ) { + s->moveSplitter( pos, id() ); + } else { + s->setRubberband( s->adjustPos(pos, id()) ); + } +} + +void TQSplitterHandle::mousePressEvent( TQMouseEvent *e ) +{ + if ( e->button() == LeftButton ) + mouseOffset = s->pick( e->pos() ); +} + +void TQSplitterHandle::mouseReleaseEvent( TQMouseEvent *e ) +{ + if ( !opaque() && e->button() == LeftButton ) { + TQCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) ) + - mouseOffset; + s->setRubberband( -1 ); + s->moveSplitter( pos, id() ); + } +} + +void TQSplitterHandle::paintEvent( TQPaintEvent * ) +{ + TQPainter p( this ); + parentWidget()->style().drawPrimitive( TQStyle::PE_Splitter, &p, rect(), + colorGroup(), + (orientation() == Horizontal ? + TQStyle::Style_Horizontal : 0) ); +} + +TQCOORD TQSplitterLayoutStruct::getSizer( Orientation orient ) +{ + if ( sizer == -1 ) { + TQSize s = wid->sizeHint(); + if ( !s.isValid() || wid->testWState(WState_Resized) ) + s = wid->size(); + sizer = ( orient == Horizontal ) ? s.width() : s.height(); + } + return sizer; +} + +/*! + \class TQSplitter + \brief The TQSplitter 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 TQListBox, a TQListView and a TQTextEdit side by side: + \code + TQSplitter *split = new TQSplitter( parent ); + TQListBox *lb = new TQListBox( split ); + TQListView *lv = new TQListView( split ); + TQTextEdit *ed = new TQTextEdit( split ); + \endcode + + TQSplitter lays out its children horizontally (side by side); you + can use setOrientation(TQSplitter::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 TQSplitter 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 TQTabBar +*/ + + +/*! + Constructs a horizontal splitter with the \a parent and \a name + arguments being passed on to the TQFrame constructor. +*/ + +TQSplitter::TQSplitter( TQWidget *parent, const char *name ) + : TQFrame( 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 TQFrame constructor. +*/ + +TQSplitter::TQSplitter( Orientation o, TQWidget *parent, const char *name ) + : TQFrame( parent, name, WPaintUnclipped ) +{ + orient = o; + init(); +} + + +/*! + Destroys the splitter and any children. +*/ + +TQSplitter::~TQSplitter() +{ + delete d; +} + + +void TQSplitter::init() +{ + d = new TQSplitterPrivate; + d->list.setAutoDelete( TRUE ); + TQSizePolicy sp( TQSizePolicy::Expanding, TQSizePolicy::Preferred ); + if ( orient == Vertical ) + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); +} + +/*! + \fn void TQSplitter::refresh() + + Updates the splitter's state. You should not need to call this + function. +*/ + + +/*! + \property TQSplitter::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 TQSplitter::setOrientation( Orientation o ) +{ + if ( orient == o ) + return; + + if ( !testWState( WState_OwnSizePolicy ) ) { + TQSizePolicy sp = sizePolicy(); + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); + } + + orient = o; + + TQSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->isHandle ) + ((TQSplitterHandle*)s->wid)->setOrientation( o ); + s = d->list.next(); + } + recalc( isVisible() ); +} + +/*! + \property TQSplitter::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 TQSplitter::setChildrenCollapsible( bool collapse ) +{ + d->childrenCollapsible = collapse; +} + +bool TQSplitter::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 TQSplitter::setCollapsible( TQWidget *w, bool collapse ) +{ + findWidget( w )->collapsible = collapse ? 1 : 0; +} + +/*! + \reimp +*/ +void TQSplitter::resizeEvent( TQResizeEvent * ) +{ + doResize(); +} + +TQSplitterLayoutStruct *TQSplitter::findWidget( TQWidget *w ) +{ + processChildEvents(); + TQSplitterLayoutStruct *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.) +*/ + +TQSplitterLayoutStruct *TQSplitter::addWidget( TQWidget *w, bool prepend ) +{ + TQSplitterLayoutStruct *s; + TQSplitterHandle *newHandle = 0; + if ( d->list.count() > 0 ) { + s = new TQSplitterLayoutStruct; + s->resizeMode = KeepSize; + TQString tmp = "qt_splithandle_"; + tmp += w->name(); + newHandle = new TQSplitterHandle( 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 TQSplitterLayoutStruct; + 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 TQSplitter::childEvent( TQChildEvent *c ) +{ + if ( c->type() == TQEvent::ChildInserted ) { + if ( !c->child()->isWidgetType() ) + return; + + if ( ((TQWidget*)c->child())->testWFlags( WType_TopLevel ) ) + return; + + TQSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->wid == c->child() ) + return; + s = d->list.next(); + } + addWidget( (TQWidget*)c->child() ); + recalc( isVisible() ); + } else if ( c->type() == TQEvent::ChildRemoved ) { + TQSplitterLayoutStruct *prev = 0; + if ( d->list.count() > 1 ) + prev = d->list.at( 1 ); // yes, this is correct + TQSplitterLayoutStruct *curr = d->list.first(); + while ( curr ) { + if ( curr->wid == c->child() ) { + d->list.removeRef( curr ); + if ( prev && prev->isHandle ) { + TQWidget *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 TQSplitter::setRubberband( int p ) +{ + TQPainter paint( this ); + paint.setPen( gray ); + paint.setBrush( gray ); + paint.setRasterOp( XorROP ); + TQRect 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 TQSplitter::event( TQEvent *e ) +{ + switch ( e->type() ) { + case TQEvent::Show: + if ( !d->firstShow ) + break; + d->firstShow = FALSE; + // fall through + case TQEvent::LayoutHint: + recalc( isVisible() ); + break; + default: + ; + } + return TQWidget::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 TQStyle::drawPrimitive() +*/ + +// ### Remove this in 4.0 + +void TQSplitter::drawSplitter( TQPainter *p, + TQCOORD x, TQCOORD y, TQCOORD w, TQCOORD h ) +{ + style().drawPrimitive(TQStyle::PE_Splitter, p, TQRect(x, y, w, h), colorGroup(), + (orientation() == Horizontal ? + TQStyle::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 TQSplitter or \a w is at the end). +*/ + +int TQSplitter::idAfter( TQWidget* w ) const +{ + TQSplitterLayoutStruct *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 TQSplitter::moveSplitter( TQCOORD p, int id ) +{ + TQSplitterLayoutStruct *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 ( TQApplication::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 TQSplitter::setGeo( TQWidget *w, int p, int s, bool splitterMoved ) +{ + TQRect r; + if ( orient == Horizontal ) { + if ( TQApplication::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 TQSplitter::doMove( bool backwards, int pos, int id, int delta, bool upLeft, + bool mayCollapse ) +{ + if ( id < 0 || id >= (int) d->list.count() ) + return; + + TQSplitterLayoutStruct *s = d->list.at( id ); + TQWidget *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 = TQMAX( pick(qSmartMinSize(w)), + TQMIN(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 TQSplitter::findWidgetJustBeforeOrJustAfter( int id, int delta, int &collapsibleSize ) +{ + id += delta; + do { + TQWidget *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 TQSplitter::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 ); + + TQRect r = contentsRect(); + int farMinVal; + int minVal; + int maxVal; + int farMaxVal; + + int smartMinBefore = TQMAX( minBefore, pick(r.size()) - maxAfter ); + int smartMaxBefore = TQMIN( maxBefore, pick(r.size()) - minAfter ); + + if ( orient == Vertical || !TQApplication::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 TQSplitter::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 TQSplitter::adjustPos( int pos, int id ) +{ + int x, i, n, u; + return adjustPos( pos, id, &u, &n, &i, &x ); +} + +int TQSplitter::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 >= TQMIN(Threshold, width) ) { + return *farMax; + } else { + return *max; + } + } + } else { + int delta = *min - pos; + int width = *min - *farMin; + + if ( delta > width / 2 && delta >= TQMIN(Threshold, width) ) { + return *farMin; + } else { + return *min; + } + } +} + +bool TQSplitter::collapsible( TQSplitterLayoutStruct *s ) +{ + if (pick(qSmartMinSize(s->wid)) == 1) + return FALSE; + if ( s->collapsible != Default ) { + return (bool) s->collapsible; + } else { + return d->childrenCollapsible; + } +} + +void TQSplitter::doResize() +{ + TQRect r = contentsRect(); + int n = d->list.count(); + TQMemArray<TQLayoutStruct> 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(); + TQSplitterLayoutStruct *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 ) { + TQSizePolicy p = s->wid->sizePolicy(); + int sizePolicyStretch = + pick( TQSize(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 TQt + 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 ); + // TQMIN(): ad hoc work-around for layout engine limitation + a[i].stretch = TQMIN( 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++ ) { + TQSplitterLayoutStruct *s = d->list.at(i); + setGeo( s->wid, a[i].pos, a[i].size, FALSE ); + } +} + +void TQSplitter::recalc( bool update ) +{ + int fi = 2 * frameWidth(); + int maxl = fi; + int minl = fi; + int maxt = TQWIDGETSIZE_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++ ) { + TQSplitterLayoutStruct *s = d->list.at( i ); + if ( !s->isHandle ) { + TQSplitterLayoutStruct *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++ ) { + TQSplitterLayoutStruct *s = d->list.at( j ); + if ( !s->wid->isHidden() ) { + empty = FALSE; + if ( s->isHandle ) { + minl += s->getSizer( orient ); + maxl += s->getSizer( orient ); + } else { + TQSize minS = qSmartMinSize( s->wid ); + minl += pick( minS ); + maxl += pick( s->wid->maximumSize() ); + mint = TQMAX( mint, trans(minS) ); + int tm = trans( s->wid->maximumSize() ); + if ( tm > 0 ) + maxt = TQMIN( maxt, tm ); + } + } + } + if ( empty ) { + if ( ::qt_cast<TQSplitter*>(parentWidget()) ) { + // nested splitters; be nice + maxl = maxt = 0; + } else { + // TQSplitter with no children yet + maxl = TQWIDGETSIZE_MAX; + } + } else { + maxl = TQMIN( maxl, TQWIDGETSIZE_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 TQSplitter::ResizeMode + + This enum type describes how TQSplitter 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 TQSplitter will try to keep the widget's size + unchanged. + + \value FollowSizeHint TQSplitter 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 TQSplitter::setResizeMode( TQWidget *w, ResizeMode mode ) +{ + findWidget( w )->resizeMode = mode; +} + + +/*! + \property TQSplitter::opaqueResize + \brief whether resizing is opaque + + Opaque resizing is off by default. +*/ + +bool TQSplitter::opaqueResize() const +{ + return d->opaque; +} + + +void TQSplitter::setOpaqueResize( bool on ) +{ + d->opaque = on; +} + + +/*! + Moves widget \a w to the leftmost/top position. +*/ + +void TQSplitter::moveToFirst( TQWidget *w ) +{ + processChildEvents(); + bool found = FALSE; + TQSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->wid == w ) { + found = TRUE; + TQSplitterLayoutStruct *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 TQSplitter::moveToLast( TQWidget *w ) +{ + processChildEvents(); + bool found = FALSE; + TQSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->wid == w ) { + found = TRUE; + d->list.take(); // take s + TQSplitterLayoutStruct *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 TQSplitter::recalcId() +{ + int n = d->list.count(); + for ( int i = 0; i < n; i++ ) { + TQSplitterLayoutStruct *s = d->list.at( i ); + if ( s->isHandle ) + ((TQSplitterHandle*)s->wid)->setId( i ); + } +} + + +/*! + \reimp +*/ +TQSize TQSplitter::sizeHint() const +{ + constPolish(); + int l = 0; + int t = 0; + if ( children() ) { + const TQObjectList * c = children(); + TQObjectListIt it( *c ); + TQObject * o; + + while( (o = it.current()) != 0 ) { + ++it; + if ( o->isWidgetType() && !((TQWidget*)o)->isHidden() ) { + TQSize s = ((TQWidget*)o)->sizeHint(); + if ( s.isValid() ) { + l += pick( s ); + t = TQMAX( t, trans( s ) ); + } + } + } + } + return orientation() == Horizontal ? TQSize( l, t ) : TQSize( t, l ); +} + + +/*! + \reimp +*/ + +TQSize TQSplitter::minimumSizeHint() const +{ + constPolish(); + int l = 0; + int t = 0; + if ( children() ) { + const TQObjectList * c = children(); + TQObjectListIt it( *c ); + TQObject * o; + + while ( (o = it.current()) != 0 ) { + ++it; + if ( o->isWidgetType() && !((TQWidget*)o)->isHidden() ) { + TQSize s = qSmartMinSize( (TQWidget*)o ); + if ( s.isValid() ) { + l += pick( s ); + t = TQMAX( t, trans( s ) ); + } + } + } + } + return orientation() == Horizontal ? TQSize( l, t ) : TQSize( t, l ); +} + + +void TQSplitter::storeSizes() +{ + TQSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( !s->isHandle ) + s->sizer = pick( s->wid->size() ); + s = d->list.next(); + } +} + + +void TQSplitter::addContribution( int id, int *min, int *max, + bool mayCollapse ) +{ + TQSplitterLayoutStruct *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 + TQValueList<int> list = mySplitter.sizes(); + TQValueList<int>::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode + + \sa setSizes() +*/ + +TQValueList<int> TQSplitter::sizes() const +{ + if ( !testWState(WState_Polished) ) + constPolish(); + + TQValueList<int> list; + TQSplitterLayoutStruct *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 TQSplitter::setSizes( TQValueList<int> list ) +{ + processChildEvents(); + TQValueList<int>::Iterator it = list.begin(); + TQSplitterLayoutStruct *s = d->list.first(); + while ( s && it != list.end() ) { + if ( !s->isHandle ) { + s->sizer = TQMAX( *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 TQSplitter::handleWidth + \brief the width of the splitter handle +*/ + +int TQSplitter::handleWidth() const +{ + if ( d->handleWidth > 0 ) { + return d->handleWidth; + } else { + return style().pixelMetric( TQStyle::PM_SplitterWidth, this ); + } +} + +void TQSplitter::setHandleWidth( int width ) +{ + d->handleWidth = width; + updateHandles(); +} + +/*! + Processes all posted child events, ensuring that the internal state of + the splitter is kept consistent. +*/ + +void TQSplitter::processChildEvents() +{ + TQApplication::sendPostedEvents( this, TQEvent::ChildInserted ); +} + +/*! + \reimp +*/ + +void TQSplitter::styleChange( TQStyle& old ) +{ + updateHandles(); + TQFrame::styleChange( old ); +} + +void TQSplitter::updateHandles() +{ + int hw = handleWidth(); + TQSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->isHandle ) + s->sizer = hw; + s = d->list.next(); + } + recalc( isVisible() ); +} + +#ifndef QT_NO_TEXTSTREAM +/*! + \relates TQSplitter + + Writes the sizes and the hidden state of the widgets in the + splitter \a splitter to the text stream \a ts. + + \sa operator>>(), sizes(), TQWidget::isHidden() +*/ + +TQTextStream& operator<<( TQTextStream& ts, const TQSplitter& splitter ) +{ + TQSplitterLayoutStruct *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 TQSplitter + + 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(), TQWidget::hide() +*/ + +TQTextStream& operator>>( TQTextStream& ts, TQSplitter& splitter ) +{ +#undef SKIP_SPACES +#define SKIP_SPACES() \ + while ( line[i].isSpace() ) \ + i++ + + splitter.processChildEvents(); + TQSplitterLayoutStruct *s = splitter.d->list.first(); + TQString 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 000000000..598ae6037 --- /dev/null +++ b/src/widgets/qsplitter.h @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Definition of TQSplitter class +** +** Created : 980105 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSPLITTER_H +#define TQSPLITTER_H + +#ifndef QT_H +#include "qframe.h" +#include "qvaluelist.h" +#endif // QT_H + +#ifndef QT_NO_SPLITTER + +#define QT_QSPLITTER_DEFAULT 2 + +class TQSplitterHandle; +class TQSplitterPrivate; +class TQSplitterLayoutStruct; +class TQTextStream; + +class TQSplitterPrivate +{ +public: + TQSplitterPrivate() + : opaque( FALSE ), firstShow( TRUE ), childrenCollapsible( TRUE ), + handleWidth( 0 ) { } + + TQPtrList<TQSplitterLayoutStruct> list; + bool opaque; + bool firstShow; + bool childrenCollapsible; + int handleWidth; +}; + +class TQSplitterLayoutStruct : public TQt +{ +public: + TQCOORD sizer; + uint isHandle; + uint collapsible; + uint resizeMode; + TQWidget *wid; + + TQSplitterLayoutStruct() + : sizer( -1 ), collapsible( QT_QSPLITTER_DEFAULT ) { } + TQCOORD getSizer( Orientation orient ); +}; + +class Q_EXPORT TQSplitter : public TQFrame +{ + 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: + // ### TQt 4.0: remove Auto from public API + enum ResizeMode { Stretch, KeepSize, FollowSizeHint, Auto }; + + TQSplitter( TQWidget* parent = 0, const char* name = 0 ); + TQSplitter( Orientation, TQWidget* parent = 0, const char* name = 0 ); + ~TQSplitter(); + + virtual void setOrientation( Orientation ); + Orientation orientation() const { return orient; } + + // ### TQt 4.0: make setChildrenCollapsible() and setCollapsible() virtual + + void setChildrenCollapsible( bool ); + bool childrenCollapsible() const; + + void setCollapsible( TQWidget *w, bool ); + virtual void setResizeMode( TQWidget *w, ResizeMode ); + virtual void setOpaqueResize( bool = TRUE ); + bool opaqueResize() const; + + void moveToFirst( TQWidget * ); + void moveToLast( TQWidget * ); + + void refresh() { recalc( TRUE ); } + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + + TQValueList<int> sizes() const; + void setSizes( TQValueList<int> ); + + int handleWidth() const; + void setHandleWidth( int ); + +protected: + void childEvent( TQChildEvent * ); + + bool event( TQEvent * ); + void resizeEvent( TQResizeEvent * ); + + int idAfter( TQWidget* ) const; + + void moveSplitter( TQCOORD pos, int id ); + virtual void drawSplitter( TQPainter*, TQCOORD x, TQCOORD y, + TQCOORD w, TQCOORD h ); + void styleChange( TQStyle& ); + int adjustPos( int, int ); + virtual void setRubberband( int ); + void getRange( int id, int *, int * ); + +public: //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( TQSplitterLayoutStruct * ); + void processChildEvents(); + TQSplitterLayoutStruct *findWidget( TQWidget * ); + TQSplitterLayoutStruct *addWidget( TQWidget *, bool prepend = FALSE ); + void recalcId(); + void doMove( bool backwards, int pos, int id, int delta, bool upLeft, + bool mayCollapse ); + void setGeo( TQWidget *w, int pos, int size, bool splitterMoved ); + int findWidgetJustBeforeOrJustAfter( int id, int delta, int &collapsibleSize ); + void updateHandles(); + + inline TQCOORD pick( const TQPoint &p ) const + { return orient == Horizontal ? p.x() : p.y(); } + inline TQCOORD pick( const TQSize &s ) const + { return orient == Horizontal ? s.width() : s.height(); } + + inline TQCOORD trans( const TQPoint &p ) const + { return orient == Vertical ? p.x() : p.y(); } + inline TQCOORD trans( const TQSize &s ) const + { return orient == Vertical ? s.width() : s.height(); } + + TQSplitterPrivate *d; + + Orientation orient; + friend class TQSplitterHandle; + friend class TQTextStream; + +private: +#if defined(Q_DISABLE_COPY) + TQSplitter( const TQSplitter & ); + TQSplitter& operator=( const TQSplitter & ); +#endif +}; + +#ifndef QT_NO_TEXTSTREAM +Q_EXPORT TQTextStream& operator<<( TQTextStream&, const TQSplitter& ); +Q_EXPORT TQTextStream& operator>>( TQTextStream&, TQSplitter& ); +#endif + +class TQSplitterHandle : public TQWidget +{ + Q_OBJECT +public: + TQSplitterHandle( Orientation o, + TQSplitter *parent, const char* name=0 ); + void setOrientation( Orientation o ); + Orientation orientation() const { return orient; } + + bool opaque() const { return s->opaqueResize(); } + + TQSize sizeHint() const; + + int id() const { return myId; } // d->list.at(id())->wid == this + void setId( int i ) { myId = i; } + +protected: + void paintEvent( TQPaintEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void mousePressEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + +public: // private: + Orientation orient; + bool opaq; + int myId; + + TQSplitter *s; +}; + +#endif // QT_NO_SPLITTER + +#endif // TQSPLITTER_H diff --git a/src/widgets/qstatusbar.cpp b/src/widgets/qstatusbar.cpp new file mode 100644 index 000000000..dc1eda57f --- /dev/null +++ b/src/widgets/qstatusbar.cpp @@ -0,0 +1,526 @@ +/**************************************************************************** +** +** Implementation of TQStatusBar class +** +** Created : 980119 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQStatusBar qstatusbar.h + \brief The TQStatusBar 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 + + TQStatusBar 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 TQString&)), + statusBar(), SLOT(message(const TQString&)) ); + + 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 TQLabel, TQProgressBar or even TQToolButton + are useful for adding to status bars. removeWidget() is used to + remove widgets. + + \code + statusBar()->addWidget(new MyReadWriteIndication(statusBar())); + \endcode + + By default TQStatusBar provides a TQSizeGrip in the lower-right + corner. You can disable it with setSizeGripEnabled(FALSE); + + <img src=qstatusbar-m.png> <img src=qstatusbar-w.png> + + \sa TQToolBar TQMainWindow TQLabel + \link guibooks.html#fowler GUI Design Handbook: Status Bar.\endlink +*/ + + +class TQStatusBarPrivate +{ +public: + TQStatusBarPrivate() {} + + struct SBItem { + SBItem( TQWidget* widget, int stretch, bool permanent ) + : s( stretch ), w( widget ), p( permanent ) {} + int s; + TQWidget * w; + bool p; + }; + + TQPtrList<SBItem> items; + TQString tempItem; + + TQBoxLayout * box; + TQTimer * timer; + +#ifndef QT_NO_SIZEGRIP + TQSizeGrip * resizer; +#endif + + int savedStrut; +}; + + +/*! + Constructs a status bar called \a name with parent \a parent and + with a size grip. + + \sa setSizeGripEnabled() +*/ +TQStatusBar::TQStatusBar( TQWidget * parent, const char *name ) + : TQWidget( parent, name ) +{ + d = new TQStatusBarPrivate; + 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. +*/ +TQStatusBar::~TQStatusBar() +{ + delete d; + d = 0; +} + + +/*! + Adds \a widget to this status bar. \a widget is reparented if it + isn't already a child of the TQStatusBar. + + \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 TQStatusBar::addWidget( TQWidget * widget, int stretch, bool permanent ) +{ + if ( !widget ) { +#if defined(QT_CHECK_NULL) + qWarning( "TQStatusBar::addWidget(): Cannot add null widget" ); +#endif + return; + } + + if ( widget->parentWidget() != this ) + widget->reparent( this, TQPoint(0, 0), TRUE ); + + TQStatusBarPrivate::SBItem* item + = new TQStatusBarPrivate::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 TQStatusBar::removeWidget( TQWidget* widget ) +{ + if ( !widget ) + return; + bool found = FALSE; + TQStatusBarPrivate::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( "TQStatusBar::removeWidget(): Widget not found." ); +#endif +} + +/*! + \property TQStatusBar::sizeGripEnabled + \brief whether the TQSizeGrip in the bottom right of the status bar is enabled + + Enables or disables the TQSizeGrip in the bottom right of the + status bar. By default, the size grip is enabled. +*/ + +bool TQStatusBar::isSizeGripEnabled() const +{ +#ifdef QT_NO_SIZEGRIP + return FALSE; +#else + return !!d->resizer; +#endif +} + +void TQStatusBar::setSizeGripEnabled(bool enabled) +{ +#ifndef QT_NO_SIZEGRIP + if ( !enabled != !d->resizer ) { + if ( enabled ) { + d->resizer = new TQSizeGrip( this, "TQStatusBar::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 TQStatusBar::reformat() +{ + if ( d->box ) + delete d->box; + + TQBoxLayout *vbox; + if ( isSizeGripEnabled() ) { + d->box = new TQHBoxLayout( this ); + vbox = new TQVBoxLayout( d->box ); + } else { + vbox = d->box = new TQVBoxLayout( this ); + } + vbox->addSpacing( 3 ); + TQBoxLayout* l = new TQHBoxLayout( vbox ); + l->addSpacing( 3 ); + l->setSpacing( 4 ); + + int maxH = fontMetrics().height(); + + TQStatusBarPrivate::SBItem* item = d->items.first(); + while ( item && !item->p ) { + l->addWidget( item->w, item->s ); + int itemH = TQMIN(item->w->sizeHint().height(), + item->w->maximumHeight()); + maxH = TQMAX( maxH, itemH ); + item = d->items.next(); + } + + l->addStretch( 0 ); + + while ( item ) { + l->addWidget( item->w, item->s ); + int itemH = TQMIN(item->w->sizeHint().height(), + item->w->maximumHeight()); + maxH = TQMAX( maxH, itemH ); + item = d->items.next(); + } + l->addSpacing( 4 ); +#ifndef QT_NO_SIZEGRIP + if ( d->resizer ) { + maxH = TQMAX( 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 TQStatusBar::message( const TQString &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 TQStatusBar::message( const TQString &message, int ms ) +{ + d->tempItem = message; + + if ( !d->timer ) { + d->timer = new TQTimer( 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 TQStatusBar::clear() +{ + if ( d->tempItem.isEmpty() ) + return; + if ( d->timer ) { + delete d->timer; + d->timer = 0; + } + d->tempItem = TQString::null; + hideOrShow(); +} + +/*! + \fn TQStatusBar::messageChanged( const TQString &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 TQStatusBar::hideOrShow() +{ + bool haveMessage = !d->tempItem.isEmpty(); + + TQStatusBarPrivate::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 TQStatusBar::paintEvent( TQPaintEvent * ) +{ + bool haveMessage = !d->tempItem.isEmpty(); + + TQPainter p( this ); + TQStatusBarPrivate::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( TQStyle::PE_StatusBarSection, &p, + TQRect(item->w->x() - 1, item->w->y() - 1, + item->w->width()+2, item->w->height()+2), + colorGroup(), TQStyle::Style_Default, + TQStyleOption(item->w) ); + } + item = d->items.next(); + } + if ( haveMessage ) { + p.setPen( colorGroup().foreground() ); + p.drawText( 6, 0, psx, height(), AlignVCenter | SingleLine, d->tempItem ); + } +} + +/*! + \reimp +*/ +void TQStatusBar::resizeEvent( TQResizeEvent * e ) +{ + TQWidget::resizeEvent( e ); +} + +/*! + \reimp +*/ + +bool TQStatusBar::event( TQEvent *e ) +{ + if ( e->type() == TQEvent::LayoutHint ) { + // Calculate new strut height and call reformat() if it has changed + int maxH = fontMetrics().height(); + + TQStatusBarPrivate::SBItem* item = d->items.first(); + while ( item ) { + int itemH = TQMIN(item->w->sizeHint().height(), + item->w->maximumHeight()); + maxH = TQMAX( maxH, itemH ); + item = d->items.next(); + } + +#ifndef QT_NO_SIZEGRIP + if ( d->resizer ) + maxH = TQMAX( maxH, d->resizer->sizeHint().height() ); +#endif + + if ( maxH != d->savedStrut ) + reformat(); + else + update(); + } + if ( e->type() == TQEvent::ChildRemoved ) { + TQStatusBarPrivate::SBItem* item = d->items.first(); + while ( item ) { + if ( item->w == ( (TQChildEvent*)e )->child() ) + d->items.removeRef( item ); + item = d->items.next(); + } + } + return TQWidget::event( e ); +} + +#endif diff --git a/src/widgets/qstatusbar.h b/src/widgets/qstatusbar.h new file mode 100644 index 000000000..45ff2e885 --- /dev/null +++ b/src/widgets/qstatusbar.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Definition of TQStatusBar class +** +** Created : 980316 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSTATUSBAR_H +#define TQSTATUSBAR_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_STATUSBAR + + +class TQStatusBarPrivate; + + +class Q_EXPORT TQStatusBar: public TQWidget +{ + Q_OBJECT + Q_PROPERTY( bool sizeGripEnabled READ isSizeGripEnabled WRITE setSizeGripEnabled ) + +public: + TQStatusBar( TQWidget* parent=0, const char* name=0 ); + virtual ~TQStatusBar(); + + virtual void addWidget( TQWidget *, int stretch = 0, bool = FALSE ); + virtual void removeWidget( TQWidget * ); + + void setSizeGripEnabled(bool); + bool isSizeGripEnabled() const; + +public slots: + void message( const TQString &); + void message( const TQString &, int ); + void clear(); + +signals: + void messageChanged( const TQString &text ); + +protected: + void paintEvent( TQPaintEvent * ); + void resizeEvent( TQResizeEvent * ); + + void reformat(); + void hideOrShow(); + bool event( TQEvent *); + +private: + TQStatusBarPrivate * d; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQStatusBar( const TQStatusBar & ); + TQStatusBar& operator=( const TQStatusBar & ); +#endif +}; + +#endif // QT_NO_STATUSBAR + +#endif // TQSTATUSBAR_H diff --git a/src/widgets/qsyntaxhighlighter.cpp b/src/widgets/qsyntaxhighlighter.cpp new file mode 100644 index 000000000..061d9c538 --- /dev/null +++ b/src/widgets/qsyntaxhighlighter.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Implementation of the TQSyntaxHighlighter class +** +** Created : 990101 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qsyntaxhighlighter.h" +#include "private/qsyntaxhighlighter_p.h" + +#ifndef QT_NO_SYNTAXHIGHLIGHTER +#include "../kernel/qrichtext_p.h" +#include "qtextedit.h" +#include "qtimer.h" + +/*! + \class TQSyntaxHighlighter qsyntaxhighlighter.h + \brief The TQSyntaxHighlighter class is a base class for + implementing TQTextEdit syntax highlighters. + + \ingroup basic + \ingroup text + + A syntax highligher automatically highlights parts of the text in + a TQTextEdit. 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 TQTextEdit, you must + subclass TQSyntaxHighlighter and reimplement highlightParagraph(). + + When you create an instance of your TQSyntaxHighlighter subclass, + pass it the TQTextEdit 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 TQSyntaxHighlighter and installs it on \a textEdit. + + It is the caller's responsibility to delete the + TQSyntaxHighlighter when it is no longer needed. +*/ + +TQSyntaxHighlighter::TQSyntaxHighlighter( TQTextEdit *textEdit ) + : para( 0 ), edit( textEdit ), d( new TQSyntaxHighlighterPrivate ) +{ + textEdit->document()->setPreProcessor( new TQSyntaxHighlighterInternal( this ) ); + textEdit->document()->invalidate(); + TQTimer::singleShot( 0, textEdit->viewport(), SLOT( update() ) ); +} + +/*! + Destructor. Uninstalls this syntax highlighter from the textEdit() +*/ + +TQSyntaxHighlighter::~TQSyntaxHighlighter() +{ + delete d; + textEdit()->document()->setPreProcessor( 0 ); +} + +/*! + \fn int TQSyntaxHighlighter::highlightParagraph( const TQString &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 retquire. 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 TQSyntaxHighlighter::setFormat( int start, int count, const TQFont &font, const TQColor &color ) +{ + if ( !para || count <= 0 ) + return; + TQTextFormat *f = 0; + f = para->document()->formatCollection()->format( font, color ); + para->setFormat( start, count, f ); + f->removeRef(); +} + +/*! \overload */ + +void TQSyntaxHighlighter::setFormat( int start, int count, const TQColor &color ) +{ + if ( !para || count <= 0 ) + return; + TQTextFormat *f = 0; + TQFont fnt = textEdit()->TQWidget::font(); + f = para->document()->formatCollection()->format( fnt, color ); + para->setFormat( start, count, f ); + f->removeRef(); +} + +/*! \overload */ + +void TQSyntaxHighlighter::setFormat( int start, int count, const TQFont &font ) +{ + if ( !para || count <= 0 ) + return; + TQTextFormat *f = 0; + TQColor c = textEdit()->viewport()->paletteForegroundColor(); + f = para->document()->formatCollection()->format( font, c ); + para->setFormat( start, count, f ); + f->removeRef(); +} + +/*! + \fn TQTextEdit *TQSyntaxHighlighter::textEdit() const + + Returns the TQTextEdit on which this syntax highlighter is + installed +*/ + +/*! Redoes the highlighting of the whole document. +*/ + +void TQSyntaxHighlighter::rehighlight() +{ + TQTextParagraph *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 TQSyntaxHighlighter::currentParagraph() const +{ + return d->currentParagraph; +} + +#endif diff --git a/src/widgets/qsyntaxhighlighter.h b/src/widgets/qsyntaxhighlighter.h new file mode 100644 index 000000000..7b3d726d6 --- /dev/null +++ b/src/widgets/qsyntaxhighlighter.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Definition of the TQSyntaxHighlighter class +** +** Created : 022407 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSYNTAXHIGHLIGHTER_H +#define TQSYNTAXHIGHLIGHTER_H + +#ifndef QT_H +#include "qfont.h" +#include "qcolor.h" +#include "qstring.h" +#endif // QT_H + +class TQTextEdit; +class TQSyntaxHighlighterInternal; +class TQSyntaxHighlighterPrivate; +class TQTextParagraph; + +class Q_EXPORT TQSyntaxHighlighter : public TQt +{ + friend class TQSyntaxHighlighterInternal; + +public: + TQSyntaxHighlighter( TQTextEdit *textEdit ); + virtual ~TQSyntaxHighlighter(); + + virtual int highlightParagraph( const TQString &text, int endStateOfLastPara ) = 0; + + void setFormat( int start, int count, const TQFont &font, const TQColor &color ); + void setFormat( int start, int count, const TQColor &color ); + void setFormat( int start, int count, const TQFont &font ); + TQTextEdit *textEdit() const { return edit; } + + void rehighlight(); + + int currentParagraph() const; + +private: + TQTextParagraph *para; + TQTextEdit *edit; + TQSyntaxHighlighterPrivate *d; + +}; + +#endif diff --git a/src/widgets/qsyntaxhighlighter_p.h b/src/widgets/qsyntaxhighlighter_p.h new file mode 100644 index 000000000..067e3e388 --- /dev/null +++ b/src/widgets/qsyntaxhighlighter_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Definition of the internal TQSyntaxHighlighterInternal class +** +** Created : 031111 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQSYNTAXHIGHLIGHTER_P_H +#define TQSYNTAXHIGHLIGHTER_P_H + +#ifndef QT_NO_SYNTAXHIGHLIGHTER +#include "qsyntaxhighlighter.h" +#include "private/qrichtext_p.h" + +class TQSyntaxHighlighterPrivate +{ +public: + TQSyntaxHighlighterPrivate() : + currentParagraph( -1 ) + {} + + int currentParagraph; +}; + +class TQSyntaxHighlighterInternal : public TQTextPreProcessor +{ +public: + TQSyntaxHighlighterInternal( TQSyntaxHighlighter *h ) : highlighter( h ) {} + void process( TQTextDocument *doc, TQTextParagraph *p, int, bool invalidate ) { + if ( p->prev() && p->prev()->endState() == -1 ) + process( doc, p->prev(), 0, FALSE ); + + highlighter->para = p; + TQString 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 ); + TQTextParagraph *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(); + } + } + } + TQTextFormat *format( int ) { return 0; } + +private: + TQSyntaxHighlighter *highlighter; + + friend class TQTextEdit; +}; + +#endif // QT_NO_SYNTAXHIGHLIGHTER +#endif // TQSYNTAXHIGHLIGHTER_P_H diff --git a/src/widgets/qt_widgets.pri b/src/widgets/qt_widgets.pri new file mode 100644 index 000000000..a4917e9bf --- /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 000000000..d66790f38 --- /dev/null +++ b/src/widgets/qtabbar.cpp @@ -0,0 +1,1368 @@ +/**************************************************************************** +** +** Implementation of TQTab and TQTabBar classes +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQTab qtabbar.h + \brief The TQTab class provides the structures in a TQTabBar. + + \ingroup advanced + + This class is used for custom TQTabBar tab headings. + + \sa TQTabBar +*/ + + +/*! + Constructs an empty tab. All fields are set to empty. +*/ + +TQTab::TQTab() + : enabled( TRUE ), + id ( 0 ), + iconset( 0 ), + tb( 0 ) +{ +} + +/*! + Constructs a tab with the text \a text. +*/ + +TQTab::TQTab( const TQString &text ) + : label( text ), + enabled( TRUE ), + id( 0 ), + iconset( 0 ), + tb( 0 ) +{ +} + +/*! + Constructs a tab with an \a icon and the text, \a text. +*/ + +TQTab::TQTab( const TQIconSet& icon, const TQString& text ) + : label( text ), + enabled( TRUE ), + id( 0 ), + iconset( new TQIconSet(icon) ), + tb( 0 ) +{ +} + +/*! + \fn TQString TQTab::text() const + + Returns the text of the TQTab label. +*/ + +/*! + \fn TQIconSet TQTab::iconSet() const + + Return the TQIconSet of the TQTab. +*/ + +/*! + \fn void TQTab::setRect( const TQRect &rect ) + + Set the TQTab TQRect to \a rect. +*/ + +/*! + \fn TQRect TQTab::rect() const + + Return the TQRect for the TQTab. +*/ + +/*! + \fn void TQTab::setEnabled( bool enable ) + + If \a enable is TRUE enable the TQTab, otherwise disable it. +*/ + +/*! + \fn bool TQTab::isEnabled() const + + Returns TRUE if the TQTab is enabled; otherwise returns FALSE. +*/ + +/*! + \fn void TQTab::setIdentifier( int i ) + + Set the identifier for the TQTab to \a i. Each TQTab's identifier + within a TQTabBar must be unique. +*/ + +/*! + \fn int TQTab::identifier() const + + Return the TQTab's identifier. +*/ + + + +/*! + Destroys the tab and frees up all allocated resources. +*/ + +TQTab::~TQTab() +{ + delete iconset; + tb = 0; +} + +/*! + \class TQTabBar qtabbar.h + \brief The TQTabBar class provides a tab bar, e.g. for use in tabbed dialogs. + + \ingroup advanced + + TQTabBar is straightforward to use; it draws the tabs using one of + the predefined \link TQTabBar::Shape shapes\endlink, and emits a + signal when a tab is selected. It can be subclassed to tailor the + look and feel. TQt also provides a ready-made \l{TQTabWidget} and a + \l{TQTabDialog}. + + 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 TQTabBar'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. + + TQTabBar creates automatic accelerator keys in the manner of + TQButton; 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 TQTabBar::Shape + + This enum type lists the built-in shapes supported by TQTabBar: + + \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 TQTabBarToolTip; + +struct TQTabPrivate { + int id; + int focus; +#ifndef QT_NO_ACCEL + TQAccel * a; +#endif + TQTab *pressed; + TQTabBar::Shape s; + TQToolButton* rightB; + TQToolButton* leftB; + int btnWidth; + bool scrolls; + TQTabBarToolTip * toolTips; +}; + +#ifndef QT_NO_TOOLTIP +/* \internal +*/ +class TQTabBarToolTip : public TQToolTip +{ +public: + TQTabBarToolTip( TQWidget * parent ) + : TQToolTip( parent ) {} + virtual ~TQTabBarToolTip() {} + + void add( TQTab * tab, const TQString & tip ) + { + tabTips.replace( tab, tip ); + } + + void remove( TQTab * tab ) + { + tabTips.erase( tab ); + } + + TQString tipForTab( TQTab * tab ) const + { + TQMapConstIterator<TQTab *, TQString> it; + it = tabTips.find( tab ); + if ( it != tabTips.end() ) + return it.data(); + else + return TQString(); + } + +protected: + void maybeTip( const TQPoint & p ) + { + TQTabBar * tb = (TQTabBar *) 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 + TQRect 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 + TQMapIterator<TQTab *, TQString> it; + for ( it = tabTips.begin(); it != tabTips.end(); ++it ) { + if ( it.key()->rect().contains( p ) ) + tip( it.key()->rect(), it.data() ); + } +#endif + } + +private: + TQMap<TQTab *, TQString> tabTips; +}; +#endif + +/*! + \fn void TQTabBar::selected( int id ) + + TQTabBar 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 TQTabBar::layoutChanged() + + TQTabBar 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 TQWidget constructor. +*/ + +TQTabBar::TQTabBar( TQWidget * parent, const char *name ) + : TQWidget( parent, name, WNoAutoErase | WNoMousePropagation ) +{ + d = new TQTabPrivate; + d->pressed = 0; + d->id = 0; + d->focus = 0; + d->toolTips = 0; +#ifndef QT_NO_ACCEL + d->a = new TQAccel( 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 TQToolButton( LeftArrow, this, "qt_left_btn" ); + connect( d->leftB, SIGNAL( clicked() ), this, SLOT( scrollTabs() ) ); + d->leftB->hide(); + d->rightB = new TQToolButton( RightArrow, this, "qt_right_btn" ); + connect( d->rightB, SIGNAL( clicked() ), this, SLOT( scrollTabs() ) ); + d->rightB->hide(); + d->btnWidth = style().pixelMetric(TQStyle::PM_TabBarScrollButtonWidth, this); + l = new TQPtrList<TQTab>; + lstatic = new TQPtrList<TQTab>; + lstatic->setAutoDelete( TRUE ); + setFocusPolicy( TabFocus ); + setSizePolicy( TQSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Fixed ) ); +} + + +/*! + Destroys the tab control, freeing memory used. +*/ + +TQTabBar::~TQTabBar() +{ +#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 TQTabBar::addTab( TQTab * 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 TQTabBar::insertTab( TQTab * 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 = TQAccel::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 TQTabBar::removeTab( TQTab * 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 TQTabBar::setTabEnabled( int id, bool enabled ) +{ + TQTab * 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 + TQRect r( t->r ); + if ( !enabled && id == currentTab() ) { + TQPoint 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( TQTab * 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 TQTabBar::isTabEnabled( int id ) const +{ + TQTab * t = tab( id ); + if ( t ) + return t->enabled; + return FALSE; +} + + + +/*! + \reimp +*/ +TQSize TQTabBar::sizeHint() const +{ + TQSize sz(0, 0); + if ( TQTab * t = l->first() ) { + TQRect r( t->r ); + while ( (t = l->next()) != 0 ) + r = r.unite( t->r ); + sz = r.size(); + } + return sz.expandedTo(TQApplication::globalStrut()); +} + +/*! + \reimp +*/ + +TQSize TQTabBar::minimumSizeHint() const +{ + if(style().styleHint( TQStyle::SH_TabBar_PreferNoArrows, this )) + return sizeHint(); + return TQSize( 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 + TQTabBar. If you decide to reimplement it, you may also need to + reimplement sizeHint(). +*/ + +void TQTabBar::paint( TQPainter * p, TQTab * t, bool selected ) const +{ + TQStyle::SFlags flags = TQStyle::Style_Default; + + if (isEnabled() && t->isEnabled()) + flags |= TQStyle::Style_Enabled; + if (topLevelWidget() == qApp->activeWindow()) + flags |= TQStyle::Style_Active; + if ( selected ) + flags |= TQStyle::Style_Selected; + else if(t == d->pressed) + flags |= TQStyle::Style_Sunken; + //selection flags + if(t->rect().contains(mapFromGlobal(TQCursor::pos()))) + flags |= TQStyle::Style_MouseOver; + style().drawControl( TQStyle::CE_TabBarTab, p, this, t->rect(), + colorGroup(), flags, TQStyleOption(t) ); + + TQRect r( t->r ); + p->setFont( font() ); + + int iw = 0; + int ih = 0; + if ( t->iconset != 0 ) { + iw = t->iconset->pixmap( TQIconSet::Small, TQIconSet::Normal ).width() + 4; + ih = t->iconset->pixmap( TQIconSet::Small, TQIconSet::Normal ).height(); + } + TQFontMetrics 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 = TQMAX(fm.height() + 4, ih ); + int offset = 3; +#ifdef Q_WS_MAC + if (::qt_cast<TQMacStyle *>(&style())) + offset = 0; +#endif + paintLabel( p, TQRect( 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 TQTabBar::paintLabel( TQPainter* p, const TQRect& br, + TQTab* t, bool has_focus ) const +{ + TQRect r = br; + bool selected = currentTab() == t->id; + if ( t->iconset) { + // the tab has an iconset, draw it in the right mode + TQIconSet::Mode mode = (t->enabled && isEnabled()) + ? TQIconSet::Normal : TQIconSet::Disabled; + if ( mode == TQIconSet::Normal && has_focus ) + mode = TQIconSet::Active; + TQPixmap pixmap = t->iconset->pixmap( TQIconSet::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(TQStyle::PM_TabBarTabShiftHorizontal, this); + yoff = style().pixelMetric(TQStyle::PM_TabBarTabShiftVertical, this); + } + p->drawPixmap( br.left() + 2 + xoff, br.center().y()-pixh/2 + yoff, pixmap ); + } + + TQStyle::SFlags flags = TQStyle::Style_Default; + + if (isEnabled() && t->isEnabled()) + flags |= TQStyle::Style_Enabled; + if (has_focus) + flags |= TQStyle::Style_HasFocus; + if ( selected ) + flags |= TQStyle::Style_Selected; + else if(t == d->pressed) + flags |= TQStyle::Style_Sunken; + if(t->rect().contains(mapFromGlobal(TQCursor::pos()))) + flags |= TQStyle::Style_MouseOver; + style().drawControl( TQStyle::CE_TabBarLabel, p, this, r, + t->isEnabled() ? colorGroup(): palette().disabled(), + flags, TQStyleOption(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 TQTabBar::paintEvent( TQPaintEvent * e ) +{ + if ( e->rect().isNull() ) + return; + + TQSharedDoubleBuffer buffer( this, e->rect() ); + + TQTab * t; + t = l->first(); + do { + TQTab * 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 ) { + TQPointArray a; + int h = height(); + if ( d->s == RoundedAbove ) { + buffer.painter()->fillRect( 0, 3, 4, h-5, + colorGroup().brush( TQColorGroup::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( TQColorGroup::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() +*/ + +TQTab * TQTabBar::selectTab( const TQPoint & p ) const +{ + TQTab * selected = 0; + bool moreThanOne = FALSE; + + TQPtrListIterator<TQTab> i( *l ); + while( i.current() ) { + TQTab * t = i.current(); + ++i; + + if ( t && t->r.contains( p ) ) { + if ( selected ) + moreThanOne = TRUE; + else + selected = t; + } + } + + return moreThanOne ? 0 : selected; +} + + +/*! + \reimp +*/ +void TQTabBar::mousePressEvent( TQMouseEvent * e ) +{ + if ( e->button() != LeftButton ) { + e->ignore(); + return; + } + TQTab *t = selectTab( e->pos() ); + if ( t && t->enabled ) { + d->pressed = t; + if(e->type() == style().styleHint( TQStyle::SH_TabBar_SelectMouseType, this )) + setCurrentTab( t ); + else + repaint(t->rect(), FALSE); + } +} + + +/*! + \reimp +*/ + +void TQTabBar::mouseMoveEvent ( TQMouseEvent *e ) +{ + if ( e->state() != LeftButton ) { + e->ignore(); + return; + } + if(style().styleHint( TQStyle::SH_TabBar_SelectMouseType, this ) == TQEvent::MouseButtonRelease) { + TQTab *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 TQTabBar::mouseReleaseEvent( TQMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + e->ignore(); + if(d->pressed) { + TQTab *t = selectTab( e->pos() ) == d->pressed ? d->pressed : 0; + d->pressed = 0; + if(t && t->enabled && e->type() == style().styleHint( TQStyle::SH_TabBar_SelectMouseType, this )) + setCurrentTab( t ); + } +} + + +/*! + \reimp +*/ +void TQTabBar::show() +{ + // ensures that one tab is selected. + TQTab * t = l->last(); + TQWidget::show(); + + if ( t ) + emit selected( t->id ); +} + +/*! + \property TQTabBar::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 TQTabBar::currentTab() const +{ + const TQTab * t = l->getLast(); + + return t ? t->id : -1; +} + +void TQTabBar::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 TQTabBar::setCurrentTab( TQTab * tab ) +{ + if ( tab && l ) { + if ( l->last() == tab ) + return; + + TQRect 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 + TQAccessible::updateAccessibility( this, indexOf(tab->id)+1, TQAccessible::Focus ); +#endif + } +} + +/*! + \property TQTabBar::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 TQTabBar::keyboardFocusTab() const +{ + return hasFocus() ? d->focus : -1; +} + + +/*! + \reimp +*/ +void TQTabBar::keyPressEvent( TQKeyEvent * 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 = TQApplication::reverseLayout(); + if ( ( !reverse && e->key() == Key_Left ) || ( reverse && e->key() == Key_Right ) ) { + // left - skip past any disabled ones + if ( d->focus >= 0 ) { + TQTab * 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 ) ) { + TQTab * 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() +*/ + +TQTab * TQTabBar::tab( int id ) const +{ + TQTab * 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() +*/ + +TQTab * TQTabBar::tabAt( int index ) const +{ + TQTab * 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 TQTabBar::indexOf( int id ) const +{ + TQTab * t; + int idx = 0; + for( t = lstatic->first(); t; t = lstatic->next() ) { + if ( t && t->id == id ) + return idx; + idx++; + } + return -1; +} + + +/*! + \property TQTabBar::count + \brief the number of tabs in the tab bar + + \sa tab() +*/ +int TQTabBar::count() const +{ + return l->count(); +} + + +/*! + The list of TQTab objects in the tab bar. + + This list is unlikely to be in the order that the TQTab 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 +*/ +TQPtrList<TQTab> * TQTabBar::tabList() +{ + return l; +} + + +/*! + \property TQTabBar::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 +*/ +TQTabBar::Shape TQTabBar::shape() const +{ + return d ? d->s : RoundedAbove; +} + +void TQTabBar::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 TQTabBar::layoutTabs() +{ + if ( lstatic->isEmpty() ) + return; + + TQSize oldSh(0, 0); + if ( TQTab * t = l->first() ) { + TQRect r( t->r ); + while ( (t = l->next()) != 0 ) + r = r.unite( t->r ); + oldSh = r.size(); + } + + d->btnWidth = style().pixelMetric(TQStyle::PM_TabBarScrollButtonWidth, this); + int hframe, vframe, overlap; + hframe = style().pixelMetric( TQStyle::PM_TabBarTabHSpace, this ); + vframe = style().pixelMetric( TQStyle::PM_TabBarTabVSpace, this ); + overlap = style().pixelMetric( TQStyle::PM_TabBarTabOverlap, this ); + + TQFontMetrics fm = fontMetrics(); + TQRect r; + TQTab *t; + bool reverse = TQApplication::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( TQIconSet::Small, TQIconSet::Normal ).width() + 4; + ih = t->iconset->pixmap( TQIconSet::Small, TQIconSet::Normal ).height(); + } + int h = TQMAX( fm.height(), ih ); + h = TQMAX( h, TQApplication::globalStrut().height() ); + + h += vframe; + t->r = TQRect(TQPoint(x, 0), style().sizeFromContents(TQStyle::CT_TabBarTab, this, + TQSize( TQMAX( lw + hframe + iw, TQApplication::globalStrut().width() ), h ), + TQStyleOption(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 TQTabBar::event( TQEvent *e ) +{ + if ( e->type() == TQEvent::LanguageChange ) { + layoutTabs(); + updateArrowButtons(); + makeVisible( tab( currentTab() )); + } + + return TQWidget::event( e ); +} + +/*! + \reimp +*/ + +void TQTabBar::styleChange( TQStyle& old ) +{ + layoutTabs(); + updateArrowButtons(); + TQWidget::styleChange( old ); +} + +/*! + \reimp +*/ +void TQTabBar::focusInEvent( TQFocusEvent * ) +{ + TQTab *t = tab( d->focus ); + if ( t ) + repaint( t->r, FALSE ); +} + +/*! + \reimp +*/ +void TQTabBar::focusOutEvent( TQFocusEvent * ) +{ + TQTab *t = tab( d->focus ); + if ( t ) + repaint( t->r, FALSE ); +} + +/*! + \reimp +*/ +void TQTabBar::resizeEvent( TQResizeEvent * ) +{ + const int arrowWidth = TQMAX( d->btnWidth, TQApplication::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 TQTabBar::scrollTabs() +{ + TQTab* left = 0; + TQTab* right = 0; + for ( TQTab* 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 TQTabBar::makeVisible( TQTab* 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 ( TQTab* 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 TQTabBar::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 = TQMAX( d->btnWidth, TQApplication::globalStrut().width() ); + if ( TQApplication::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 TQTabBar::removeToolTip( int index ) +{ +#ifndef QT_NO_TOOLTIP + TQTab * 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 TQTabBar::setToolTip( int index, const TQString & tip ) +{ +#ifndef QT_NO_TOOLTIP + TQTab * tab = tabAt( index ); + if ( !tab ) + return; + if ( d->toolTips == 0 ) + d->toolTips = new TQTabBarToolTip( this ); + d->toolTips->add( tab, tip ); +#endif +} + +/*! + Returns the tool tip for the tab at index position \a index. +*/ +TQString TQTabBar::toolTip( int index ) const +{ +#ifndef QT_NO_TOOLTIP + if ( d->toolTips ) + return d->toolTips->tipForTab( tabAt( index ) ); + else +#endif + return TQString(); +} + +/*! + Sets the text of the tab to \a text. +*/ +void TQTab::setText( const TQString& text ) +{ + label = text; + if ( tb ) { +#ifndef QT_NO_ACCEL + tb->d->a->removeItem( id ); + int p = TQAccel::shortcutKey( text ); + if ( p ) + tb->d->a->insertItem( p, id ); +#endif + tb->layoutTabs(); + tb->repaint(FALSE); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( tb, tb->indexOf(id)+1, TQAccessible::NameChanged ); +#endif + } +} + +/*! + Sets the tab's iconset to \a icon +*/ +void TQTab::setIconSet( const TQIconSet &icon ) +{ + iconset = new TQIconSet( icon ); +} + +// this allows us to handle accelerators that are in a TQTabBar. +void TQTab::setTabBar( TQTabBar *newTb ) +{ + tb = newTb; +} + +/*! + \internal +*/ +void TQTabBar::fontChange( const TQFont & oldFont ) +{ + layoutTabs(); + TQWidget::fontChange( oldFont ); +} + +#endif diff --git a/src/widgets/qtabbar.h b/src/widgets/qtabbar.h new file mode 100644 index 000000000..48f76aeb3 --- /dev/null +++ b/src/widgets/qtabbar.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Definition of TQTab and TQTabBar classes +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQTABBAR_H +#define TQTABBAR_H + +#ifndef QT_H +#include "qwidget.h" +#include "qptrlist.h" +#endif // QT_H + +#ifndef QT_NO_TABBAR + +class TQTabBar; +class TQIconSet; + +class Q_EXPORT TQTab : public TQt +{ + friend class TQTabBar; + friend class TQTabWidget; + +public: + TQTab(); + virtual ~TQTab(); + TQTab( const TQString& text ); + TQTab( const TQIconSet& icon, const TQString& text = TQString::null ); + + void setText( const TQString& text); + TQString text() const { return label; } + void setIconSet( const TQIconSet& icon ); + TQIconSet* iconSet() const { return iconset; } + void setRect( const TQRect& rect ) { r = rect; } + TQRect 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( TQTabBar *tb ); + TQString label; + TQRect r; // the bounding rectangle of this (may overlap with others) + bool enabled; + int id; + TQIconSet* iconset; // optional iconset + TQTabBar *tb; +}; + + +struct TQTabPrivate; +//class *TQAccel; + +class Q_EXPORT TQTabBar: public TQWidget +{ + 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: + TQTabBar( TQWidget* parent=0, const char* name=0 ); + ~TQTabBar(); + + enum Shape { RoundedAbove, RoundedBelow, + TriangularAbove, TriangularBelow }; + + Shape shape() const; + virtual void setShape( Shape ); + + void show(); + + virtual int addTab( TQTab * ); + virtual int insertTab( TQTab *, int index = -1 ); + virtual void removeTab( TQTab * ); + + virtual void setTabEnabled( int, bool ); + bool isTabEnabled( int ) const; + + + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + + int currentTab() const; + int keyboardFocusTab() const; + + TQTab * tab( int ) const; + TQTab * tabAt( int ) const; + int indexOf( int ) const; + int count() const; + + virtual void layoutTabs(); + virtual TQTab * selectTab( const TQPoint & p ) const; + + void removeToolTip( int index ); + void setToolTip( int index, const TQString & tip ); + TQString toolTip( int index ) const; + +public slots: + virtual void setCurrentTab( int ); + virtual void setCurrentTab( TQTab * ); + +signals: + void selected( int ); + void layoutChanged(); + +protected: + virtual void paint( TQPainter *, TQTab *, bool ) const; // ### not const + virtual void paintLabel( TQPainter*, const TQRect&, TQTab*, bool ) const; + + void focusInEvent( TQFocusEvent *e ); + void focusOutEvent( TQFocusEvent *e ); + + void resizeEvent( TQResizeEvent * ); + void paintEvent( TQPaintEvent * ); + void mousePressEvent ( TQMouseEvent * ); + void mouseMoveEvent ( TQMouseEvent * ); + void mouseReleaseEvent ( TQMouseEvent * ); + void keyPressEvent( TQKeyEvent * ); + void styleChange( TQStyle& ); + void fontChange ( const TQFont & ); + + bool event( TQEvent *e ); + + TQPtrList<TQTab> * tabList(); + +private slots: + void scrollTabs(); + +private: + TQPtrList<TQTab> * l; + TQPtrList<TQTab> * lstatic; + void makeVisible( TQTab* t = 0 ); + void updateArrowButtons(); + TQTabPrivate * d; + + friend class TQTabBarToolTip; + friend class TQTab; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQTabBar( const TQTabBar & ); + TQTabBar& operator=( const TQTabBar & ); +#endif +}; + + +#endif // QT_NO_TABBAR + +#endif // TQTABBAR_H diff --git a/src/widgets/qtabwidget.cpp b/src/widgets/qtabwidget.cpp new file mode 100644 index 000000000..2c85a611a --- /dev/null +++ b/src/widgets/qtabwidget.cpp @@ -0,0 +1,1097 @@ +/**************************************************************************** +** +** Implementation of TQTabWidget class +** +** Created : 990318 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQTabWidget qtabwidget.h + \brief The TQTabWidget 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 TQTabWidget is to do the following in the + constructor: + \list 1 + \i Create a TQTabWidget. + \i Create a TQWidget 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 TQTabWidget is already visible, + then the page you have created will not be visible. Don't + confuse the object name you supply to the TQWidget 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 TQTabDialog for + some design hints. An alternative is to use a TQWidgetStack for + which you provide some means of navigating between pages, for + example, a TQToolBar or a TQListBox. + + Most of the functionality in TQTabWidget is provided by a TQTabBar + (at the top, providing the tabs) and a TQWidgetStack (most of the + area, organizing the individual pages). + + <img src=qtabwidget-m.png> <img src=qtabwidget-w.png> + + \sa TQTabDialog, TQToolBox +*/ + +/*! + \enum TQt::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 TQTabWidget::TabPosition + + This enum type defines where TQTabWidget draws the tab row: + \value Top above the pages + \value Bottom below the pages +*/ + +/*! + \enum TQTabWidget::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 TQTabWidget::selected( const TQString &tabLabel ); + + This signal is emitted whenever a tab is selected (raised), + including during the first show(). + + \sa raise() +*/ + + +/*! + \fn void TQTabWidget::currentChanged( TQWidget* ); + + This signal is emitted whenever the current page changes. The + parameter is the new current page. + + \sa currentPage(), showPage(), tabLabel() +*/ + +class TQTabBarBase : public TQWidget +{ +public: + TQTabBarBase( TQTabWidget* parent=0, const char* name=0 ) + : TQWidget( parent, name ) {}; +protected: + void paintEvent( TQPaintEvent * ) + { + TQObject * obj = parent(); + if( obj ){ + TQTabWidget * t = (TQTabWidget *) obj; + TQPainter p( this ); + TQStyle::SFlags flags = TQStyle::Style_Default; + + if ( t->tabPosition() == TQTabWidget::Top ) + flags |= TQStyle::Style_Top; + if ( t->tabPosition() == TQTabWidget::Bottom ) + flags |= TQStyle::Style_Bottom; + if(parentWidget()->isEnabled()) + flags |= TQStyle::Style_Enabled; + + style().drawPrimitive( TQStyle::PE_TabBarBase, &p, rect(), + colorGroup(), flags ); + } + } +}; + +class TQTabWidgetData +{ +public: + TQTabWidgetData() + : tabs(0), tabBase(0), stack(0), dirty( TRUE ), + pos( TQTabWidget::Top ), shape( TQTabWidget::Rounded ), + leftCornerWidget(0), rightCornerWidget(0) {}; + ~TQTabWidgetData(){}; + TQTabBar* tabs; + TQTabBarBase* tabBase; + TQWidgetStack* stack; + bool dirty; + TQTabWidget::TabPosition pos; + TQTabWidget::TabShape shape; + int alignment; + TQWidget* leftCornerWidget; + TQWidget* rightCornerWidget; +}; + +/*! + Constructs a tabbed widget called \a name with parent \a parent, + and widget flags \a f. +*/ +TQTabWidget::TQTabWidget( TQWidget *parent, const char *name, WFlags f ) + : TQWidget( parent, name, f ) +{ + d = new TQTabWidgetData; + d->stack = new TQWidgetStack( this, "tab pages" ); + d->stack->installEventFilter( this ); + d->tabBase = new TQTabBarBase( this, "tab base" ); + d->tabBase->resize( 1, 1 ); + setTabBar( new TQTabBar( this, "tab control" ) ); + + d->stack->setFrameStyle( TQFrame::TabWidgetPanel | TQFrame::Raised ); +#ifdef Q_OS_TEMP + d->pos = Bottom; +#endif + + setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Expanding ) ); + setFocusPolicy( TabFocus ); + setFocusProxy( d->tabs ); + + installEventFilter( this ); +#ifdef Q_OS_MACX + if (::qt_cast<TQMacStyle*>(&style())) + setMargin(10); // According to HIGuidelines at least. +#endif +} + +/*! + \reimp +*/ +TQTabWidget::~TQTabWidget() +{ + 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 TQTabWidget::addTab( TQWidget *child, const TQString &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 TQTabWidget::addTab( TQWidget *child, const TQIconSet& iconset, const TQString &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 TQTabBar subclass with an overridden + TQTabBar::paint() function for a subclass of TQTab. The \a child is + the new page and \a tab is the tab to put the \a child on. +*/ +void TQTabWidget::addTab( TQWidget *child, TQTab* 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 TQTabWidget::insertTab( TQWidget *child, const TQString &label, int index) +{ + TQTab * t = new TQTab(); + 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 TQTabWidget::insertTab( TQWidget *child, const TQIconSet& iconset, const TQString &label, int index ) +{ + TQTab * t = new TQTab(); + Q_CHECK_PTR( t ); + t->label = label; + t->iconset = new TQIconSet( 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 TQTabBar subclass with an overridden + TQTabBar::paint() function for a subclass of TQTab. 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 TQTabWidget::insertTab( TQWidget *child, TQTab* tab, int index) +{ + tab->enabled = TRUE; + int id = d->tabs->insertTab( tab, index ); + d->stack->addWidget( child, id ); + if ( d->stack->frameStyle() != ( TQFrame::TabWidgetPanel | TQFrame::Raised ) ) + d->stack->setFrameStyle( TQFrame::TabWidgetPanel | TQFrame::Raised ); + setUpLayout(); +} + + +/*! + Defines a new \a label for page \a{w}'s tab. +*/ +void TQTabWidget::changeTab( TQWidget *w, const TQString &label) +{ + int id = d->stack->id( w ); + if ( id < 0 ) + return; + TQTab* 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 TQTabWidget::changeTab( TQWidget *w, const TQIconSet& iconset, const TQString &label) +{ + int id = d->stack->id( w ); + if ( id < 0 ) + return; + TQTab* 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 TQIconSet( 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(), TQWidget::isEnabled() +*/ + +bool TQTabWidget::isTabEnabled( TQWidget* 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. + + TQTabWidget uses TQWidget::setEnabled() internally, rather than + keeping a separate flag. + + Note that even a disabled tab/page may be visible. If the page is + visible already, TQTabWidget will not hide it; if all the pages are + disabled, TQTabWidget will show one of them. + + \sa isTabEnabled(), TQWidget::setEnabled() +*/ + +void TQTabWidget::setTabEnabled( TQWidget* 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 TQTabWidget::setCornerWidget( TQWidget * w, TQt::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. +*/ +TQWidget * TQTabWidget::cornerWidget( TQt::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 TQTabBar::setCurrentTab() +*/ +void TQTabWidget::showPage( TQWidget * 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() != ( TQFrame::TabWidgetPanel |TQFrame::Raised ) ) + d->stack->setFrameStyle( TQFrame::TabWidgetPanel | TQFrame::Raised ); + } +} + +/*! + Removes page \a w from this stack of widgets. Does not delete \a + w. + + \sa addTab(), showPage(), TQWidgetStack::removeWidget() +*/ +void TQTabWidget::removePage( TQWidget * 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( TQFrame::NoFrame ); + } +} + +/*! + Returns the label text for the tab on page \a w. +*/ + +TQString TQTabWidget::tabLabel( TQWidget * w ) const +{ + TQTab * t = d->tabs->tab( d->stack->id( w ) ); + return t ? t->label : TQString::null; +} + +/*! + Sets the tab label for page \a w to \a l +*/ + +void TQTabWidget::setTabLabel( TQWidget * w, const TQString &l ) +{ + TQTab * 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). +*/ + +TQWidget * TQTabWidget::currentPage() const +{ + return page( currentPageIndex() ); +} + +/*! + \property TQTabWidget::autoMask + \brief whether the tab widget is automatically masked + + \sa TQWidget::setAutoMask() +*/ + +/*! + \property TQTabWidget::currentPage + \brief the index position of the current tab page + + \sa TQTabBar::currentTab() +*/ + +int TQTabWidget::currentPageIndex() const +{ + return d->tabs->indexOf( d->tabs->currentTab() ); +} + +void TQTabWidget::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 TQTabWidget::indexOf( TQWidget* w ) const +{ + return d->tabs->indexOf( d->stack->id( w ) ); +} + + +/*! + \reimp +*/ +void TQTabWidget::resizeEvent( TQResizeEvent *e ) +{ + TQWidget::resizeEvent( e ); + setUpLayout(); +} + +/*! + Replaces the dialog's TQTabBar 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 TQTabWidget::setTabBar( TQTabBar* tb) +{ + if ( tb->parentWidget() != this ) + tb->reparent( this, TQPoint(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 TQTabBar. + + \sa setTabBar() +*/ +TQTabBar* TQTabWidget::tabBar() const +{ + return d->tabs; +} + +/*! + Ensures that the selected tab's page is visible and appropriately + sized. +*/ + +void TQTabWidget::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 TQTabWidget::setUpLayout( bool onlyCheck ) +{ + if ( onlyCheck && !d->dirty ) + return; // nothing to do + + if ( !isVisible() ) { + d->dirty = TRUE; + return; // we'll do it later + } + + TQSize t( 0, d->stack->frameWidth() ); + if ( d->tabs->isVisibleTo(this) ) + t = d->tabs->sizeHint(); + int lcw = 0; + if ( d->leftCornerWidget && d->leftCornerWidget->isVisible() ) { + TQSize 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() ) { + TQSize 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 = TQApplication::reverseLayout(); + int tabx, taby, stacky, exty, exth, overlap; + + exth = style().pixelMetric( TQStyle::PM_TabBarBaseHeight, this ); + overlap = style().pixelMetric( TQStyle::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 = (TQMAX( 0, lw - 2 ) + lcw); + int alignment = style().styleHint( TQStyle::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 = TQMIN( 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()+TQMAX(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 +*/ +TQSize TQTabWidget::sizeHint() const +{ + TQSize lc(0, 0), rc(0, 0); + + if (d->leftCornerWidget) + lc = d->leftCornerWidget->sizeHint(); + if(d->rightCornerWidget) + rc = d->rightCornerWidget->sizeHint(); + if ( !d->dirty ) { + TQTabWidget *that = (TQTabWidget*)this; + that->setUpLayout( TRUE ); + } + TQSize s( d->stack->sizeHint() ); + TQSize t( d->tabs->sizeHint() ); + if(!style().styleHint(TQStyle::SH_TabBar_PreferNoArrows, d->tabs)) + t = t.boundedTo( TQSize(200,200) ); + else + t = t.boundedTo( TQApplication::desktop()->size() ); + + TQSize sz( TQMAX( s.width(), t.width() + rc.width() + lc.width() ), + s.height() + (TQMAX( rc.height(), TQMAX(lc.height(), t.height()))) + ( d->tabBase->isVisible() ? d->tabBase->height() : 0 ) ); + return style().sizeFromContents(TQStyle::CT_TabWidget, this, sz).expandedTo(TQApplication::globalStrut()); +} + + +/*! + \reimp + + Returns a suitable minimum size for the tab widget. +*/ +TQSize TQTabWidget::minimumSizeHint() const +{ + TQSize lc(0, 0), rc(0, 0); + + if(d->leftCornerWidget) + lc = d->leftCornerWidget->minimumSizeHint(); + if(d->rightCornerWidget) + rc = d->rightCornerWidget->minimumSizeHint(); + if ( !d->dirty ) { + TQTabWidget *that = (TQTabWidget*)this; + that->setUpLayout( TRUE ); + } + TQSize s( d->stack->minimumSizeHint() ); + TQSize t( d->tabs->minimumSizeHint() ); + + TQSize sz( TQMAX( s.width(), t.width() + rc.width() + lc.width() ), + s.height() + (TQMAX( rc.height(), TQMAX(lc.height(), t.height()))) + ( d->tabBase->isVisible() ? d->tabBase->height() : 0 ) ); + return style().sizeFromContents(TQStyle::CT_TabWidget, this, sz).expandedTo(TQApplication::globalStrut()); +} + +/*! + \reimp + */ +void TQTabWidget::showEvent( TQShowEvent * ) +{ + setUpLayout(); +} + + +/*! + \property TQTabWidget::tabPosition + \brief the position of the tabs in this tab widget + + Possible values for this property are \c TQTabWidget::Top and \c + TQTabWidget::Bottom. + + \sa TabPosition +*/ +TQTabWidget::TabPosition TQTabWidget::tabPosition() const +{ + return d->pos; +} + +void TQTabWidget::setTabPosition( TabPosition pos) +{ + if (d->pos == pos) + return; + d->pos = pos; + if (d->tabs->shape() == TQTabBar::TriangularAbove || d->tabs->shape() == TQTabBar::TriangularBelow ) { + if ( pos == Bottom ) + d->tabs->setShape( TQTabBar::TriangularBelow ); + else + d->tabs->setShape( TQTabBar::TriangularAbove ); + } + else { + if ( pos == Bottom ) + d->tabs->setShape( TQTabBar::RoundedBelow ); + else + d->tabs->setShape( TQTabBar::RoundedAbove ); + } + d->tabs->layoutTabs(); + setUpLayout(); +} + +/*! + \property TQTabWidget::tabShape + \brief the shape of the tabs in this tab widget + + Possible values for this property are \c TQTabWidget::Rounded + (default) or \c TQTabWidget::Triangular. + + \sa TabShape +*/ + +TQTabWidget::TabShape TQTabWidget::tabShape() const +{ + return d->shape; +} + +void TQTabWidget::setTabShape( TabShape s ) +{ + if ( d->shape == s ) + return; + d->shape = s; + if ( d->pos == Top ) { + if ( s == Rounded ) + d->tabs->setShape( TQTabBar::RoundedAbove ); + else + d->tabs->setShape( TQTabBar::TriangularAbove ); + } else { + if ( s == Rounded ) + d->tabs->setShape( TQTabBar::RoundedBelow ); + else + d->tabs->setShape( TQTabBar::TriangularBelow ); + } + d->tabs->layoutTabs(); + setUpLayout(); +} + + +/*! + \property TQTabWidget::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 TQTabWidget::margin() const +{ + return d->stack->margin(); +} + +void TQTabWidget::setMargin( int w ) +{ + d->stack->setMargin( w ); + setUpLayout(); +} + + +/*! + \reimp + */ +void TQTabWidget::styleChange( TQStyle& old ) +{ + TQWidget::styleChange( old ); + setUpLayout(); +} + + +/*! + \reimp + */ +void TQTabWidget::updateMask() +{ + if ( !autoMask() ) + return; + + TQRect r; + TQRegion reg( r ); + reg += TQRegion( d->tabs->geometry() ); + reg += TQRegion( d->stack->geometry() ); + setMask( reg ); +} + + +/*! + \reimp + */ +bool TQTabWidget::eventFilter( TQObject *o, TQEvent * e) +{ + if ( o == this ) { + if ( e->type() == TQEvent::LanguageChange || e->type() == TQEvent::LayoutHint ) { + d->dirty = TRUE; + setUpLayout(); + updateGeometry(); + } else if ( e->type() == TQEvent::KeyPress ) { + TQKeyEvent *ke = (TQKeyEvent*) e; + if ( ( ke->key() == TQt::Key_Tab || ke->key() == TQt::Key_Backtab ) && + count() > 1 && + ke->state() & TQt::ControlButton ) { + int page = currentPageIndex(); + if ( ke->key() == TQt::Key_Backtab || ke->state() & TQt::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() == TQEvent::ChildRemoved + && ( (TQChildEvent*)e )->child()->isWidgetType() ) { + removePage( (TQWidget*) ( (TQChildEvent*)e )->child() ); + return TRUE; + } else if ( e->type() == TQEvent::LayoutHint ) { + updateGeometry(); + } + } + return FALSE; +} + +/*! + Returns the tab page at index position \a index or 0 if the \a + index is out of range. +*/ +TQWidget *TQTabWidget::page( int index ) const +{ + TQTab *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 + TQString::null if the \a index is out of range. +*/ +TQString TQTabWidget::label( int index ) const +{ + TQTab *t = d->tabs->tabAt( index ); + if ( t ) + return t->label; + // else + return TQString::null; +} + +/*! + \property TQTabWidget::count + \brief the number of tabs in the tab bar +*/ +int TQTabWidget::count() const +{ + return d->tabs->count(); +} + +/*! + Returns the iconset of page \a w or a \link TQIconSet::TQIconSet() + null iconset\endlink if \a w is not a tab page or does not have an + iconset. +*/ +TQIconSet TQTabWidget::tabIconSet( TQWidget * w ) const +{ + int id = d->stack->id( w ); + if ( id < 0 ) + return TQIconSet(); + TQTab* t = d->tabs->tab( id ); + if ( !t ) + return TQIconSet(); + if ( t->iconset ) + return TQIconSet( *t->iconset ); + else + return TQIconSet(); +} + +/*! + Sets the iconset for page \a w to \a iconset. +*/ +void TQTabWidget::setTabIconSet( TQWidget * w, const TQIconSet & iconset ) +{ + int id = d->stack->id( w ); + if ( id < 0 ) + return; + TQTab* t = d->tabs->tab( id ); + if ( !t ) + return; + if ( t->iconset ) + delete t->iconset; + t->iconset = new TQIconSet( iconset ); + + d->tabs->layoutTabs(); + d->tabs->update(); + setUpLayout(); +} + +/*! + Sets the tab tool tip for page \a w to \a tip. + + \sa removeTabToolTip(), tabToolTip() +*/ +void TQTabWidget::setTabToolTip( TQWidget * w, const TQString & 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 TQString::null if no tool + tip has been set. + + \sa setTabToolTip(), removeTabToolTip() +*/ +TQString TQTabWidget::tabToolTip( TQWidget * w ) const +{ + int index = d->tabs->indexOf( d->stack->id( w ) ); + if ( index < 0 ) + return TQString(); + 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 TQTabWidget::removeTabToolTip( TQWidget * 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 000000000..6219528b4 --- /dev/null +++ b/src/widgets/qtabwidget.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Definition of TQTabWidget class +** +** Created : 990318 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQTABWIDGET_H +#define TQTABWIDGET_H + +#ifndef QT_H +#include "qwidget.h" +#include "qiconset.h" +#endif // QT_H + +#ifndef QT_NO_TABWIDGET + +class TQTabBar; +class TQTabWidgetData; +class TQTab; +class TQWidgetStack; + + +class Q_EXPORT TQTabWidget : public TQWidget +{ + 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: + TQTabWidget( TQWidget *parent = 0, const char *name = 0, WFlags f = 0 ); + ~TQTabWidget(); + + virtual void addTab( TQWidget *, const TQString & ); // ### make these inline in 4.0 + virtual void addTab( TQWidget *child, const TQIconSet& iconset, + const TQString &label ); + virtual void addTab( TQWidget *, TQTab* ); + + virtual void insertTab( TQWidget *, const TQString &, int index = -1 ); + virtual void insertTab( TQWidget *child, const TQIconSet& iconset, + const TQString &label, int index = -1 ); + virtual void insertTab( TQWidget *, TQTab*, int index = -1 ); + + void changeTab( TQWidget *, const TQString &); + void changeTab( TQWidget *child, const TQIconSet& iconset, + const TQString &label ); + + bool isTabEnabled( TQWidget * ) const; + void setTabEnabled( TQWidget *, bool ); + + void setCornerWidget( TQWidget * w, TQt::Corner corner = TQt::TopRight ); + TQWidget * cornerWidget( TQt::Corner corner = TQt::TopRight ) const; + + TQString tabLabel( TQWidget * ) const; + void setTabLabel( TQWidget *p, const TQString &l ); + + TQIconSet tabIconSet( TQWidget * w ) const; + void setTabIconSet( TQWidget * w, const TQIconSet & iconset ); + + void removeTabToolTip( TQWidget * w ); + void setTabToolTip( TQWidget * w, const TQString & tip ); + TQString tabToolTip( TQWidget * w ) const; + + TQWidget * currentPage() const; + TQWidget *page( int ) const; + TQString label( int ) const; + int currentPageIndex() const; + int indexOf( TQWidget * ) const; + + TQSize sizeHint() const; + TQSize 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( TQWidget * ); + virtual void removePage( TQWidget * ); + +protected: + void showEvent( TQShowEvent * ); + void resizeEvent( TQResizeEvent * ); + void setTabBar( TQTabBar * ); + TQTabBar* tabBar() const; + void styleChange( TQStyle& ); + void updateMask(); + bool eventFilter( TQObject *, TQEvent * ); + +signals: + void currentChanged( TQWidget * ); +#ifndef Q_QDOC + void selected( const TQString& ); +#endif + +private slots: + void showTab( int ); + +private: + TQTabWidgetData *d; + void setUpLayout( bool = FALSE ); + friend class TQTabDialog; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQTabWidget( const TQTabWidget & ); + TQTabWidget& operator=( const TQTabWidget & ); +#endif +}; + +#endif // QT_NO_TABWIDGET + +#endif // TQTABWIDGET_H diff --git a/src/widgets/qtextbrowser.cpp b/src/widgets/qtextbrowser.cpp new file mode 100644 index 000000000..9cd88a028 --- /dev/null +++ b/src/widgets/qtextbrowser.cpp @@ -0,0 +1,555 @@ +/**************************************************************************** +** +** Implementation of the TQTextBrowser class +** +** Created : 990101 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQTextBrowser qtextbrowser.h + \brief The TQTextBrowser class provides a rich text browser with hypertext navigation. + + \ingroup advanced + \ingroup helpsystem + \ingroup text + \mainclass + + This class extends TQTextEdit (in read-only mode), adding some + navigation functionality so that users can follow links in + hypertext documents. The contents of TQTextEdit is set with + setText(), but TQTextBrowser 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. + + TQTextBrowser 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 TQTextEdit::setMimeSourceFactory() you can provide your + own subclass of TQMimeSourceFactory. This makes it possible to + access data from anywhere, for example from a network or from a + database. See TQMimeSourceFactory::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. + + TQTextBrowser interprets the tags it processes in accordance with + the default style sheet. Change the style sheet with + \l{setStyleSheet()}; see TQStyleSheet for details. + + If you want to provide your users with editable rich text use + TQTextEdit. If you want a text browser without hypertext navigation + use TQTextEdit, and use TQTextEdit::setReadOnly() to disable + editing. If you just need to display a small piece of rich text + use TQSimpleRichText or TQLabel. + + <img src=qtextbrowser-m.png> <img src=qtextbrowser-w.png> +*/ + +class TQTextBrowserData +{ +public: + TQTextBrowserData():textOrSourceChanged(FALSE) {} + + TQValueStack<TQString> stack; + TQValueStack<TQString> forwardStack; + TQString home; + TQString curmain; + TQString 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 TQTextBrowser called \a name, with parent \a + parent. +*/ +TQTextBrowser::TQTextBrowser(TQWidget *parent, const char *name) + : TQTextEdit( parent, name ) +{ + setReadOnly( TRUE ); + d = new TQTextBrowserData; + + viewport()->setMouseTracking( TRUE ); +} + +/*! + \reimp +*/ +TQTextBrowser::~TQTextBrowser() +{ + delete d; +} + + +/*! + \property TQTextBrowser::source + \brief the name of the displayed document. + + This is a TQString::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 + TQMimeSourceFactory::data() for details. +*/ + +TQString TQTextBrowser::source() const +{ + if ( d->stack.isEmpty() ) + return TQString::null; + else + return d->stack.top(); +} + +/*! + \property TQTextBrowser::undoDepth + \brief This text browser's undo depth. +*/ + +/*! + \property TQTextBrowser::overwriteMode + \brief This text browser's overwrite mode. +*/ + +/*! + \property TQTextBrowser::modified + \brief Whether the contents have been modified. +*/ + +/*! + \property TQTextBrowser::readOnly + \brief Whether the contents are read only. +*/ + +/*! + \property TQTextBrowser::undoRedoEnabled + \brief Whether undo and redo are enabled. +*/ + + + +/*! + Reloads the current set source. +*/ + +void TQTextBrowser::reload() +{ + TQString s = d->curmain; + d->curmain = ""; + setSource( s ); +} + + +void TQTextBrowser::setSource(const TQString& name) +{ +#ifndef QT_NO_CURSOR + if ( isVisible() ) + qApp->setOverrideCursor( waitCursor ); +#endif + d->textOrSourceChanged = TRUE; + TQString source = name; + TQString 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); + + TQString url = mimeSourceFactory()->makeAbsolute( source, context() ); + TQString txt; + bool dosettext = FALSE; + + if ( !source.isEmpty() && url != d->curmain ) { + const TQMimeSource* m = + mimeSourceFactory()->data( source, context() ); + if ( !m ){ + qWarning("TQTextBrowser: no mimesource for %s", source.latin1() ); + } + else { + if ( !TQTextDrag::decode( m, txt ) ) { + qWarning("TQTextBrowser: cannot decode %s", source.latin1() ); + } + } + if ( isVisible() ) { + TQString firstTag = txt.left( txt.find( '>' ) + 1 ); + if ( firstTag.left( 3 ) == "<qt" && firstTag.contains( "type" ) && firstTag.contains( "detail" ) ) { + popupDetail( txt, TQCursor::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 ) + TQTextEdit::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 TQTextBrowser::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 TQTextBrowser::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 TQTextBrowser::sourceChanged( const TQString& 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 TQTextBrowser::highlighted (const TQString &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 TQTextBrowser::linkClicked( const TQString& 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 TQTextBrowser::anchorClicked( const TQString& name, const TQString &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 TQTextBrowser::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 TQTextBrowser::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 TQTextBrowser::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 TQTextBrowser::keyPressEvent( TQKeyEvent * e ) +{ + if ( e->state() & AltButton ) { + switch (e->key()) { + case Key_Right: + forward(); + return; + case Key_Left: + backward(); + return; + case Key_Up: + home(); + return; + } + } + TQTextEdit::keyPressEvent(e); +} + +class TQTextDetailPopup : public TQWidget +{ +public: + TQTextDetailPopup() + : TQWidget ( 0, "automatic TQText detail widget", WType_Popup | WDestructiveClose ) + { + } + +protected: + + void mousePressEvent( TQMouseEvent*) + { + close(); + } +}; + + +void TQTextBrowser::popupDetail( const TQString& contents, const TQPoint& pos ) +{ + + const int shadowWidth = 6; // also used as '5' and '6' and even '8' below + const int vMargin = 8; + const int hMargin = 12; + + TQWidget* popup = new TQTextDetailPopup; + popup->setBackgroundMode( TQWidget::NoBackground ); + + TQSimpleRichText* doc = new TQSimpleRichText( contents, popup->font() ); + doc->adjustSize(); + TQRect 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() > TQApplication::desktop()->width()) + popup->move( TQApplication::desktop()->width() - popup->width(), + popup->y() ); + if (popup->geometry().bottom() > TQApplication::desktop()->height()) + popup->move( popup->x(), + TQApplication::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. + + TQPainter p( popup ); + p.setPen( TQApplication::palette().active().foreground() ); + p.drawRect( 0, 0, w, h ); + p.setPen( TQApplication::palette().active().mid() ); + p.setBrush( TQColor( 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 TQTextBrowser::setText( const TQString &txt ) + + \overload + + Sets the text to \a txt. +*/ + +/*! + \reimp +*/ + +void TQTextBrowser::setText( const TQString &txt, const TQString &context ) +{ + d->textOrSourceChanged = TRUE; + d->curmark = ""; + d->curmain = ""; + TQTextEdit::setText( txt, context ); +} + +void TQTextBrowser::emitHighlighted( const TQString &s ) +{ + emit highlighted( s ); +} + +void TQTextBrowser::emitLinkClicked( const TQString &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 000000000..5a245b4e3 --- /dev/null +++ b/src/widgets/qtextbrowser.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Definition of the TQTextBrowser class +** +** Created : 990101 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQTEXTBROWSER_H +#define TQTEXTBROWSER_H + +#ifndef QT_H +#include "qptrlist.h" +#include "qpixmap.h" +#include "qcolor.h" +#include "qtextedit.h" +#endif // QT_H + +#ifndef QT_NO_TEXTBROWSER + +class TQTextBrowserData; + +class Q_EXPORT TQTextBrowser : public TQTextEdit +{ + Q_OBJECT + Q_PROPERTY( TQString 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: + TQTextBrowser( TQWidget* parent=0, const char* name=0 ); + ~TQTextBrowser(); + + TQString source() const; + +public slots: + virtual void setSource(const TQString& name); + virtual void backward(); + virtual void forward(); + virtual void home(); + virtual void reload(); + void setText( const TQString &txt ) { setText( txt, TQString::null ); } + virtual void setText( const TQString &txt, const TQString &context ); + +signals: + void backwardAvailable( bool ); + void forwardAvailable( bool ); + void sourceChanged( const TQString& ); + void highlighted( const TQString& ); + void linkClicked( const TQString& ); + void anchorClicked( const TQString&, const TQString& ); + +protected: + void keyPressEvent( TQKeyEvent * e); + +private: + void popupDetail( const TQString& contents, const TQPoint& pos ); + bool linksEnabled() const { return TRUE; } + void emitHighlighted( const TQString &s ); + void emitLinkClicked( const TQString &s ); + TQTextBrowserData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQTextBrowser( const TQTextBrowser & ); + TQTextBrowser& operator=( const TQTextBrowser & ); +#endif +}; + +#endif // QT_NO_TEXTBROWSER + +#endif // TQTEXTBROWSER_H diff --git a/src/widgets/qtextedit.cpp b/src/widgets/qtextedit.cpp new file mode 100644 index 000000000..09466f459 --- /dev/null +++ b/src/widgets/qtextedit.cpp @@ -0,0 +1,7467 @@ +/**************************************************************************** +** +** Implementation of the TQTextEdit class +** +** Created : 990101 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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" + TQString(TQKeySequence( TQt::CTRL | TQt::Key_ ## k )) +#else +#define ACCEL_KEY(k) "\t" + TQString("Ctrl+" #k) +#endif + +#ifdef QT_TEXTEDIT_OPTIMIZATION +#define LOGOFFSET(i) d->logOffset + i +#endif + +struct TQUndoRedoInfoPrivate +{ + TQTextString text; +}; + +class TQTextEditPrivate +{ +public: + TQTextEditPrivate() + :preeditStart(-1),preeditLength(-1),ensureCursorVisibleInShowEvent(FALSE), + tabChangesFocus(FALSE), +#ifndef QT_NO_CLIPBOARD + clipboard_mode( TQClipboard::Clipboard ), +#endif +#ifdef QT_TEXTEDIT_OPTIMIZATION + od(0), optimMode(FALSE), + maxLogLines(-1), + logOffset(0), +#endif + autoFormatting( (uint)TQTextEdit::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; + TQString scrollToAnchor; // used to deferr scrollToAnchor() until the show event when we are resized + TQString pressedName; + TQString onName; +#ifndef QT_NO_CLIPBOARD + TQClipboard::Mode clipboard_mode; +#endif + TQTimer *trippleClickTimer; + TQPoint trippleClickPoint; +#ifdef QT_TEXTEDIT_OPTIMIZATION + TQTextEditOptimPrivate * od; + bool optimMode : 1; + int maxLogLines; + int logOffset; +#endif + uint autoFormatting; +}; + +#ifndef QT_NO_MIME +class TQRichTextDrag : public TQTextDrag +{ +public: + TQRichTextDrag( TQWidget *dragSource = 0, const char *name = 0 ); + + void setPlainText( const TQString &txt ) { setText( txt ); } + void setRichText( const TQString &txt ) { richTxt = txt; } + + virtual TQByteArray encodedData( const char *mime ) const; + virtual const char* format( int i ) const; + + static bool decode( TQMimeSource *e, TQString &str, const TQCString &mimetype, + const TQCString &subtype ); + static bool canDecode( TQMimeSource* e ); + +private: + TQString richTxt; + +}; + +TQRichTextDrag::TQRichTextDrag( TQWidget *dragSource, const char *name ) + : TQTextDrag( dragSource, name ) +{ +} + +TQByteArray TQRichTextDrag::encodedData( const char *mime ) const +{ + if ( qstrcmp( "application/x-qrichtext", mime ) == 0 ) { + return richTxt.utf8(); // #### perhaps we should use USC2 instead? + } else + return TQTextDrag::encodedData( mime ); +} + +bool TQRichTextDrag::decode( TQMimeSource *e, TQString &str, const TQCString &mimetype, + const TQCString &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 = TQString::fromUtf8( e->encodedData( mime ) ); + return TRUE; + } + return FALSE; + } + + // do a regular text decode + TQCString subt = subtype; + return TQTextDrag::decode( e, str, subt ); +} + +bool TQRichTextDrag::canDecode( TQMimeSource* e ) +{ + if ( e->provides( "application/x-qrichtext" ) ) + return TRUE; + return TQTextDrag::canDecode( e ); +} + +const char* TQRichTextDrag::format( int i ) const +{ + if ( TQTextDrag::format( i ) ) + return TQTextDrag::format( i ); + if ( TQTextDrag::format( i-1 ) ) + return "application/x-qrichtext"; + return 0; +} + +#endif + +static bool block_set_alignment = FALSE; + +/*! + \class TQTextEdit qtextedit.h + \brief The TQTextEdit widget provides a powerful single-page rich text editor. + + \ingroup basic + \ingroup text + \mainclass + + \tableofcontents + + \section1 Introduction and Concepts + + TQTextEdit is an advanced WYSIWYG viewer/editor supporting rich + text formatting using HTML-style tags. It is optimized to handle + large documents and to respond tquickly to user input. + + TQTextEdit 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 TQt release.</small> + + TQTextEdit can be used as a syntax highlighting editor when used in + conjunction with TQSyntaxHighlighter. + + 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. + + TQTextEdit 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 + + TQTextEdit can display images (using TQMimeSourceFactory), 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 + TQStyleSheet for details. The images identified by image tags are + displayed if they can be interpreted using the text edit's + \l{TQMimeSourceFactory}; see setMimeSourceFactory(). + + If you want a text browser with more navigation use TQTextBrowser. + If you just need to display a small piece of rich text use TQLabel + or TQSimpleRichText. + + If you create a new TQTextEdit, and want to allow the user to edit + rich text, call setTextFormat(TQt::RichText) to ensure that the + text is treated as rich text. (Rich text uses HTML tags to set + text formatting attributes. See TQStyleSheet 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(TQt::PlainText) to preserve such + text. + + Note that we do not intend to add a full-featured web browser + widget to TQt (because that would easily double TQt's size and only + a few applications would benefit from it). The rich + text support in TQt 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 TQTextEdit as a Display Widget + + TQTextEdit 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 + TQFile file( fileName ); // Read the text from a file + if ( file.open( IO_ReadOnly ) ) { + TQTextStream stream( &file ); + textEdit->setText( stream.read() ); + } + + TQFile file( fileName ); // Write the text to a file + if ( file.open( IO_WriteOnly ) ) { + TQTextStream 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 TQTextEdit provides the same functionality as the + (obsolete) TQTextView. (TQTextView is still supplied for + compatibility with old code.) + + \section2 Read-only key bindings + + When TQTextEdit 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 TQMimeSourceFactory uses to resolve + the locations of files and images. It is passed to the + mimeSourceFactory() when quering data. (See TQTextEdit() and + \l{context()}.) + + \target logtextmode + \section2 Using TQTextEdit 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 + TQTextEdit * log = new TQTextEdit( this ); + log->setTextFormat( TQt::LogText ); + TQStyleSheetItem * item = new TQStyleSheetItem( log->styleSheet(), "mytag" ); + item->setColor( "red" ); + item->setFontWeight( TQFont::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 TQStyleSheetItem 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 TQTextEdit as an Editor + + All the information about using TQTextEdit 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(). TQTextEdit 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. + + TQTextEdit 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 + TQt 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 TQTextEdit::AutoFormatting + + \value AutoNone Do not perform any automatic formatting + \value AutoBulletList Only automatically format bulletted lists + \value AutoAll Apply all available autoformatting +*/ + + +/*! + \enum TQTextEdit::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 TQTextEdit::VerticalAlignment + + This enum is used to set the vertical alignment of the text. + + \value AlignNormal Normal alignment + \value AlignSuperScript Superscript + \value AlignSubScript Subscript +*/ + +/*! + \enum TQTextEdit::TextInsertionFlags + + \internal + + \value RedoIndentation + \value CheckNewLines + \value RemoveSelected +*/ + + +/*! + \fn void TQTextEdit::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 TQTextEdit::textChanged() + + This signal is emitted whenever the text in the text edit changes. + + \sa setText() append() +*/ + +/*! + \fn void TQTextEdit::selectionChanged() + + This signal is emitted whenever the selection changes. + + \sa setSelection() copyAvailable() +*/ + +/*! \fn TQTextDocument *TQTextEdit::document() const + + \internal + + This function returns the TQTextDocument which is used by the text + edit. +*/ + +/*! \fn void TQTextEdit::setDocument( TQTextDocument *doc ) + + \internal + + This function sets the TQTextDocument 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 + TQTextDocument 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 TQTextEdit::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 TQt::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 TQTextEdit::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 TQTextEdit::setCurrentFont( const TQFont &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 TQTextEdit::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 TQTextEdit::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 TQTextEdit::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 TQTextEdit::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 TQTextEdit::currentFontChanged( const TQFont &f ) + + This signal is emitted if the font of the current format has + changed. + + The new font is \a f. + + \sa setCurrentFont() +*/ + +/*! + \fn void TQTextEdit::currentColorChanged( const TQColor &c ) + + This signal is emitted if the color of the current format has + changed. + + The new color is \a c. + + \sa setColor() +*/ + +/*! + \fn void TQTextEdit::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 TQTextEdit::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 TQTextEdit::cursorPositionChanged( TQTextCursor *c ) + + \internal +*/ + +/*! + \fn void TQTextEdit::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 TQTextEdit::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 TQTextEdit::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 TQTextEdit::returnPressed() + + This signal is emitted if the user pressed the Return or the Enter + key. +*/ + +/*! + \fn TQTextCursor *TQTextEdit::textCursor() const + + Returns the text edit's text cursor. + + \warning TQTextCursor is not in the public API, but in special + circumstances you might wish to use it. +*/ + +/*! + Constructs an empty TQTextEdit called \a name, with parent \a + parent. +*/ + +TQTextEdit::TQTextEdit( TQWidget *parent, const char *name ) + : TQScrollView( parent, name, WStaticContents | WNoAutoErase ), + doc( new TQTextDocument( 0 ) ), undoRedoInfo( doc ) +{ + init(); +} + +/*! + Constructs a TQTextEdit 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 TQMimeSourceFactory + 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 + TQMimeSourceFactory 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 + TQMimeSourceFactory 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". +*/ + +TQTextEdit::TQTextEdit( const TQString& text, const TQString& context, + TQWidget *parent, const char *name) + : TQScrollView( parent, name, WStaticContents | WNoAutoErase ), + doc( new TQTextDocument( 0 ) ), undoRedoInfo( doc ) +{ + init(); + setText( text, context ); +} + +/*! + \reimp +*/ + +TQTextEdit::~TQTextEdit() +{ + delete undoRedoInfo.d; + undoRedoInfo.d = 0; + delete cursor; + delete doc; +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + delete d->od; +#endif + delete d; +} + +void TQTextEdit::init() +{ + d = new TQTextEditPrivate; + 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 = TQString::null; + d->onName = TQString::null; + overWrite = FALSE; + wrapMode = WidgetWidth; + wrapWidth = -1; + wPolicy = AtWhiteSpace; + inDnD = FALSE; + doc->setFormatter( new TQTextFormatterBreakWords ); + doc->formatCollection()->defaultFormat()->setFont( TQScrollView::font() ); + doc->formatCollection()->defaultFormat()->setColor( colorGroup().color( TQColorGroup::Text ) ); + currentFormat = doc->formatCollection()->defaultFormat(); + currentAlignment = TQt::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 TQTextCursor( doc ); + + formatTimer = new TQTimer( this ); + connect( formatTimer, SIGNAL( timeout() ), + this, SLOT( formatMore() ) ); + lastFormatted = doc->firstParagraph(); + + scrollTimer = new TQTimer( this ); + connect( scrollTimer, SIGNAL( timeout() ), + this, SLOT( autoScrollTimerDone() ) ); + + interval = 0; + changeIntervalTimer = new TQTimer( this ); + connect( changeIntervalTimer, SIGNAL( timeout() ), + this, SLOT( doChangeInterval() ) ); + + cursorVisible = TRUE; + blinkTimer = new TQTimer( this ); + connect( blinkTimer, SIGNAL( timeout() ), + this, SLOT( blinkCursor() ) ); + +#ifndef QT_NO_DRAGANDDROP + dragStartTimer = new TQTimer( this ); + connect( dragStartTimer, SIGNAL( timeout() ), + this, SLOT( startDrag() ) ); +#endif + + d->trippleClickTimer = new TQTimer( 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 TQTextEdit::paintDocument( bool drawAll, TQPainter *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( TQStyle::SH_BlinkCursorWhenTextSelected ) ) || + isReadOnly() || !cursorVisible || doc->hasSelection( TQTextDocument::IMSelectionText )) + drawCur = FALSE; + TQColorGroup g = colorGroup(); + const TQColorGroup::ColorRole backRole = TQPalette::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 TQTextEdit::drawContents( TQPainter *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 retquired to follow dragging of active window + // by the showed candidate window. + updateMicroFocusHint(); +} + +/*! + \reimp +*/ + +void TQTextEdit::drawContents( TQPainter *p ) +{ + if ( horizontalScrollBar()->isVisible() && + verticalScrollBar()->isVisible() ) { + const TQRect verticalRect = verticalScrollBar()->geometry(); + const TQRect horizontalRect = horizontalScrollBar()->geometry(); + + TQRect cornerRect; + cornerRect.setTop( verticalRect.bottom() ); + cornerRect.setBottom( horizontalRect.bottom() ); + cornerRect.setLeft( verticalRect.left() ); + cornerRect.setRight( verticalRect.right() ); + + p->fillRect( cornerRect, colorGroup().background() ); + } +} + +/*! + \reimp +*/ + +bool TQTextEdit::event( TQEvent *e ) +{ + if ( e->type() == TQEvent::AccelOverride && !isReadOnly() ) { + TQKeyEvent* ke = (TQKeyEvent*) 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() == TQEvent::Show ) { + if ( +#ifdef QT_TEXTEDIT_OPTIMIZATION + !d->optimMode && +#endif + d->ensureCursorVisibleInShowEvent ) { + ensureCursorVisible(); + d->ensureCursorVisibleInShowEvent = FALSE; + } + if ( !d->scrollToAnchor.isEmpty() ) { + scrollToAnchor( d->scrollToAnchor ); + d->scrollToAnchor = TQString::null; + } + } + return TQWidget::event( e ); +} + +/*! + Processes the key event, \a e. By default key events are used to + provide keyboard navigation and text editing. +*/ + +void TQTextEdit::keyPressEvent( TQKeyEvent *e ) +{ + changeIntervalTimer->stop(); + interval = 10; + bool unknownKey = FALSE; + if ( isReadOnly() ) { + if ( !handleReadOnlyKeyEvent( e ) ) + TQScrollView::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( TQTextDocument::Standard, FALSE ) ) + removeSelectedText(); + if ( textFormat() == TQt::RichText && ( e->state() & ControlButton ) ) { + // Ctrl-Enter inserts a line break in rich text mode + insert( TQString( TQChar( 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( TQTextDocument::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( TQTextDocument::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() == TQt::PlainText ) { + // change the whole doc + TQTextParagraph *p = doc->firstParagraph(); + while ( p ) { + p->setDirection( TQChar::DirL ); + p->setAlignment( TQt::AlignLeft ); + p->invalidate( 0 ); + p = p->next(); + } + } else { + if ( !cursor->paragraph() || cursor->paragraph()->direction() == TQChar::DirL ) + return; + cursor->paragraph()->setDirection( TQChar::DirL ); + if ( cursor->paragraph()->length() <= 1&& + ( (cursor->paragraph()->alignment() & (TQt::AlignLeft | TQt::AlignRight) ) != 0 ) ) + setAlignment( TQt::AlignLeft ); + } + repaintChanged(); + break; + case Key_Direction_R: + if ( doc->textFormat() == TQt::PlainText ) { + // change the whole doc + TQTextParagraph *p = doc->firstParagraph(); + while ( p ) { + p->setDirection( TQChar::DirR ); + p->setAlignment( TQt::AlignRight ); + p->invalidate( 0 ); + p = p->next(); + } + } else { + if ( !cursor->paragraph() || cursor->paragraph()->direction() == TQChar::DirR ) + return; + cursor->paragraph()->setDirection( TQChar::DirR ); + if ( cursor->paragraph()->length() <= 1&& + ( (cursor->paragraph()->alignment() & (TQt::AlignLeft | TQt::AlignRight) ) != 0 ) ) + setAlignment( TQt::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() == TQt::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 = TQTextStyleCommand::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() == TQt::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 = TQTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + setParagType( TQStyleSheetItem::DisplayListItem, TQStyleSheetItem::ListDisc ); + clearUndoRedo(); + drawCursor( FALSE ); + repaintChanged(); + drawCursor( TRUE ); + break; + } + if (overWrite && !cursor->atParagEnd() && !doc->hasSelection(TQTextDocument::Standard)) { + doKeyboardAction(ActionDelete); + clearUndoRedoInfo = FALSE; + } + TQString 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 + TQTextParagraph *p = cursor->paragraph(); + if ( p && p->string() && p->string()->isRightToLeft() ) { + TQChar *c = (TQChar *)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( TQTextDocument::Standard ) ) { + removeSelectedText(); + break; + } + doKeyboardAction( ActionDelete ); + clearUndoRedoInfo = FALSE; + break; + case Key_H: + if ( doc->hasSelection( TQTextDocument::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 TQWidget::sendMouseEventToInputContext() easily for this + class. + */ +bool TQTextEdit::sendMouseEventToInputContext( TQMouseEvent *e ) +{ +#ifndef QT_NO_IM + if ( d->composeMode() ) { + TQTextCursor 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 ) { + TQWidget::sendMouseEventToInputContext( mousePos, e->type(), + e->button(), e->state() ); + } + } else if ( e->type() != TQEvent::MouseMove ) { + // send button events on out of preedit + TQWidget::sendMouseEventToInputContext( -1, e->type(), + e->button(), e->state() ); + } + return TRUE; + } +#endif + return FALSE; +} + + +/*! + \reimp +*/ +void TQTextEdit::imStartEvent( TQIMEvent *e ) +{ + if ( isReadOnly() ) { + e->ignore(); + return; + } + + if ( hasSelectedText() ) + removeSelectedText(); + d->preeditStart = cursor->index(); + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::IME; +} + +/*! + \reimp +*/ +void TQTextEdit::imComposeEvent( TQIMEvent *e ) +{ + if ( isReadOnly() ) { + e->ignore(); + return; + } + + doc->removeSelection( TQTextDocument::IMCompositionText ); + doc->removeSelection( TQTextDocument::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 ); + TQTextCursor c = *cursor; + cursor->setIndex( d->preeditStart ); + doc->setSelectionStart( TQTextDocument::IMCompositionText, *cursor ); + doc->setSelectionEnd( TQTextDocument::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( TQTextDocument::IMSelectionText, *cursor ); + doc->setSelectionEnd( TQTextDocument::IMSelectionText, c ); +#if 0 + // Disabled for Asian input method that shows candidate + // window. This behavior is same as TQt/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 TQTextEdit::imEndEvent( TQIMEvent *e ) +{ + if ( isReadOnly() ) { + e->ignore(); + return; + } + + doc->removeSelection( TQTextDocument::IMCompositionText ); + doc->removeSelection( TQTextDocument::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: TQt 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 TQTextEdit::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 = TQString::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( TQTextDocument::Temp, *cursor ); + if ( action == ActionWordDelete && !cursor->atParagEnd() ) { + cursor->gotoNextWord(); + } else { + cursor->gotoNextLetter(); + } + doc->setSelectionEnd( TQTextDocument::Temp, *cursor ); + removeSelectedText( TQTextDocument::Temp ); + } + break; + case ActionWordBackspace: + case ActionBackspace: + if ( textFormat() == TQt::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 = TQTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + } + int ldepth = cursor->paragraph()->listDepth(); + if ( cursor->paragraph()->isListItem() && ldepth == 1 ) { + cursor->paragraph()->setListItem( FALSE ); + } else if ( TQMAX( 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 = TQString::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( TQTextDocument::Temp, *cursor ); + if ( action == ActionWordBackspace && !cursor->atParagStart() ) { + cursor->gotoPreviousWord(); + } else { + cursor->gotoPreviousLetter(); + } + doc->setSelectionEnd( TQTextDocument::Temp, *cursor ); + removeSelectedText( TQTextDocument::Temp ); + } + break; + case ActionReturn: + if ( undoEnabled ) { + checkUndoRedoInfo( UndoRedoInfo::Return ); + if ( !undoRedoInfo.valid() ) { + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.index = cursor->index(); + undoRedoInfo.d->text = TQString::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( TQTextDocument::Temp, *cursor ); + if ( cursor->atParagEnd() ) + cursor->gotoNextLetter(); + else + cursor->setIndex( cursor->paragraph()->length() - 1 ); + doc->setSelectionEnd( TQTextDocument::Temp, *cursor ); + removeSelectedText( TQTextDocument::Temp ); + break; + } + + formatMore(); + repaintChanged(); + ensureCursorVisible(); + drawCursor( TRUE ); + updateMicroFocusHint(); + if ( doUpdateCurrentFormat ) + updateCurrentFormat(); + setModified(); + emit textChanged(); +} + +void TQTextEdit::readFormats( TQTextCursor &c1, TQTextCursor &c2, TQTextString &text, bool fillStyles ) +{ +#ifndef QT_NO_DATASTREAM + TQDataStream 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 ( TQTextParagraph *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 TQTextEdit::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 TQTextEdit::removeSelectedText( int selNum ) +{ + if(selNum != 0) + resetInputContext(); + + TQTextCursor c1 = doc->selectionStartCursor( selNum ); + c1.restoreState(); + TQTextCursor 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 = TQString::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 TQTextCursor( doc ); + drawCursor( TRUE ); + repaintContents( TRUE ); + } + setModified(); + emit textChanged(); + emit selectionChanged(); + emit copyAvailable( doc->hasSelection( TQTextDocument::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 TQTextEdit::moveCursor( CursorAction action, bool select ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return; +#endif +#ifdef Q_WS_MACX + TQTextCursor c1 = *cursor; + TQTextCursor c2; +#endif + drawCursor( FALSE ); + if ( select ) { + if ( !doc->hasSelection( TQTextDocument::Standard ) ) + doc->setSelectionStart( TQTextDocument::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( TQTextDocument::Standard, *cursor ) ) { + cursor->paragraph()->document()->nextDoubleBuffered = TRUE; + repaintChanged(); + } else { + drawCursor( TRUE ); + } + ensureCursorVisible(); + emit selectionChanged(); + emit copyAvailable( doc->hasSelection( TQTextDocument::Standard ) ); + } else { +#ifdef Q_WS_MACX + TQTextCursor cStart = doc->selectionStartCursor( TQTextDocument::Standard ); + TQTextCursor cEnd = doc->selectionEndCursor( TQTextDocument::Standard ); + bool redraw = doc->removeSelection( TQTextDocument::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( TQTextDocument::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( TQTextDocument::Standard ) ); + emit selectionChanged(); + } + } + + drawCursor( TRUE ); + updateCurrentFormat(); + updateMicroFocusHint(); +} + +/*! + \overload +*/ + +void TQTextEdit::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 TQTextEdit::resizeEvent( TQResizeEvent *e ) +{ + TQScrollView::resizeEvent( e ); + if ( doc->visibleWidth() == 0 ) + doResize(); +} + +/*! + \reimp +*/ + +void TQTextEdit::viewportResizeEvent( TQResizeEvent *e ) +{ + TQScrollView::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 TQTextEdit::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(); + TQTextStringChar *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 TQTextEdit::sliderReleased() +{ + if ( d->ensureCursorVisibleInShowEvent && isVisible() ) { + d->ensureCursorVisibleInShowEvent = FALSE; + ensureCursorVisible(); + } +} + +/*! + \internal +*/ +void TQTextEdit::drawCursor( bool visible ) +{ + if ( !isUpdatesEnabled() || + !viewport()->isUpdatesEnabled() || + !cursor->paragraph() || + !cursor->paragraph()->isValid() || + ( !style().styleHint( TQStyle::SH_BlinkCursorWhenTextSelected ) && + ( d->optimMode ? optimHasSelection() : doc->hasSelection( TQTextDocument::Standard, TRUE ))) || + ( visible && !hasFocus() && !viewport()->hasFocus() && !inDnD ) || + doc->hasSelection( TQTextDocument::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( TQTextDocument::IMSelectionText ) ) { + visible = FALSE; + } + + TQPainter p( viewport() ); + TQRect r( cursor->topParagraph()->rect() ); + cursor->paragraph()->setChanged( TRUE ); + p.translate( -contentsX() + cursor->totalOffsetX(), -contentsY() + cursor->totalOffsetY() ); + TQPixmap *pix = 0; + TQColorGroup cg( colorGroup() ); + const TQColorGroup::ColorRole backRole = TQPalette::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() & TQt::AlignJustify ) == TQt::AlignJustify ) + dist = 50; + int x = r.x() - cursor->totalOffsetX() + cursor->x() - dist; + x = TQMAX( x, 0 ); + p.setClipRect( TQRect( 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 TQTextEdit::contentsWheelEvent( TQWheelEvent *e ) +{ + if ( isReadOnly() ) { + if ( e->state() & ControlButton ) { + if ( e->delta() > 0 ) + zoomOut(); + else if ( e->delta() < 0 ) + zoomIn(); + return; + } + } + TQScrollView::contentsWheelEvent( e ); +} +#endif + +/*! + \reimp +*/ + +void TQTextEdit::contentsMousePressEvent( TQMouseEvent *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() < + TQApplication::startDragDistance() ) { + TQTextCursor c1 = *cursor; + TQTextCursor c2 = *cursor; + c1.gotoLineStart(); + c2.gotoLineEnd(); + doc->setSelectionStart( TQTextDocument::Standard, c1 ); + doc->setSelectionEnd( TQTextDocument::Standard, c2 ); + *cursor = c2; + repaintChanged(); + mousePressed = TRUE; + return; + } + + clearUndoRedo(); + TQTextCursor oldCursor = *cursor; + TQTextCursor c = *cursor; + mousePos = e->pos(); + mightStartDrag = FALSE; + pressedLink = TQString::null; + d->pressedName = TQString::null; + + if ( e->button() == LeftButton ) { + mousePressed = TRUE; + drawCursor( FALSE ); + placeCursor( e->pos() ); + ensureCursorVisible(); + + if ( isReadOnly() && linksEnabled() ) { + TQTextCursor 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( TQTextDocument::Standard, e->pos() ) ) { + mightStartDrag = TRUE; + drawCursor( TRUE ); + dragStartTimer->start( TQApplication::startDragTime(), TRUE ); + dragStartPos = e->pos(); + return; + } +#endif + + bool redraw = FALSE; + if ( doc->hasSelection( TQTextDocument::Standard ) ) { + if ( !( e->state() & ShiftButton ) ) { + redraw = doc->removeSelection( TQTextDocument::Standard ); + doc->setSelectionStart( TQTextDocument::Standard, *cursor ); + } else { + redraw = doc->setSelectionEnd( TQTextDocument::Standard, *cursor ) || redraw; + } + } else { + if ( isReadOnly() || !( e->state() & ShiftButton ) ) { + doc->setSelectionStart( TQTextDocument::Standard, *cursor ); + } else { + doc->setSelectionStart( TQTextDocument::Standard, c ); + redraw = doc->setSelectionEnd( TQTextDocument::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( TQTextDocument::Standard ); + if ( !redraw ) { + drawCursor( TRUE ); + } else { + repaintChanged(); +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + } + } + + if ( *cursor != oldCursor ) + updateCurrentFormat(); +} + +/*! + \reimp +*/ + +void TQTextEdit::contentsMouseMoveEvent( TQMouseEvent *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() > TQApplication::startDragDistance() ) { + TQGuardedPtr<TQTextEdit> 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( TQTextDocument::Standard ) && doc->inSelection( TQTextDocument::Standard, e->pos() ) ) + viewport()->setCursor( arrowCursor ); + else + viewport()->setCursor( ibeamCursor ); + } +#endif + updateCursor( e->pos() ); +} + +void TQTextEdit::copyToClipboard() +{ +#ifndef QT_NO_CLIPBOARD + if (TQApplication::clipboard()->supportsSelection()) { + d->clipboard_mode = TQClipboard::Selection; + + // don't listen to selection changes + disconnect( TQApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); + copy(); + // listen to selection changes + connect( TQApplication::clipboard(), SIGNAL(selectionChanged()), + this, SLOT(clipboardChanged()) ); + + d->clipboard_mode = TQClipboard::Clipboard; + } +#endif +} + +/*! + \reimp +*/ + +void TQTextEdit::contentsMouseReleaseEvent( TQMouseEvent * 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; + TQTextCursor 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 (TQApplication::clipboard()->supportsSelection()) { + drawCursor( FALSE ); + placeCursor( e->pos() ); + ensureCursorVisible(); + doc->setSelectionStart( TQTextDocument::Standard, oldCursor ); + bool redraw = FALSE; + if ( doc->hasSelection( TQTextDocument::Standard ) ) { + redraw = doc->removeSelection( TQTextDocument::Standard ); + doc->setSelectionStart( TQTextDocument::Standard, *cursor ); + } else { + doc->setSelectionStart( TQTextDocument::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 = TQClipboard::Selection; + paste(); + d->clipboard_mode = TQClipboard::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()) { + TQUrl u( doc->context(), onLink, TRUE ); + emitLinkClicked( u.toString( FALSE, FALSE ) ); + } + if (::qt_cast<TQTextBrowser*>(this)) { // change for 4.0 + TQConnectionList *clist = receivers( + "anchorClicked(const TQString&,const TQString&)"); + if (!signalsBlocked() && clist) { + TQUObject o[3]; + static_QUType_TQString.set(o+1, d->onName); + static_QUType_TQString.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( TQTextDocument::Standard, TRUE ) ) + doc->removeSelection( TQTextDocument::Standard ); + + emit copyAvailable( doc->hasSelection( TQTextDocument::Standard ) ); + emit selectionChanged(); +} + +/*! + \reimp +*/ + +void TQTextEdit::contentsMouseDoubleClickEvent( TQMouseEvent * e ) +{ + if ( e->button() != TQt::LeftButton && !d->composeMode() ) { + e->ignore(); + return; + } + int para = 0; + int index = charAt( e->pos(), ¶ ); +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + TQString 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; + + TQTextCursor c1 = *cursor; + TQTextCursor c2 = *cursor; +#if defined(Q_OS_MAC) + TQTextParagraph *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( TQTextDocument::Standard, c1 ); + doc->setSelectionEnd( TQTextDocument::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 TQTextEdit::contentsDragEnterEvent( TQDragEnterEvent *e ) +{ + if ( isReadOnly() || !TQTextDrag::canDecode( e ) ) { + e->ignore(); + return; + } + e->acceptAction(); + inDnD = TRUE; +} + +/*! + \reimp +*/ + +void TQTextEdit::contentsDragMoveEvent( TQDragMoveEvent *e ) +{ + if ( isReadOnly() || !TQTextDrag::canDecode( e ) ) { + e->ignore(); + return; + } + drawCursor( FALSE ); + placeCursor( e->pos(), cursor ); + drawCursor( TRUE ); + e->acceptAction(); +} + +/*! + \reimp +*/ + +void TQTextEdit::contentsDragLeaveEvent( TQDragLeaveEvent * ) +{ + drawCursor( FALSE ); + inDnD = FALSE; +} + +/*! + \reimp +*/ + +void TQTextEdit::contentsDropEvent( TQDropEvent *e ) +{ + if ( isReadOnly() ) + return; + inDnD = FALSE; + e->acceptAction(); + bool intern = FALSE; + if ( TQRichTextDrag::canDecode( e ) ) { + bool hasSel = doc->hasSelection( TQTextDocument::Standard ); + bool internalDrag = e->source() == this || e->source() == viewport(); + int dropId, dropIndex; + TQTextCursor insertCursor = *cursor; + dropId = cursor->paragraph()->paragId(); + dropIndex = cursor->index(); + if ( hasSel && internalDrag ) { + TQTextCursor c1, c2; + int selStartId, selStartIndex; + int selEndId, selEndIndex; + c1 = doc->selectionStartCursor( TQTextDocument::Standard ); + c1.restoreState(); + c2 = doc->selectionEndCursor( TQTextDocument::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() == TQDropEvent::Move ) { + removeSelectedText(); + intern = TRUE; + doc->removeSelection( TQTextDocument::Standard ); + } else { + doc->removeSelection( TQTextDocument::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() ) { + TQString 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 TQTextEdit::contentsContextMenuEvent( TQContextMenuEvent *e ) +{ + e->accept(); +#ifndef QT_NO_IM + if ( d->composeMode() ) + return; +#endif + + clearUndoRedo(); + mousePressed = FALSE; + +#ifndef QT_NO_POPUPMENU + TQGuardedPtr<TQTextEdit> that = this; + TQGuardedPtr<TQPopupMenu> 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 (TQApplication::clipboard()->supportsSelection()) { + d->clipboard_mode = TQClipboard::Selection; + + // don't listen to selection changes + disconnect( TQApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); + copy(); + // listen to selection changes + connect( TQApplication::clipboard(), SIGNAL(selectionChanged()), + this, SLOT(clipboardChanged()) ); + + d->clipboard_mode = TQClipboard::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 TQTextEdit::autoScrollTimerDone() +{ + if ( mousePressed ) + handleMouseMove( viewportToContents( viewport()->mapFromGlobal( TQCursor::pos() ) ) ); +} + +void TQTextEdit::handleMouseMove( const TQPoint& 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 ); + TQTextCursor oldCursor = *cursor; + + placeCursor( pos ); + + if ( inDoubleClick ) { + TQTextCursor cl = *cursor; + cl.gotoPreviousWord(); + TQTextCursor cr = *cursor; + cr.gotoNextWord(); + + int diff = TQABS( oldCursor.paragraph()->at( oldCursor.index() )->x - mousePos.x() ); + int ldiff = TQABS( cl.paragraph()->at( cl.index() )->x - mousePos.x() ); + int rdiff = TQABS( 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( TQTextDocument::Standard ) ) { + redraw = doc->setSelectionEnd( TQTextDocument::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 TQTextEdit::placeCursor( const TQPoint &pos, TQTextCursor *c, bool link ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return; +#endif + if ( !c ) + c = cursor; + + resetInputContext(); + c->restoreState(); + TQTextParagraph *s = doc->firstParagraph(); + c->place( pos, s, link ); + updateMicroFocusHint(); +} + + +void TQTextEdit::updateMicroFocusHint() +{ + TQTextCursor c( *cursor ); +#if 0 + // Disabled for Asian input method that shows candidate + // window. This behavior is same as TQt/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(TQTextDocument::IMSelectionText)) { + int para, index; + doc->selectionStart(TQTextDocument::IMSelectionText, para, index); + c.setIndex(index); + } + } +#endif + + if ( hasFocus() || viewport()->hasFocus() ) { + int h = c.paragraph()->lineHeightOfChar( cursor->index() ); + if ( !readonly ) { + TQFont 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 TQTextEdit::formatMore() +{ + if ( !lastFormatted ) + return; + + int bottom = contentsHeight(); + int lastTop = -1; + int lastBottom = -1; + int to = 20; + bool firstVisible = FALSE; + TQRect 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 = TQMAX( bottom, lastBottom ); + lastFormatted = lastFormatted->next(); + } + + if ( bottom > contentsHeight() ) { + resizeContents( contentsWidth(), TQMAX( doc->height(), bottom ) ); + } else if ( !lastFormatted && lastBottom < contentsHeight() ) { + resizeContents( contentsWidth(), TQMAX( doc->height(), lastBottom ) ); + if ( contentsHeight() < visibleHeight() ) + updateContents( 0, contentsHeight(), visibleWidth(), + visibleHeight() - contentsHeight() ); + } + + if ( lastFormatted ) + formatTimer->start( interval, TRUE ); + else + interval = TQMAX( 0, interval ); +} + +void TQTextEdit::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 TQTextEdit::doChangeInterval() +{ + interval = 0; +} + +/*! + \reimp +*/ + +bool TQTextEdit::eventFilter( TQObject *o, TQEvent *e ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( !d->optimMode && (o == this || o == viewport()) ) { +#else + if ( o == this || o == viewport() ) { +#endif + if ( e->type() == TQEvent::FocusIn ) { + if ( TQApplication::cursorFlashTime() > 0 ) + blinkTimer->start( TQApplication::cursorFlashTime() / 2 ); + drawCursor( TRUE ); + updateMicroFocusHint(); + } else if ( e->type() == TQEvent::FocusOut ) { + blinkTimer->stop(); + drawCursor( FALSE ); + } + } + + if ( o == this && e->type() == TQEvent::PaletteChange ) { + TQColor old( viewport()->colorGroup().color( TQColorGroup::Text ) ); + if ( old != colorGroup().color( TQColorGroup::Text ) ) { + TQColor c( colorGroup().color( TQColorGroup::Text ) ); + doc->setMinimumWidth( -1 ); + doc->setDefaultFormat( doc->formatCollection()->defaultFormat()->font(), c ); + lastFormatted = doc->firstParagraph(); + formatMore(); + repaintChanged(); + } + } + + return TQScrollView::eventFilter( o, e ); +} + +/*! + \obsolete + */ +void TQTextEdit::insert( const TQString &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 TQTextEdit's newline handling + behavior, but you can use TQString::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 TQTextEdit::insert( const TQString &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; + TQString txt( text ); + drawCursor( FALSE ); + if ( !isReadOnly() && doc->hasSelection( TQTextDocument::Standard ) && removeSelected ) + removeSelectedText(); + TQTextCursor 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 = TQString::null; + } + oldLen = undoRedoInfo.d->text.length(); + } + + lastFormatted = checkNewLine && cursor->paragraph()->prev() ? + cursor->paragraph()->prev() : cursor->paragraph(); + TQTextCursor oldCursor = *cursor; + cursor->insert( txt, checkNewLine ); + if ( doc->useFormatCollection() && !doc->preProcessor() ) { + doc->setSelectionStart( TQTextDocument::Temp, oldCursor ); + doc->setSelectionEnd( TQTextDocument::Temp, *cursor ); + doc->setFormat( TQTextDocument::Temp, currentFormat, TQTextFormat::Format ); + doc->removeSelection( TQTextDocument::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( TQTextDocument::Standard, oldCursor ); + doc->setSelectionEnd( TQTextDocument::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 TQTextEdit::insertAt( const TQString &text, int para, int index ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimInsert( text, para, index ); + return; + } +#endif + resetInputContext(); + TQTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return; + removeSelection( TQTextDocument::Standard ); + TQTextCursor tmp = *cursor; + cursor->setParagraph( p ); + cursor->setIndex( index ); + insert( text, FALSE, TRUE, FALSE ); + *cursor = tmp; + removeSelection( TQTextDocument::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 TQTextEdit::insertParagraph( const TQString &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 ); + + TQTextParagraph *p = doc->paragAt( para ); + + bool append = !p; + if ( !p ) + p = doc->lastParagraph(); + + TQTextCursor 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 TQTextEdit::removeParagraph( int para ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return; +#endif + resetInputContext(); + TQTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return; + + for ( int i = 0; i < doc->numSelections(); ++i ) + doc->removeSelection( i ); + + TQTextCursor start( doc ); + TQTextCursor 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( TQTextDocument::Temp, start ); + doc->setSelectionEnd( TQTextDocument::Temp, end ); + removeSelectedText( TQTextDocument::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 TQTextEdit::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 ); + TQTextCursor *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 TQTextCursor( 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 TQTextEdit::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 ); + TQTextCursor *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() TQTextEdit::copy() +*/ + +void TQTextEdit::paste() +{ +#ifndef QT_NO_MIMECLIPBOARD + if ( isReadOnly() ) + return; + TQString subType = "plain"; + if ( textFormat() != PlainText ) { + TQMimeSource *m = TQApplication::clipboard()->data( d->clipboard_mode ); + if ( !m ) + return; + if ( m->provides( "application/x-qrichtext" ) ) + subType = "x-qrichtext"; + } + + pasteSubType( subType.latin1() ); + updateMicroFocusHint(); +#endif +} + +void TQTextEdit::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 TQTextEdit::repaintChanged() +{ + if ( !isUpdatesEnabled() || !viewport()->isUpdatesEnabled() ) + return; + + TQPainter 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 +TQTextDrag *TQTextEdit::dragObject( TQWidget *parent ) const +{ + if ( !doc->hasSelection( TQTextDocument::Standard ) || + doc->selectedText( TQTextDocument::Standard ).isEmpty() ) + return 0; + if ( textFormat() != RichText ) + return new TQTextDrag( doc->selectedText( TQTextDocument::Standard ), parent ); + TQRichTextDrag *drag = new TQRichTextDrag( parent ); + drag->setPlainText( doc->selectedText( TQTextDocument::Standard ) ); + drag->setRichText( doc->selectedText( TQTextDocument::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 TQTextEdit::copy() paste() pasteSubType() +*/ + +void TQTextEdit::cut() +{ + if ( isReadOnly() ) + return; + resetInputContext(); + normalCopy(); + removeSelectedText(); + updateMicroFocusHint(); +} + +void TQTextEdit::normalCopy() +{ +#ifndef QT_NO_MIME + TQTextDrag *drag = dragObject(); + if ( !drag ) + return; +#ifndef QT_NO_MIMECLIPBOARD + TQApplication::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 TQTextEdit::copy() +{ +#ifndef QT_NO_CLIPBOARD +# ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode && optimHasSelection() ) + TQApplication::clipboard()->setText( optimSelectedText(), d->clipboard_mode ); + else + normalCopy(); +# else + normalCopy(); +# endif +#endif +} + +/*! + \internal + + Re-indents the current paragraph. +*/ + +void TQTextEdit::indent() +{ + if ( isReadOnly() ) + return; + + drawCursor( FALSE ); + if ( !doc->hasSelection( TQTextDocument::Standard ) ) + cursor->indent(); + else + doc->indentSelection( TQTextDocument::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 TQTextEdit::focusNextPrevChild( bool n ) +{ + if ( !isReadOnly() || !linksEnabled() ) + return FALSE; + bool b = doc->focusNextPrevChild( n ); + repaintChanged(); + if ( b ) { + TQTextParagraph *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() ) { + + TQTextTable *t = (TQTextTable*)p->at( start )->customItem(); + TQPtrList<TQTextTableCell> cells = t->tableCells(); + TQTextTableCell *c; + for ( c = cells.first(); c; c = cells.next() ) { + TQTextDocument *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(), TQMIN( 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 TQTextEdit::setFormat( TQTextFormat *f, int flags ) +{ + if ( doc->hasSelection( TQTextDocument::Standard ) ) { + drawCursor( FALSE ); + TQTextCursor c1 = doc->selectionStartCursor( TQTextDocument::Standard ); + c1.restoreState(); + TQTextCursor c2 = doc->selectionEndCursor( TQTextDocument::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( TQTextDocument::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 TQTextEdit::setPalette( const TQPalette &p ) +{ + TQScrollView::setPalette( p ); + if ( textFormat() == PlainText ) { + TQTextFormat *f = doc->formatCollection()->defaultFormat(); + f->setColor( colorGroup().text() ); + updateContents(); + } +} + +/*! \internal + \warning In TQt 3.1 we will provide a cleaer API for the + functionality which is provided by this function and in TQt 4.0 this + function will go away. + + Sets the paragraph style of the current paragraph + to \a dm. If \a dm is TQStyleSheetItem::DisplayListItem, the + type of the list item is set to \a listStyle. + + \sa setAlignment() +*/ + +void TQTextEdit::setParagType( TQStyleSheetItem::DisplayMode dm, + TQStyleSheetItem::ListStyle listStyle ) +{ + if ( isReadOnly() ) + return; + + drawCursor( FALSE ); + TQTextParagraph *start = cursor->paragraph(); + TQTextParagraph *end = start; + if ( doc->hasSelection( TQTextDocument::Standard ) ) { + start = doc->selectionStartCursor( TQTextDocument::Standard ).topParagraph(); + end = doc->selectionEndCursor( TQTextDocument::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 = TQTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + + while ( start != end->next() ) { + start->setListStyle( listStyle ); + if ( dm == TQStyleSheetItem::DisplayListItem ) { + start->setListItem( TRUE ); + if( start->listDepth() == 0 ) + start->setListDepth( 1 ); + } else if ( start->isListItem() ) { + start->setListItem( FALSE ); + start->setListDepth( TQMAX( 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 TQt::AlignLeft, \c TQt::AlignRight, + \c TQt::AlignJustify and \c TQt::AlignCenter (which centers + horizontally). +*/ + +void TQTextEdit::setAlignment( int a ) +{ + if ( isReadOnly() || block_set_alignment ) + return; + + drawCursor( FALSE ); + TQTextParagraph *start = cursor->paragraph(); + TQTextParagraph *end = start; + if ( doc->hasSelection( TQTextDocument::Standard ) ) { + start = doc->selectionStartCursor( TQTextDocument::Standard ).topParagraph(); + end = doc->selectionEndCursor( TQTextDocument::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 = TQTextStyleCommand::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 TQTextEdit::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 TQTextEdit::setItalic( bool b ) +{ + TQTextFormat f( *currentFormat ); + f.setItalic( b ); + TQTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat(f2, TQTextFormat::Italic ); +} + +/*! + If \a b is TRUE sets the current format to bold; otherwise sets + the current format to non-bold. + + \sa bold() +*/ + +void TQTextEdit::setBold( bool b ) +{ + TQTextFormat f( *currentFormat ); + f.setBold( b ); + TQTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, TQTextFormat::Bold ); +} + +/*! + If \a b is TRUE sets the current format to underline; otherwise + sets the current format to non-underline. + + \sa underline() +*/ + +void TQTextEdit::setUnderline( bool b ) +{ + TQTextFormat f( *currentFormat ); + f.setUnderline( b ); + TQTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, TQTextFormat::Underline ); +} + +/*! + Sets the font family of the current format to \a fontFamily. + + \sa family() setCurrentFont() +*/ + +void TQTextEdit::setFamily( const TQString &fontFamily ) +{ + TQTextFormat f( *currentFormat ); + f.setFamily( fontFamily ); + TQTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, TQTextFormat::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 TQTextEdit::setPointSize( int s ) +{ + TQTextFormat f( *currentFormat ); + f.setPointSize( s ); + TQTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, TQTextFormat::Size ); +} + +/*! + Sets the color of the current format, i.e. of the text, to \a c. + + \sa color() setPaper() +*/ + +void TQTextEdit::setColor( const TQColor &c ) +{ + TQTextFormat f( *currentFormat ); + f.setColor( c ); + TQTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, TQTextFormat::Color ); +} + +/*! + Sets the vertical alignment of the current format, i.e. of the + text, to \a a. + + \sa color() setPaper() +*/ + +void TQTextEdit::setVerticalAlignment( VerticalAlignment a ) +{ + TQTextFormat f( *currentFormat ); + f.setVAlign( (TQTextFormat::VerticalAlignment)a ); + TQTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, TQTextFormat::VAlign ); +} + +void TQTextEdit::setFontInternal( const TQFont &f_ ) +{ + TQTextFormat f( *currentFormat ); + f.setFont( f_ ); + TQTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, TQTextFormat::Font ); +} + + +TQString TQTextEdit::text() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return optimText(); +#endif + + TQTextParagraph *p = doc->firstParagraph(); + if ( !p || (!p->next() && p->length() <= 1) ) + return TQString::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. +*/ + +TQString TQTextEdit::text( int para ) const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode && (d->od->numLines >= para) ) { + TQString 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 TQStyleSheet for details. + + The optional \a context is a path which the text edit's + TQMimeSourceFactory uses to resolve the locations of files and + images. (See \l{TQTextEdit::TQTextEdit()}.) It is passed to the text + edit's TQMimeSourceFactory when quering data. + + Note that the undo/redo history is cleared by this function. + + \sa text(), setTextFormat() +*/ + +void TQTextEdit::setText( const TQString &text, const TQString &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 TQTextCursor( 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 = TQString::null; +} + +/*! + \property TQTextEdit::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 TQTextEdit will cause + the text to be regenerated from the textedit. This may mean that + the TQString returned may not be exactly the same as the one that + was set. + + \sa textFormat +*/ + + +/*! + \property TQTextEdit::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 TQTextEdit::find( const TQString &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 + TQTextCursor findcur = *cursor; + if ( para && index ) { + if ( doc->paragAt( *para ) ) + findcur.gotoPosition( doc->paragAt(*para), *index ); + else + findcur.gotoEnd(); + } else if ( doc->hasSelection( TQTextDocument::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( TQTextDocument::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 TQTextEdit::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 TQTextEdit::setCursorPosition( int para, int index ) +{ + TQTextParagraph *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 TQTextEdit::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 TQTextEdit::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 ); + TQTextParagraph *p1 = doc->paragAt( paraFrom ); + if ( !p1 ) + return; + TQTextParagraph *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 ); + TQTextCursor c = *cursor; + TQTextCursor 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 != TQTextDocument::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 TQTextEdit::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 TQTextEdit::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 TQStyleSheet::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 TQStyleSheet::mightBeRichText() function. + \endlist +*/ + +void TQTextEdit::setTextFormat( TextFormat format ) +{ + doc->setTextFormat( format ); +#ifdef QT_TEXTEDIT_OPTIMIZATION + checkOptimMode(); +#endif +} + +TQt::TextFormat TQTextEdit::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 TQTextEdit::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 TQTextEdit::linesOfParagraph( int para ) const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + if ( d->od->numLines >= para ) + return 1; + else + return -1; + } +#endif + TQTextParagraph *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 TQTextEdit::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 + TQTextParagraph *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 TQTextEdit::lines() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + return d->od->numLines; + } +#endif + TQTextParagraph *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 TQTextEdit::lineOfChar( int para, int index ) +{ + TQTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return -1; + + int idx, line; + TQTextStringChar *c = p->lineStartOfChar( index, &idx, &line ); + if ( !c ) + return -1; + + return line; +} + +void TQTextEdit::setModified( bool m ) +{ + bool oldModified = modified; + modified = m; + if ( modified && doc->oTextValid ) + doc->invalidateOriginalText(); + if ( oldModified != modified ) + emit modificationChanged( modified ); +} + +/*! + \property TQTextEdit::modified + \brief whether the document has been modified by the user +*/ + +bool TQTextEdit::isModified() const +{ + return modified; +} + +void TQTextEdit::setModified() +{ + if ( !isModified() ) + setModified( TRUE ); +} + +/*! + Returns TRUE if the current format is italic; otherwise returns FALSE. + + \sa setItalic() +*/ + +bool TQTextEdit::italic() const +{ + return currentFormat->font().italic(); +} + +/*! + Returns TRUE if the current format is bold; otherwise returns FALSE. + + \sa setBold() +*/ + +bool TQTextEdit::bold() const +{ + return currentFormat->font().bold(); +} + +/*! + Returns TRUE if the current format is underlined; otherwise returns + FALSE. + + \sa setUnderline() +*/ + +bool TQTextEdit::underline() const +{ + return currentFormat->font().underline(); +} + +/*! + Returns the font family of the current format. + + \sa setFamily() setCurrentFont() setPointSize() +*/ + +TQString TQTextEdit::family() const +{ + return currentFormat->font().family(); +} + +/*! + Returns the point size of the font of the current format. + + \sa setFamily() setCurrentFont() setPointSize() +*/ + +int TQTextEdit::pointSize() const +{ + return currentFormat->font().pointSize(); +} + +/*! + Returns the color of the current format. + + \sa setColor() setPaper() +*/ + +TQColor TQTextEdit::color() const +{ + return currentFormat->color(); +} + +/*! + \obsolete + + Returns TQScrollView::font() + + \warning In previous versions this function returned the font of + the current format. This lead to confusion. Please use + currentFont() instead. +*/ + +TQFont TQTextEdit::font() const +{ + return TQScrollView::font(); +} + +/*! + Returns the font of the current format. + + \sa setCurrentFont() setFamily() setPointSize() +*/ + +TQFont TQTextEdit::currentFont() const +{ + return currentFormat->font(); +} + + +/*! + Returns the alignment of the current paragraph. + + \sa setAlignment() +*/ + +int TQTextEdit::alignment() const +{ + return currentAlignment; +} + +void TQTextEdit::startDrag() +{ +#ifndef QT_NO_DRAGANDDROP + mousePressed = FALSE; + inDoubleClick = FALSE; + TQDragObject *drag = dragObject( viewport() ); + if ( !drag ) + return; + if ( isReadOnly() ) { + drag->dragCopy(); + } else { + if ( drag->drag() && TQDragObject::target() != this && TQDragObject::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 TQTextEdit::selectAll( bool select ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + if ( select ) + optimSelectAll(); + else + optimRemoveSelection(); + return; + } +#endif + if ( !select ) + doc->removeSelection( TQTextDocument::Standard ); + else + doc->selectAll( TQTextDocument::Standard ); + repaintChanged(); + emit copyAvailable( doc->hasSelection( TQTextDocument::Standard ) ); + emit selectionChanged(); +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif +} + +void TQTextEdit::UndoRedoInfo::clear() +{ + if ( valid() ) { + if ( type == Insert || type == Return ) + doc->addCommand( new TQTextInsertCommand( doc, id, index, d->text.rawData(), styleInformation ) ); + else if ( type == Format ) + doc->addCommand( new TQTextFormatCommand( doc, id, index, eid, eindex, d->text.rawData(), format, flags ) ); + else if ( type == Style ) + doc->addCommand( new TQTextStyleCommand( doc, id, eid, styleInformation ) ); + else if ( type != Invalid ) { + doc->addCommand( new TQTextDeleteCommand( doc, id, index, d->text.rawData(), styleInformation ) ); + } + } + type = Invalid; + d->text = TQString::null; + id = -1; + index = -1; + styleInformation = TQByteArray(); +} + + +/*! + 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 TQTextEdit::del() +{ + if ( doc->hasSelection( TQTextDocument::Standard ) ) { + removeSelectedText(); + return; + } + + doKeyboardAction( ActionDelete ); +} + + +TQTextEdit::UndoRedoInfo::UndoRedoInfo( TQTextDocument *dc ) + : type( Invalid ), doc( dc ) +{ + d = new TQUndoRedoInfoPrivate; + d->text = TQString::null; + id = -1; + index = -1; +} + +TQTextEdit::UndoRedoInfo::~UndoRedoInfo() +{ + delete d; +} + +bool TQTextEdit::UndoRedoInfo::valid() const +{ + return id >= 0 && type != Invalid; +} + +/*! + \internal + + Resets the current format to the default format. +*/ + +void TQTextEdit::resetFormat() +{ + setAlignment( TQt::AlignAuto ); + setParagType( TQStyleSheetItem::DisplayBlock, TQStyleSheetItem::ListDisc ); + setFormat( doc->formatCollection()->defaultFormat(), TQTextFormat::Format ); +} + +/*! + Returns the TQStyleSheet which is being used by this text edit. + + \sa setStyleSheet() +*/ + +TQStyleSheet* TQTextEdit::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 TQTextEdit::setStyleSheet( TQStyleSheet* styleSheet ) +{ + doc->setStyleSheet( styleSheet ); +} + +/*! + \property TQTextEdit::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 TQTextEdit::setPaper( const TQBrush& pap ) +{ + doc->setPaper( new TQBrush( 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(); +} + +TQBrush TQTextEdit::paper() const +{ + if ( doc->paper() ) + return *doc->paper(); + return TQBrush( colorGroup().base() ); +} + +/*! + \property TQTextEdit::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 TQTextEdit::setLinkUnderline( bool b ) +{ + if ( doc->underlineLinks() == b ) + return; + doc->setUnderlineLinks( b ); + repaintChanged(); +} + +bool TQTextEdit::linkUnderline() const +{ + return doc->underlineLinks(); +} + +/*! + Sets the text edit's mimesource factory to \a factory. See + TQMimeSourceFactory for further details. + + \sa mimeSourceFactory() + */ + +#ifndef QT_NO_MIME +void TQTextEdit::setMimeSourceFactory( TQMimeSourceFactory* factory ) +{ + doc->setMimeSourceFactory( factory ); +} + +/*! + Returns the TQMimeSourceFactory which is being used by this text + edit. + + \sa setMimeSourceFactory() +*/ + +TQMimeSourceFactory* TQTextEdit::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 TQTextEdit::heightForWidth( int w ) const +{ + int oldw = doc->width(); + doc->doLayout( 0, w ); + int h = doc->height(); + doc->setWidth( oldw ); + doc->invalidate(); + ( (TQTextEdit*)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 TQTextEdit::append( const TQString &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( TQTextDocument::Standard ); + TextFormat f = doc->textFormat(); + if ( f == AutoText ) { + if ( TQStyleSheet::mightBeRichText( text ) ) + f = RichText; + else + f = PlainText; + } + + drawCursor( FALSE ); + TQTextCursor oldc( *cursor ); + ensureFormatted( doc->lastParagraph() ); + bool atBottom = contentsY() >= contentsHeight() - visibleHeight(); + cursor->gotoEnd(); + if ( cursor->index() > 0 ) + cursor->splitAndInsertEmptyParagraph(); + TQTextCursor oldCursor2 = *cursor; + + if ( f == TQt::PlainText ) { + cursor->insert( text, TRUE ); + if ( doc->useFormatCollection() && !doc->preProcessor() && + currentFormat != cursor->paragraph()->at( cursor->index() )->format() ) { + doc->setSelectionStart( TQTextDocument::Temp, oldCursor2 ); + doc->setSelectionEnd( TQTextDocument::Temp, *cursor ); + doc->setFormat( TQTextDocument::Temp, currentFormat, TQTextFormat::Format ); + doc->removeSelection( TQTextDocument::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 TQTextEdit::hasSelectedText + \brief whether some text is selected in selection 0 +*/ + +bool TQTextEdit::hasSelectedText() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return optimHasSelection(); + else +#endif + return doc->hasSelection( TQTextDocument::Standard ); +} + +/*! + \property TQTextEdit::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 +*/ + +TQString TQTextEdit::selectedText() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return optimSelectedText(); + else +#endif + return doc->selectedText( TQTextDocument::Standard, textFormat() == RichText ); +} + +bool TQTextEdit::handleReadOnlyKeyEvent( TQKeyEvent *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()) { + TQUrl u( doc->context(), doc->focusIndicator.href, TRUE ); + emitLinkClicked( u.toString( FALSE, FALSE ) ); + } + if (!doc->focusIndicator.name.isEmpty()) { + if (::qt_cast<TQTextBrowser*>(this)) { // change for 4.0 + TQConnectionList *clist = receivers( + "anchorClicked(const TQString&,const TQString&)"); + if (!signalsBlocked() && clist) { + TQUObject o[3]; + static_QUType_TQString.set(o+1, + doc->focusIndicator.name); + static_QUType_TQString.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 TQMimeSourceFactory uses to resolve the locations + of files and images. + + \sa text +*/ + +TQString TQTextEdit::context() const +{ + return doc->context(); +} + +/*! + \property TQTextEdit::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. +*/ + +TQString TQTextEdit::documentTitle() const +{ + return doc->attributes()[ "title" ]; +} + +void TQTextEdit::makeParagVisible( TQTextParagraph *p ) +{ + setContentsPos( contentsX(), TQMIN( 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 TQTextEdit::scrollToAnchor( const TQString& name ) +{ + if ( !isVisible() ) { + d->scrollToAnchor = name; + return; + } + if ( name.isEmpty() ) + return; + sync(); + TQTextCursor cursor( doc ); + TQTextParagraph* last = doc->lastParagraph(); + for (;;) { + TQTextStringChar* c = cursor.paragraph()->at( cursor.index() ); + if( c->isAnchor() ) { + TQString a = c->anchorName(); + if ( a == name || + (a.contains( '#' ) && TQStringList::split( '#', a ).contains( name ) ) ) { + setContentsPos( contentsX(), TQMIN( 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 TQPoint& pos) should be merged into function anchorAt(const TQPoint& pos, AnchorAttribute attr)" +#endif + +/*! + \overload + + If there is an anchor at position \a pos (in contents + coordinates), its \c href is returned, otherwise TQString::null is + returned. +*/ + +TQString TQTextEdit::anchorAt( const TQPoint& 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 TQString::null is returned. +*/ + +TQString TQTextEdit::anchorAt( const TQPoint& pos, AnchorAttribute attr ) +{ + TQTextCursor 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 TQString::null; +} + +void TQTextEdit::documentWidthChanged( int w ) +{ + resizeContents( TQMAX( visibleWidth(), w), contentsHeight() ); +} + +/*! \internal + + This function does nothing +*/ + +void TQTextEdit::updateStyles() +{ +} + +void TQTextEdit::setDocument( TQTextDocument *dc ) +{ + if ( dc == 0 ) { + qWarning( "Q3TextEdit::setDocument() called with null Q3TextDocument pointer" ); + return; + } + if ( dc == doc ) + return; + resetInputContext(); + doc = dc; + delete cursor; + cursor = new TQTextCursor( 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() TQTextEdit::copy() +*/ + +void TQTextEdit::pasteSubType( const TQCString &subtype ) +{ +#ifndef QT_NO_MIMECLIPBOARD + TQMimeSource *m = TQApplication::clipboard()->data( d->clipboard_mode ); + pasteSubType( subtype, m ); +#endif +} + +/*! \internal */ + +void TQTextEdit::pasteSubType( const TQCString& subtype, TQMimeSource *m ) +{ +#ifndef QT_NO_MIME + TQCString st = subtype; + if ( subtype != "x-qrichtext" ) + st.prepend( "text/" ); + else + st.prepend( "application/" ); + if ( !m ) + return; + if ( doc->hasSelection( TQTextDocument::Standard ) ) + removeSelectedText(); + if ( !TQRichTextDrag::canDecode( m ) ) + return; + TQString t; + if ( !TQRichTextDrag::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-->" ); + TQTextCursor 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( TQTextDocument::Temp, oldC ); + doc->setSelectionEnd( TQTextDocument::Temp, *cursor ); + + checkUndoRedoInfo( UndoRedoInfo::Insert ); + if ( !undoRedoInfo.valid() ) { + undoRedoInfo.id = oldC.paragraph()->paragId(); + undoRedoInfo.index = oldC.index(); + undoRedoInfo.d->text = TQString::null; + } + int oldLen = undoRedoInfo.d->text.length(); + if ( !doc->preProcessor() ) { + TQString txt = doc->selectedText( TQTextDocument::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( TQTextDocument::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 + TQChar *uc = (TQChar *)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 TQTextEdit::pasteSpecial( const TQPoint& pt ) +{ + TQCString st = pickSpecial( TQApplication::clipboard()->data( d->clipboard_mode ), + TRUE, pt ); + if ( !st.isEmpty() ) + pasteSubType( st ); +} +#endif +#ifndef QT_NO_MIME +TQCString TQTextEdit::pickSpecial( TQMimeSource* ms, bool always_ask, const TQPoint& pt ) +{ + if ( ms ) { +#ifndef QT_NO_POPUPMENU + TQPopupMenu popup( this, "qt_pickspecial_menu" ); + TQString fmt; + int n = 0; + TQDict<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 + TQString 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 TQCString(); +} +#endif // QT_NO_MIME +#endif // QT_NO_CLIPBOARD + +/*! + \enum TQTextEdit::WordWrap + + This enum defines the TQTextEdit'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 TQTextEdit::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 TQTextEdit::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 +} + +TQTextEdit::WordWrap TQTextEdit::wordWrap() const +{ + return wrapMode; +} + +/*! + \property TQTextEdit::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 TQTextEdit::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 TQTextEdit::wrapColumnOrWidth() const +{ + if ( wrapMode == WidgetWidth ) + return visibleWidth(); + return wrapWidth; +} + + +/*! + \enum TQTextEdit::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 TQTextEdit::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 TQTextEdit::setWrapPolicy( WrapPolicy policy ) +{ + if ( wPolicy == policy ) + return; + wPolicy = policy; + TQTextFormatter *formatter; + if ( policy == AtWordBoundary || policy == AtWordOrDocumentBoundary ) { + formatter = new TQTextFormatterBreakWords; + formatter->setAllowBreakInWords( policy == AtWordOrDocumentBoundary ); + } else { + formatter = new TQTextFormatterBreakInWords; + } + formatter->setWrapAtColumn( document()->formatter()->wrapAtColumn() ); + formatter->setWrapEnabled( document()->formatter()->isWrapEnabled( 0 ) ); + document()->setFormatter( formatter ); + doc->invalidate(); + updateContents(); + lastFormatted = doc->firstParagraph(); + interval = 0; + formatMore(); +} + +TQTextEdit::WrapPolicy TQTextEdit::wrapPolicy() const +{ + return wPolicy; +} + +/*! + Deletes all the text in the text edit. + + \sa cut() removeSelectedText() setText() +*/ + +void TQTextEdit::clear() +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimSetText(""); + } else +#endif + { + // make clear undoable + doc->selectAll( TQTextDocument::Temp ); + removeSelectedText( TQTextDocument::Temp ); + setContentsPos( 0, 0 ); + if ( cursor->isValid() ) + cursor->restoreState(); + doc->clear( TRUE ); + delete cursor; + cursor = new TQTextCursor( doc ); + lastFormatted = 0; + } + updateContents(); + + emit cursorPositionChanged( cursor ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); +} + +int TQTextEdit::undoDepth() const +{ + return document()->undoDepth(); +} + +/*! + \property TQTextEdit::length + \brief the number of characters in the text +*/ + +int TQTextEdit::length() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return d->od->len; + else +#endif + return document()->length(); +} + +/*! + \property TQTextEdit::tabStopWidth + \brief the tab stop width in pixels +*/ + +int TQTextEdit::tabStopWidth() const +{ + return document()->tabStopWidth(); +} + +void TQTextEdit::setUndoDepth( int d ) +{ + document()->setUndoDepth( d ); +} + +void TQTextEdit::setTabStopWidth( int ts ) +{ + document()->setTabStops( ts ); + doc->invalidate(); + lastFormatted = doc->firstParagraph(); + interval = 0; + formatMore(); + updateContents(); +} + +/*! + \reimp +*/ + +TQSize TQTextEdit::sizeHint() const +{ + // cf. TQScrollView::sizeHint() + constPolish(); + int f = 2 * frameWidth(); + int h = fontMetrics().height(); + TQSize sz( f, f ); + return sz.expandedTo( TQSize(12 * h, 8 * h) ); +} + +void TQTextEdit::clearUndoRedo() +{ + if ( !undoEnabled ) + return; + undoRedoInfo.clear(); + emit undoAvailable( doc->commands()->isUndoAvailable() ); + emit redoAvailable( doc->commands()->isRedoAvailable() ); +} + +/*! \internal + \warning In TQt 3.1 we will provide a cleaer API for the + functionality which is provided by this function and in TQt 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 TQTextEdit::getFormat( int para, int index, TQFont *font, TQColor *color, VerticalAlignment *verticalAlignment ) +{ + if ( !font || !color ) + return FALSE; + TQTextParagraph *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 TQt 3.1 we will provide a cleaer API for the + functionality which is provided by this function and in TQt 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 TQStyleSheetItem::DisplayListItem) and \a + listDepth to the depth of the list (if the display mode is + TQStyleSheetItem::DisplayListItem). + + Returns FALSE if \a para is out of range otherwise returns TRUE. +*/ + +bool TQTextEdit::getParagraphFormat( int para, TQFont *font, TQColor *color, + VerticalAlignment *verticalAlignment, int *alignment, + TQStyleSheetItem::DisplayMode *displayMode, + TQStyleSheetItem::ListStyle *listStyle, + int *listDepth ) +{ + if ( !font || !color || !alignment || !displayMode || !listStyle ) + return FALSE; + TQTextParagraph *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() ? TQStyleSheetItem::DisplayListItem : TQStyleSheetItem::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 TQPopupMenu 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. +*/ + +TQPopupMenu *TQTextEdit::createPopupMenu( const TQPoint& pos ) +{ + Q_UNUSED( pos ) +#ifndef QT_NO_POPUPMENU + TQPopupMenu *popup = new TQPopupMenu( 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 + TQInputContext *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( TQTextDocument::Standard, TRUE ) ); +#ifdef QT_TEXTEDIT_OPTIMIZATION + popup->setItemEnabled( d->id[ IdCopy ], d->optimMode ? optimHasSelection() : doc->hasSelection( TQTextDocument::Standard, TRUE ) ); +#else + popup->setItemEnabled( d->id[ IdCopy ], doc->hasSelection( TQTextDocument::Standard, TRUE ) ); +#endif + popup->setItemEnabled( d->id[ IdPaste ], !isReadOnly() && !TQApplication::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 TQPoint & ) + returns 0. +*/ + +TQPopupMenu *TQTextEdit::createPopupMenu() +{ + return 0; +} + +/*! + \reimp +*/ + +void TQTextEdit::setFont( const TQFont &f ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + TQScrollView::setFont( f ); + doc->setDefaultFormat( f, doc->formatCollection()->defaultFormat()->color() ); + // recalculate the max string width + TQFontMetrics 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 + TQScrollView::setFont( f ); + doc->setMinimumWidth( -1 ); + doc->setDefaultFormat( f, doc->formatCollection()->defaultFormat()->color() ); + lastFormatted = doc->firstParagraph(); + formatMore(); + repaintChanged(); +} + +/*! + \fn TQTextEdit::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 TQTextEdit::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 TQTextEdit::zoomIn( int range ) +{ + TQFont f( TQScrollView::font() ); + f.setPointSize( TQFontInfo(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 TQTextEdit::zoomOut( int range ) +{ + TQFont f( TQScrollView::font() ); + f.setPointSize( TQMAX( 1, TQFontInfo(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 TQTextEdit::zoomTo( int size ) +{ + TQFont f( TQScrollView::font() ); + f.setPointSize( size ); + setFont( f ); +} + +/*! + TQTextEdit 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 TQTextEdit::sync() +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + TQFontMetrics fm( TQScrollView::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 TQTextEdit::setEnabled( bool b ) +{ + TQScrollView::setEnabled( b ); + if ( textFormat() == PlainText ) { + TQTextFormat *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 TQTextEdit::setSelectionAttributes( int selNum, const TQColor &back, bool invertText ) +{ + if ( selNum < 1 ) + return; + if ( selNum > doc->numSelections() ) + doc->addSelection( selNum ); + doc->setSelectionColor( selNum, back ); + doc->setInvertSelectionText( selNum, invertText ); +} + +/*! + \reimp +*/ +void TQTextEdit::windowActivationChange( bool oldActive ) +{ + if ( oldActive && scrollTimer ) + scrollTimer->stop(); + if ( palette().active() != palette().inactive() ) + updateContents(); + TQScrollView::windowActivationChange( oldActive ); +} + +void TQTextEdit::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 + retquired. +*/ + +void TQTextEdit::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. +*/ + +TQRect TQTextEdit::paragraphRect( int para ) const +{ + TQTextEdit *that = (TQTextEdit *)this; + that->sync(); + TQTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return TQRect( -1, -1, -1, -1 ); + return p->rect(); +} + +/*! + Returns the paragraph which is at position \a pos (in contents + coordinates). +*/ + +int TQTextEdit::paragraphAt( const TQPoint &pos ) const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + TQFontMetrics fm( TQScrollView::font() ); + int parag = pos.y() / fm.lineSpacing(); + if ( parag <= d->od->numLines ) + return parag; + else + return 0; + } +#endif + TQTextCursor 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 TQTextEdit::charAt( const TQPoint &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 + TQTextCursor 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 TQTextEdit::setParagraphBackgroundColor( int para, const TQColor &bg ) +{ + TQTextParagraph *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 TQTextEdit::clearParagraphBackground( int para ) +{ + TQTextParagraph *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 +*/ + +TQColor TQTextEdit::paragraphBackgroundColor( int para ) const +{ + TQTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return TQColor(); + TQColor *c = p->backgroundColor(); + if ( c ) + return *c; + return TQColor(); +} + +/*! + \property TQTextEdit::undoRedoEnabled + \brief whether undo/redo is enabled + + When changing this property, the undo/redo history is cleared. + + The default is TRUE. +*/ + +void TQTextEdit::setUndoRedoEnabled( bool b ) +{ + undoRedoInfo.clear(); + doc->commands()->clear(); + + undoEnabled = b; +} + +bool TQTextEdit::isUndoRedoEnabled() const +{ + return undoEnabled; +} + +/*! + Returns TRUE if undo is available; otherwise returns FALSE. +*/ + +bool TQTextEdit::isUndoAvailable() const +{ + return undoEnabled && (doc->commands()->isUndoAvailable() || undoRedoInfo.valid()); +} + +/*! + Returns TRUE if redo is available; otherwise returns FALSE. +*/ + +bool TQTextEdit::isRedoAvailable() const +{ + return undoEnabled && doc->commands()->isRedoAvailable(); +} + +void TQTextEdit::ensureFormatted( TQTextParagraph *p ) +{ + while ( !p->isValid() ) { + if ( !lastFormatted ) + return; + formatMore(); + } +} + +/*! \internal */ +void TQTextEdit::updateCursor( const TQPoint & pos ) +{ + if ( isReadOnly() && linksEnabled() ) { + TQTextCursor 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 = TQString::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 = TQString::null; + + if (!c.paragraph()->at( c.index() )->anchorHref().isEmpty() ) { +#ifndef QT_NO_CURSOR + viewport()->setCursor( onLink.isEmpty() ? arrowCursor : pointingHandCursor ); +#endif + TQUrl u( doc->context(), onLink, TRUE ); + emitHighlighted( u.toString( FALSE, FALSE ) ); + } + } else { +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + onLink = TQString::null; + emitHighlighted( TQString::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 TQTextEdit::placeCursor( const TQPoint &pos, TQTextCursor *c ) +{ + placeCursor( pos, c, FALSE ); +} + +/*! \internal */ +void TQTextEdit::clipboardChanged() +{ +#ifndef QT_NO_CLIPBOARD + // don't listen to selection changes + disconnect( TQApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); +#endif + selectAll(FALSE); +} + +/*! \property TQTextEdit::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 TQTextEdit::setTabChangesFocus( bool b ) +{ + d->tabChangesFocus = b; +} + +bool TQTextEdit::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 TQTextEdit::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 TQTextEditOptimPrivate; + 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 TQTextCursor( 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 */ +TQString TQTextEdit::optimText() const +{ + TQString str, tmp; + + if ( d->od->len == 0 ) + return str; + + // concatenate all strings + int i; + int offset; + TQMapConstIterator<int,TQTextEditOptimPrivate::Tag *> it; + TQTextEditOptimPrivate::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 TQTextEdit::optimSetText( const TQString &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(); + TQFontMetrics fm( TQScrollView::font() ); + if ( !(str.isEmpty() || str.isNull() || d->maxLogLines == 0) ) { + TQStringList strl = TQStringList::split( '\n', str, TRUE ); + int lWidth = 0; + for ( TQStringList::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. +*/ +TQTextEditOptimPrivate::Tag * TQTextEdit::optimAppendTag( int index, const TQString & tag ) +{ + TQTextEditOptimPrivate::Tag * t = new TQTextEditOptimPrivate::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 +*/ + +TQTextEditOptimPrivate::Tag *TQTextEdit::optimInsertTag(int line, int index, const TQString &tag) +{ + TQTextEditOptimPrivate::Tag *t = new TQTextEditOptimPrivate::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. + TQMap<int,TQTextEditOptimPrivate::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 TQTextEdit::optimParseTags( TQString * 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; + TQString tagStr; + TQPtrStack<TQTextEditOptimPrivate::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] == ';' ) { + TQString esc = line->mid( escIndex, i - escIndex + 1 ); + TQString 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() ) { + TQTextEditOptimPrivate::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 ? TQTextEditOptimPrivate::Format + : TQTextEditOptimPrivate::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( "TQTextEdit::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( "TQTextEdit::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 TQString& str, int tabWidth, const TQFontMetrics& 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 TQTextEdit::optimHasBoldMetrics(int line) +{ + TQTextEditOptimPrivate::Tag *t; + TQMapConstIterator<int,TQTextEditOptimPrivate::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 TQTextEdit::optimAppend( const TQString &str ) +{ + if ( str.isEmpty() || str.isNull() || d->maxLogLines == 0 ) + return; + + TQStringList strl = TQStringList::split( '\n', str, TRUE ); + TQStringList::Iterator it = strl.begin(); + + TQFontMetrics fm(TQScrollView::font()); + int lWidth = 0; + + for ( ; it != strl.end(); ++it ) { + optimParseTags( &*it ); + optimCheckLimit( *it ); + if (optimHasBoldMetrics(d->od->numLines-1)) { + TQFont fnt = TQScrollView::font(); + fnt.setBold(TRUE); + fm = TQFontMetrics(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(TQString *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] == ';' ) { + TQString esc = line->mid( escIndex, i - escIndex + 1 ); + TQString 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 TQTextEdit::optimInsert(const TQString& 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(); + + TQStringList strl = TQStringList::split('\n', text, TRUE); + int numNewLines = (int)strl.count() - 1; + TQTextEditOptimPrivate::Tag *tag = 0; + TQMap<int,TQTextEditOptimPrivate::Tag *>::ConstIterator ii; + int x; + + if (numNewLines == 0) { + // Case 1. Fast single line case - just inject it! + TQString 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! + TQString left = d->od->lines[LOGOFFSET(line)].left(index); + TQString 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 + TQString 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 + TQStringList::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 - + TQFontMetrics fm(TQScrollView::font()); + int lWidth = 0; + + for (x = line; x < line + numNewLines; x++) { + if (optimHasBoldMetrics(x)) { + TQFont fnt = TQScrollView::font(); + fnt.setBold(TRUE); + fm = TQFontMetrics(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. + */ +TQTextEditOptimPrivate::Tag * TQTextEdit::optimPreviousLeftTag( int line ) +{ + TQTextEditOptimPrivate::Tag * ftag = 0; + TQMapConstIterator<int,TQTextEditOptimPrivate::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 TQTextEdit::optimSetTextFormat( TQTextDocument * td, TQTextCursor * cur, + TQTextFormat * f, int start, int end, + TQTextEditOptimPrivate::Tag * tag ) +{ + int formatFlags = TQTextFormat::Bold | TQTextFormat::Italic | + TQTextFormat::Underline; + cur->setIndex( start ); + td->setSelectionStart( 0, *cur ); + cur->setIndex( end ); + td->setSelectionEnd( 0, *cur ); + TQStyleSheetItem * ssItem = styleSheet()->item( tag->tag ); + if ( !ssItem || tag->type == TQTextEditOptimPrivate::Format ) { + f->setBold( tag->bold ); + f->setItalic( tag->italic ); + f->setUnderline( tag->underline ); + if ( tag->type == TQTextEditOptimPrivate::Format ) { + // check to see if there are any open color tags prior to + // this format tag + tag = tag->prev; + while ( tag && (tag->type == TQTextEditOptimPrivate::Format || + tag->leftTag) ) { + tag = tag->leftTag ? tag->parent : tag->prev; + } + } + if ( tag ) { + TQString 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 ); + } + TQColor color = TQColor( col ); + if ( color.isValid() ) { + formatFlags |= TQTextFormat::Color; + f->setColor( color ); + } + } + } else { // use the stylesheet tag definition + if ( ssItem->color().isValid() ) { + formatFlags |= TQTextFormat::Color; + f->setColor( ssItem->color() ); + } + f->setBold( ssItem->fontWeight() == TQFont::Bold ); + f->setItalic( ssItem->fontItalic() ); + f->setUnderline( ssItem->fontUnderline() ); + } + td->setFormat( 0, f, formatFlags ); + td->removeSelection( 0 ); +} + +/*! \internal */ +void TQTextEdit::optimDrawContents( TQPainter * p, int clipx, int clipy, + int clipw, int cliph ) +{ + TQFontMetrics fm( TQScrollView::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; + TQString str; + for ( i = startLine; i < (startLine + nLines); i++ ) + str.append( d->od->lines[ LOGOFFSET(i) ] + "\n" ); + + TQTextDocument * td = new TQTextDocument( 0 ); + td->setDefaultFormat( TQScrollView::font(), TQColor() ); + td->setPlainText( str ); + td->setFormatter( new TQTextFormatterBreakWords ); // deleted by TQTextDoc + td->formatter()->setWrapEnabled( FALSE ); + td->setTabStops(doc->tabStopWidth()); + + // get the current text color from the current format + td->selectAll( TQTextDocument::Standard ); + TQTextFormat f; + f.setColor( colorGroup().text() ); + f.setFont( TQScrollView::font() ); + td->setFormat( TQTextDocument::Standard, &f, + TQTextFormat::Color | TQTextFormat::Font ); + td->removeSelection( TQTextDocument::Standard ); + + // add tag formatting + if ( d->od->tags ) { + int i = startLine; + TQMapConstIterator<int,TQTextEditOptimPrivate::Tag *> it; + TQTextEditOptimPrivate::Tag * tag = 0, * tmp = 0; + TQTextCursor 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() ) { + TQTextCursor c1( td ); + TQTextCursor 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); + TQTextParagraph * 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( TQTextDocument::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; + TQTextParagraph * 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; + TQTextParagraph * 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( TQTextDocument::Standard ) ) { + td->setSelectionStart( TQTextDocument::Standard, c1 ); + td->setSelectionEnd( TQTextDocument::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; + TQRect 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 TQTextEdit::optimMousePressEvent( TQMouseEvent * e ) +{ + if ( e->button() != LeftButton ) + return; + + TQFontMetrics fm( TQScrollView::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 { + TQString 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 TQTextEdit::optimMouseReleaseEvent( TQMouseEvent * e ) +{ + if ( e->button() != LeftButton ) + return; + + if ( scrollTimer->isActive() ) + scrollTimer->stop(); + if ( !inDoubleClick ) { + TQFontMetrics fm( TQScrollView::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; + } + TQString 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 TQTextEdit::optimMouseMoveEvent( TQMouseEvent * e ) +{ + mousePos = e->pos(); + optimDoAutoScroll(); + oldMousePos = mousePos; +} + +/*! \internal */ +void TQTextEdit::optimDoAutoScroll() +{ + if ( !mousePressed ) + return; + + TQFontMetrics fm( TQScrollView::font() ); + TQPoint pos( mapFromGlobal( TQCursor::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; + } + + TQString 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 = TQABS(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 TQTextEdit::optimCharIndex( const TQString &str, int mx ) const +{ + TQFontMetrics fm(TQScrollView::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 (TQABS(dd) <= dist) { + dist = TQABS(dd); + if (mx >= strWidth) + curpos = i; + } + ++i; + } + return curpos; +} + +/*! \internal */ +void TQTextEdit::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 TQTextEdit::optimRemoveSelection() +{ + d->od->selStart.line = d->od->selEnd.line = -1; + d->od->selStart.index = d->od->selEnd.index = -1; + repaintContents( FALSE ); +} + +/*! \internal */ +void TQTextEdit::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 TQTextEdit::optimHasSelection() const +{ + if ( d->od->selStart.line != d->od->selEnd.line || + d->od->selStart.index != d->od->selEnd.index ) + return TRUE; + return FALSE; +} + +/*! \internal */ +TQString TQTextEdit::optimSelectedText() const +{ + TQString 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 TQTextEdit::optimFind( const TQString & 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() ); + TQFontMetrics fm( TQScrollView::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 TQTextEdit::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(); + TQWidget::polish(); +} + +/*! + Sets the maximum number of lines a TQTextEdit 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 TQTextEdit::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 TQTextEdit can hold in \c + LogText mode. By default the number of lines is unlimited, which + is signified by a value of -1. + */ +int TQTextEdit::maxLogLines() +{ + return d->maxLogLines; +} + +/*! + Check if the number of lines in the buffer is limited, and uphold + that limit when appending new lines. + */ +void TQTextEdit::optimCheckLimit( const TQString& 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. + TQTextEditOptimPrivate::Tag *t = d->od->tags, *tmp, *itr; + TQPtrList<TQTextEditOptimPrivate::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 + TQMapIterator<int, TQTextEditOptimPrivate::Tag *> idx; + if ( (idx = d->od->tagIndex.find( d->logOffset )) != d->od->tagIndex.end() ) + d->od->tagIndex.remove( idx ); + + TQMapIterator<int,TQString> 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 TQTextEdit::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 TQt may offer more. +*/ + +void TQTextEdit::setAutoFormatting( uint features ) +{ + d->autoFormatting = features; +} + +uint TQTextEdit::autoFormatting() const +{ + return d->autoFormatting; +} + +/*! + Returns the TQSyntaxHighlighter set on this TQTextEdit. 0 is + returned if no syntax highlighter is set. + */ +TQSyntaxHighlighter * TQTextEdit::syntaxHighlighter() const +{ + if (document()->preProcessor()) + return ((TQSyntaxHighlighterInternal *) 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 000000000..ae3c8ee43 --- /dev/null +++ b/src/widgets/qtextedit.h @@ -0,0 +1,616 @@ +/**************************************************************************** +** +** Definition of the TQTextEdit class +** +** Created : 990101 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQTEXTEDIT_H +#define TQTEXTEDIT_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 TQPainter; +class TQTextDocument; +class TQTextCursor; +class TQKeyEvent; +class TQResizeEvent; +class TQMouseEvent; +class TQTimer; +class TQTextString; +class TQTextCommand; +class TQTextParagraph; +class TQTextFormat; +class TQFont; +class TQColor; +class TQTextEdit; +class TQTextBrowser; +class TQTextString; +struct TQUndoRedoInfoPrivate; +class TQPopupMenu; +class TQTextEditPrivate; +class TQSyntaxHighlighter; + +#ifdef QT_TEXTEDIT_OPTIMIZATION +class TQTextEditOptimPrivate +{ +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; + TQString tag; + }; + TQTextEditOptimPrivate() + { + 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(); + } + ~TQTextEditOptimPrivate() + { + clearTags(); + } + int len; + int numLines; + int maxLineWidth; + struct Selection { + int line; + int index; + }; + Selection selStart, selEnd, search; + Tag * tags, * lastTag; + TQMap<int, TQString> lines; + TQMap<int, Tag *> tagIndex; +}; +#endif + +class Q_EXPORT TQTextEdit : public TQScrollView +{ + friend class TQTextBrowser; + friend class TQSyntaxHighlighter; + + Q_OBJECT + Q_ENUMS( WordWrap WrapPolicy ) + Q_SETS( AutoFormatting ) + Q_PROPERTY( TextFormat textFormat READ textFormat WRITE setTextFormat ) + Q_PROPERTY( TQString text READ text WRITE setText ) + Q_PROPERTY( TQBrush paper READ paper WRITE setPaper ) + Q_PROPERTY( bool linkUnderline READ linkUnderline WRITE setLinkUnderline ) + Q_PROPERTY( TQString 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( TQString 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 + }; + + TQTextEdit( const TQString& text, const TQString& context = TQString::null, + TQWidget* parent=0, const char* name=0); + TQTextEdit( TQWidget* parent=0, const char* name=0 ); + virtual ~TQTextEdit(); + void setPalette( const TQPalette & ); + + TQString text() const; + TQString text( int para ) const; + TextFormat textFormat() const; + TQString context() const; + TQString documentTitle() const; + + void getSelection( int *paraFrom, int *indexFrom, + int *paraTo, int *indexTo, int selNum = 0 ) const; + virtual bool find( const TQString &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; + TQRect paragraphRect( int para ) const; + int paragraphAt( const TQPoint &pos ) const; + int charAt( const TQPoint &pos, int *para ) const; + int paragraphLength( int para ) const; + + TQStyleSheet* styleSheet() const; +#ifndef QT_NO_MIME + TQMimeSourceFactory* mimeSourceFactory() const; +#endif + TQBrush paper() const; + bool linkUnderline() const; + + int heightForWidth( int w ) const; + + bool hasSelectedText() const; + TQString selectedText() const; + bool isUndoAvailable() const; + bool isRedoAvailable() const; + + WordWrap wordWrap() const; + int wrapColumnOrWidth() const; + WrapPolicy wrapPolicy() const; + + int tabStopWidth() const; + + TQString anchorAt( const TQPoint& pos ); + TQString anchorAt( const TQPoint& pos, AnchorAttribute a ); + + TQSize 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; + TQString family() const; + int pointSize() const; + TQColor color() const; + TQFont font() const; + TQFont currentFont() const; + int alignment() const; + int undoDepth() const; + + // do not use, will go away + virtual bool getFormat( int para, int index, TQFont *font, TQColor *color, VerticalAlignment *verticalAlignment ); + // do not use, will go away + virtual bool getParagraphFormat( int para, TQFont *font, TQColor *color, + VerticalAlignment *verticalAlignment, int *alignment, + TQStyleSheetItem::DisplayMode *displayMode, + TQStyleSheetItem::ListStyle *listStyle, + int *listDepth ); + + + bool isOverwriteMode() const { return overWrite; } + TQColor paragraphBackgroundColor( int para ) const; + + bool isUndoRedoEnabled() const; + bool eventFilter( TQObject *o, TQEvent *e ); + bool tabChangesFocus() const; + + void setAutoFormatting( uint features ); + uint autoFormatting() const; + TQSyntaxHighlighter *syntaxHighlighter() const; + +public slots: + void setEnabled( bool ); +#ifndef QT_NO_MIME + virtual void setMimeSourceFactory( TQMimeSourceFactory* factory ); +#endif + virtual void setStyleSheet( TQStyleSheet* styleSheet ); + virtual void scrollToAnchor( const TQString& name ); + virtual void setPaper( const TQBrush& 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 TQString& text ); + + void setText( const TQString &txt ) { setText( txt, TQString::null ); } + virtual void setText( const TQString &txt, const TQString &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 TQCString &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 TQString &f ); + virtual void setPointSize( int s ); + virtual void setColor( const TQColor &c ); + virtual void setFont( const TQFont &f ); + virtual void setVerticalAlignment( VerticalAlignment a ); + virtual void setAlignment( int a ); + + // do not use, will go away + virtual void setParagType( TQStyleSheetItem::DisplayMode dm, TQStyleSheetItem::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 TQColor &back, bool invertText ); + virtual void setModified( bool m ); + virtual void resetFormat(); + virtual void setUndoDepth( int d ); + virtual void setFormat( TQTextFormat *f, int flags ); + virtual void ensureCursorVisible(); + virtual void placeCursor( const TQPoint &pos, TQTextCursor *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 TQFont &f ); + virtual void setOverwriteMode( bool b ) { overWrite = b; } + + virtual void scrollToBottom(); + + void insert( const TQString &text, uint insertionFlags = CheckNewLines | RemoveSelected ); // ## virtual in 4.0 + + // obsolete + virtual void insert( const TQString &text, bool, bool = TRUE, bool = TRUE ); + + virtual void insertAt( const TQString &text, int para, int index ); + virtual void removeParagraph( int para ); + virtual void insertParagraph( const TQString &text, int para ); + + virtual void setParagraphBackgroundColor( int para, const TQColor &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 TQFont &f ); + void currentColorChanged( const TQColor &c ); + void currentAlignmentChanged( int a ); + void currentVerticalAlignmentChanged( VerticalAlignment a ); + void cursorPositionChanged( TQTextCursor *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( TQPainter *p, int cx, int cy, int cw, int ch ); + bool event( TQEvent *e ); + void keyPressEvent( TQKeyEvent *e ); + void resizeEvent( TQResizeEvent *e ); + void viewportResizeEvent( TQResizeEvent* ); + void contentsMousePressEvent( TQMouseEvent *e ); + void contentsMouseMoveEvent( TQMouseEvent *e ); + void contentsMouseReleaseEvent( TQMouseEvent *e ); + void contentsMouseDoubleClickEvent( TQMouseEvent *e ); +#ifndef QT_NO_WHEELEVENT + void contentsWheelEvent( TQWheelEvent *e ); +#endif + void imStartEvent( TQIMEvent * ); + void imComposeEvent( TQIMEvent * ); + void imEndEvent( TQIMEvent * ); +#ifndef QT_NO_DRAGANDDROP + void contentsDragEnterEvent( TQDragEnterEvent *e ); + void contentsDragMoveEvent( TQDragMoveEvent *e ); + void contentsDragLeaveEvent( TQDragLeaveEvent *e ); + void contentsDropEvent( TQDropEvent *e ); +#endif + void contentsContextMenuEvent( TQContextMenuEvent *e ); + bool sendMouseEventToInputContext( TQMouseEvent *e ); + bool focusNextPrevChild( bool next ); + TQTextDocument *document() const; + TQTextCursor *textCursor() const; + void setDocument( TQTextDocument *doc ); + virtual TQPopupMenu *createPopupMenu( const TQPoint& pos ); + virtual TQPopupMenu *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 TQt 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( TQTextDocument *dc ); + ~UndoRedoInfo(); + void clear(); + bool valid() const; + + TQUndoRedoInfoPrivate *d; + int id; + int index; + int eid; + int eindex; + TQTextFormat *format; + int flags; + Type type; + TQTextDocument *doc; + TQByteArray styleInformation; + }; + +private: + void updateCursor( const TQPoint & pos ); + void handleMouseMove( const TQPoint& pos ); + void drawContents( TQPainter * ); + virtual bool linksEnabled() const { return FALSE; } + void init(); + void checkUndoRedoInfo( UndoRedoInfo::Type t ); + void updateCurrentFormat(); + bool handleReadOnlyKeyEvent( TQKeyEvent *e ); + void makeParagVisible( TQTextParagraph *p ); + void normalCopy(); + void copyToClipboard(); +#ifndef QT_NO_MIME + TQCString pickSpecial(TQMimeSource* ms, bool always_ask, const TQPoint&); + TQTextDrag *dragObject( TQWidget *parent = 0 ) const; +#endif +#ifndef QT_NO_MIMECLIPBOARD + void pasteSpecial(const TQPoint&); +#endif + void setFontInternal( const TQFont &f ); + + virtual void emitHighlighted( const TQString & ) {} + virtual void emitLinkClicked( const TQString & ) {} + + void readFormats( TQTextCursor &c1, TQTextCursor &c2, TQTextString &text, bool fillStyles = FALSE ); + void clearUndoRedo(); + void paintDocument( bool drawAll, TQPainter *p, int cx = -1, int cy = -1, int cw = -1, int ch = -1 ); + void moveCursor( CursorAction action ); + void ensureFormatted( TQTextParagraph *p ); + void placeCursor( const TQPoint &pos, TQTextCursor *c, bool link ); + void updateMicroFocusHint(); + +#ifdef QT_TEXTEDIT_OPTIMIZATION + bool checkOptimMode(); + TQString optimText() const; + void optimSetText( const TQString &str ); + void optimAppend( const TQString &str ); + void optimInsert( const TQString &str, int line, int index ); + void optimDrawContents( TQPainter * p, int cx, int cy, int cw, int ch ); + void optimMousePressEvent( TQMouseEvent * e ); + void optimMouseReleaseEvent( TQMouseEvent * e ); + void optimMouseMoveEvent( TQMouseEvent * e ); + int optimCharIndex( const TQString &str, int mx ) const; + void optimSelectAll(); + void optimRemoveSelection(); + void optimSetSelection( int startLine, int startIdx, int endLine, + int endIdx ); + bool optimHasSelection() const; + TQString optimSelectedText() const; + bool optimFind( const TQString & str, bool, bool, bool, int *, int * ); + void optimParseTags( TQString * str, int lineNo = -1, int indexOffset = 0 ); + TQTextEditOptimPrivate::Tag * optimPreviousLeftTag( int line ); + void optimSetTextFormat( TQTextDocument *, TQTextCursor *, TQTextFormat * f, + int, int, TQTextEditOptimPrivate::Tag * t ); + TQTextEditOptimPrivate::Tag * optimAppendTag( int index, const TQString & tag ); + TQTextEditOptimPrivate::Tag * optimInsertTag( int line, int index, const TQString & tag ); + void optimCheckLimit( const TQString& str ); + bool optimHasBoldMetrics( int line ); + +private slots: + void optimDoAutoScroll(); +#endif // QT_TEXTEDIT_OPTIMIZATION + +private: +#ifndef QT_NO_CLIPBOARD + void pasteSubType( const TQCString &subtype, TQMimeSource *m ); +#endif + +private: + TQTextDocument *doc; + TQTextCursor *cursor; + TQTimer *formatTimer, *scrollTimer, *changeIntervalTimer, *blinkTimer, *dragStartTimer; + TQTextParagraph *lastFormatted; + int interval; + UndoRedoInfo undoRedoInfo; + TQTextFormat *currentFormat; + int currentAlignment; + TQPoint oldMousePos, mousePos; + TQPoint dragStartPos; + TQString onLink; + WordWrap wrapMode; + WrapPolicy wPolicy; + int wrapWidth; + TQString pressedLink; + TQTextEditPrivate *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) + TQTextEdit( const TQTextEdit & ); + TQTextEdit &operator=( const TQTextEdit & ); +#endif +}; + +inline TQTextDocument *TQTextEdit::document() const +{ + return doc; +} + +inline TQTextCursor *TQTextEdit::textCursor() const +{ + return cursor; +} + +inline void TQTextEdit::setCurrentFont( const TQFont &f ) +{ + TQTextEdit::setFontInternal( f ); +} + +#endif //QT_NO_TEXTEDIT +#endif //TQTEXTVIEW_H diff --git a/src/widgets/qtextview.cpp b/src/widgets/qtextview.cpp new file mode 100644 index 000000000..59db4d9cc --- /dev/null +++ b/src/widgets/qtextview.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Implementation of the TQTextView class +** +** Created : 990101 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qtextview.h" + +#ifndef QT_NO_TEXTVIEW + +/*! \class TQTextView + \brief The TQTextView class provides a rich-text viewer. + + \obsolete + + This class wraps a read-only \l TQTextEdit. + Use a \l TQTextEdit instead, and call setReadOnly(TRUE) + to disable editing. +*/ + +/*! \reimp */ + +TQTextView::TQTextView( const TQString& text, const TQString& context, + TQWidget *parent, const char *name ) + : TQTextEdit( text, context, parent, name ) +{ + setReadOnly( TRUE ); +} + +/*! \reimp */ + +TQTextView::TQTextView( TQWidget *parent, const char *name ) + : TQTextEdit( parent, name ) +{ + setReadOnly( TRUE ); +} + +/*! \reimp */ + +TQTextView::~TQTextView() +{ +} + +/*! + \property TQTextView::undoDepth + \brief the number of undoable steps +*/ + +/*! + \property TQTextView::overwriteMode + \brief whether new text overwrites or pushes aside existing text +*/ + +/*! + \property TQTextView::modified + \brief Whether the text view's contents have been modified. +*/ + +/*! + \property TQTextView::readOnly + \brief Whether the text view's contents are read only. +*/ + +/*! + \property TQTextView::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 000000000..68378a02b --- /dev/null +++ b/src/widgets/qtextview.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Definition of the TQTextView class +** +** Created : 990101 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQTEXTVIEW_H +#define TQTEXTVIEW_H + +#ifndef QT_H +#include "qtextedit.h" +#endif // QT_H + +#ifndef QT_NO_TEXTVIEW + +class Q_EXPORT TQTextView : public TQTextEdit +{ + 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: + TQTextView( const TQString& text, const TQString& context = TQString::null, + TQWidget* parent=0, const char* name=0); + TQTextView( TQWidget* parent=0, const char* name=0 ); + + virtual ~TQTextView(); + +private: +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQTextView( const TQTextView & ); + TQTextView &operator=( const TQTextView & ); +#endif +}; + +#endif //QT_NO_TEXTVIEW +#endif //TQTEXTVIEW_H diff --git a/src/widgets/qtitlebar.cpp b/src/widgets/qtitlebar.cpp new file mode 100644 index 000000000..ceec98254 --- /dev/null +++ b/src/widgets/qtitlebar.cpp @@ -0,0 +1,671 @@ +/**************************************************************************** +** +** Implementation of some TQt private functions. +** +** Created : 001101 +** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQTitleBarTip : public TQToolTip +{ +public: + TQTitleBarTip( TQWidget * parent ) : TQToolTip( parent ) { } + + void maybeTip( const TQPoint &pos ) + { + if ( !::qt_cast<TQTitleBar*>(parentWidget()) ) + return; + TQTitleBar *t = (TQTitleBar *)parentWidget(); + + TQString tipstring; + TQStyle::SubControl ctrl = t->style().querySubControl(TQStyle::CC_TitleBar, t, pos); + TQSize controlSize = t->style().querySubControlMetrics(TQStyle::CC_TitleBar, t, ctrl).size(); + + TQWidget *window = t->window(); + if ( window ) { + switch(ctrl) { + case TQStyle::SC_TitleBarSysMenu: + if ( t->testWFlags( WStyle_SysMenu ) ) + tipstring = TQTitleBar::tr( "System Menu" ); + break; + + case TQStyle::SC_TitleBarShadeButton: + if ( t->testWFlags( WStyle_Tool ) && t->testWFlags( WStyle_MinMax ) ) + tipstring = TQTitleBar::tr( "Shade" ); + break; + + case TQStyle::SC_TitleBarUnshadeButton: + if ( t->testWFlags( WStyle_Tool ) && t->testWFlags( WStyle_MinMax ) ) + tipstring = TQTitleBar::tr( "Unshade" ); + break; + + case TQStyle::SC_TitleBarNormalButton: + case TQStyle::SC_TitleBarMinButton: + if ( !t->testWFlags( WStyle_Tool ) && t->testWFlags( WStyle_Minimize ) ) { + if( window->isMinimized() ) + tipstring = TQTitleBar::tr( "Normalize" ); + else + tipstring = TQTitleBar::tr( "Minimize" ); + } + break; + + case TQStyle::SC_TitleBarMaxButton: + if ( !t->testWFlags( WStyle_Tool ) && t->testWFlags( WStyle_Maximize ) ) + tipstring = TQTitleBar::tr( "Maximize" ); + break; + + case TQStyle::SC_TitleBarCloseButton: + if ( t->testWFlags( WStyle_SysMenu ) ) + tipstring = TQTitleBar::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( TQRect(pos, controlSize), tipstring ); + } +}; +#endif + +class TQTitleBarPrivate +{ +public: + TQTitleBarPrivate() + : toolTip( 0 ), act( 0 ), window( 0 ), movable( 1 ), pressed( 0 ), autoraise(0) + { + } + + TQStyle::SCFlags buttonDown; + TQPoint moveOffset; + TQToolTip *toolTip; + bool act :1; + TQWidget* window; + bool movable :1; + bool pressed :1; + bool autoraise :1; + TQString cuttext; +#ifdef QT_NO_WIDGET_TOPEXTRA + TQString cap; +#endif +}; + +TQTitleBar::TQTitleBar(TQWidget* w, TQWidget* parent, const char* name) + : TQWidget( parent, name, WStyle_Customize | WStyle_NoBorder | WNoAutoErase ) +{ + d = new TQTitleBarPrivate(); + +#ifndef QT_NO_TOOLTIP + d->toolTip = new TQTitleBarTip( this ); +#endif + d->window = w; + d->buttonDown = TQStyle::SC_None; + d->act = 0; + if ( w ) { + setWFlags( ((TQTitleBar*)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( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed ) ); + setMouseTracking(TRUE); +} + +TQTitleBar::~TQTitleBar() +{ +#ifndef QT_NO_TOOLTIP + delete d->toolTip; +#endif + + delete d; + d = 0; +} + +#ifdef Q_WS_WIN +extern TQRgb qt_colorref2qrgb(COLORREF col); +#endif + +void TQTitleBar::readColors() +{ + TQPalette 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 ( TQApplication::desktopSettingsAware() ) { + pal.setColor( TQPalette::Active, TQColorGroup::Highlight, qt_colorref2qrgb(GetSysColor(COLOR_ACTIVECAPTION)) ); + pal.setColor( TQPalette::Inactive, TQColorGroup::Highlight, qt_colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTION)) ); + pal.setColor( TQPalette::Active, TQColorGroup::HighlightedText, qt_colorref2qrgb(GetSysColor(COLOR_CAPTIONTEXT)) ); + pal.setColor( TQPalette::Inactive, TQColorGroup::HighlightedText, qt_colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTIONTEXT)) ); + if ( qt_winver != TQt::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( TQPalette::Active, TQColorGroup::Base, qt_colorref2qrgb(GetSysColor(COLOR_GRADIENTACTIVECAPTION)) ); + pal.setColor( TQPalette::Inactive, TQColorGroup::Base, qt_colorref2qrgb(GetSysColor(COLOR_GRADIENTINACTIVECAPTION)) ); + } else { + pal.setColor( TQPalette::Active, TQColorGroup::Base, palette().active().highlight() ); + pal.setColor( TQPalette::Inactive, TQColorGroup::Base, palette().inactive().highlight() ); + } + } + } +#endif // Q_WS_WIN + if ( !colorsInitialized ) { + pal.setColor( TQPalette::Active, TQColorGroup::Highlight, palette().active().highlight() ); + pal.setColor( TQPalette::Active, TQColorGroup::Base, palette().active().highlight() ); + pal.setColor( TQPalette::Inactive, TQColorGroup::Highlight, palette().inactive().dark() ); + pal.setColor( TQPalette::Inactive, TQColorGroup::Base, palette().inactive().dark() ); + pal.setColor( TQPalette::Inactive, TQColorGroup::HighlightedText, palette().inactive().background() ); + } + + setPalette( pal ); + setActive( d->act ); +} + +void TQTitleBar::mousePressEvent( TQMouseEvent * e) +{ + if ( !d->act ) + emit doActivate(); + if ( e->button() == LeftButton ) { + d->pressed = TRUE; + TQStyle::SCFlags ctrl = style().querySubControl(TQStyle::CC_TitleBar, this, e->pos()); + switch (ctrl) { + case TQStyle::SC_TitleBarSysMenu: + if ( testWFlags( WStyle_SysMenu ) && !testWFlags( WStyle_Tool ) ) { + d->buttonDown = TQStyle::SC_None; + static TQTime* t = 0; + static TQTitleBar* tc = 0; + if ( !t ) + t = new TQTime; + if ( tc != this || t->elapsed() > TQApplication::doubleClickInterval() ) { + emit showOperationMenu(); + t->start(); + tc = this; + } else { + tc = 0; + emit doClose(); + return; + } + } + break; + + case TQStyle::SC_TitleBarShadeButton: + case TQStyle::SC_TitleBarUnshadeButton: + if ( testWFlags( WStyle_MinMax ) && testWFlags( WStyle_Tool ) ) + d->buttonDown = ctrl; + break; + + case TQStyle::SC_TitleBarNormalButton: + if( testWFlags( WStyle_Minimize ) && !testWFlags( WStyle_Tool ) ) + d->buttonDown = ctrl; + break; + + case TQStyle::SC_TitleBarMinButton: + if( testWFlags( WStyle_Minimize ) && !testWFlags( WStyle_Tool ) ) + d->buttonDown = ctrl; + break; + + case TQStyle::SC_TitleBarMaxButton: + if ( testWFlags( WStyle_Maximize ) && !testWFlags( WStyle_Tool ) ) + d->buttonDown = ctrl; + break; + + case TQStyle::SC_TitleBarCloseButton: + if ( testWFlags( WStyle_SysMenu ) ) + d->buttonDown = ctrl; + break; + + case TQStyle::SC_TitleBarLabel: + d->buttonDown = ctrl; + d->moveOffset = mapToParent( e->pos() ); + break; + + default: + break; + } + repaint( FALSE ); + } else { + d->pressed = FALSE; + } +} + +void TQTitleBar::contextMenuEvent( TQContextMenuEvent *e ) +{ + TQStyle::SCFlags ctrl = style().querySubControl(TQStyle::CC_TitleBar, this, e->pos()); + if( ctrl == TQStyle::SC_TitleBarLabel || ctrl == TQStyle::SC_TitleBarSysMenu ) + emit popupOperationMenu(e->globalPos()); + else + e->ignore(); +} + +void TQTitleBar::mouseReleaseEvent( TQMouseEvent * e) +{ + if ( e->button() == LeftButton && d->pressed) { + TQStyle::SCFlags ctrl = style().querySubControl(TQStyle::CC_TitleBar, this, e->pos()); + + if (ctrl == d->buttonDown) { + switch(ctrl) { + case TQStyle::SC_TitleBarShadeButton: + case TQStyle::SC_TitleBarUnshadeButton: + if( testWFlags( WStyle_MinMax ) && testWFlags( WStyle_Tool ) ) + emit doShade(); + break; + + case TQStyle::SC_TitleBarNormalButton: + if( testWFlags( WStyle_MinMax ) && !testWFlags( WStyle_Tool ) ) + emit doNormal(); + break; + + case TQStyle::SC_TitleBarMinButton: + if( testWFlags( WStyle_Minimize ) && !testWFlags( WStyle_Tool ) ) + emit doMinimize(); + break; + + case TQStyle::SC_TitleBarMaxButton: + if( d->window && testWFlags( WStyle_Maximize ) && !testWFlags( WStyle_Tool ) ) { + if(d->window->isMaximized()) + emit doNormal(); + else + emit doMaximize(); + } + break; + + case TQStyle::SC_TitleBarCloseButton: + if( testWFlags( WStyle_SysMenu ) ) { + d->buttonDown = TQStyle::SC_None; + repaint(FALSE); + emit doClose(); + return; + } + break; + + default: + break; + } + } + d->buttonDown = TQStyle::SC_None; + repaint(FALSE); + d->pressed = FALSE; + } +} + +void TQTitleBar::mouseMoveEvent( TQMouseEvent * e) +{ + switch (d->buttonDown) { + case TQStyle::SC_None: + if(autoRaise()) + repaint( FALSE ); + break; + case TQStyle::SC_TitleBarSysMenu: + break; + case TQStyle::SC_TitleBarShadeButton: + case TQStyle::SC_TitleBarUnshadeButton: + case TQStyle::SC_TitleBarNormalButton: + case TQStyle::SC_TitleBarMinButton: + case TQStyle::SC_TitleBarMaxButton: + case TQStyle::SC_TitleBarCloseButton: + { + TQStyle::SCFlags last_ctrl = d->buttonDown; + d->buttonDown = style().querySubControl(TQStyle::CC_TitleBar, this, e->pos()); + if( d->buttonDown != last_ctrl) + d->buttonDown = TQStyle::SC_None; + repaint(FALSE); + d->buttonDown = last_ctrl; + } + break; + + case TQStyle::SC_TitleBarLabel: + if ( d->buttonDown == TQStyle::SC_TitleBarLabel && d->movable && d->pressed ) { + if ( (d->moveOffset - mapToParent( e->pos() ) ).manhattanLength() >= 4 ) { + TQPoint p = mapFromGlobal(e->globalPos()); +#ifndef QT_NO_WORKSPACE + if(d->window && d->window->parentWidget()->inherits("TQWorkspaceChild")) { + TQWorkspace *workspace = ::qt_cast<TQWorkspace*>(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 + TQPoint pp = p - d->moveOffset; + if (!parentWidget()->isMaximized()) + parentWidget()->move( pp ); + } + } else { + TQStyle::SCFlags last_ctrl = d->buttonDown; + d->buttonDown = TQStyle::SC_None; + if( d->buttonDown != last_ctrl) + repaint(FALSE); + } + break; + } +} + +void TQTitleBar::resizeEvent( TQResizeEvent *r) +{ + TQWidget::resizeEvent(r); + cutText(); +} + +void TQTitleBar::paintEvent(TQPaintEvent *) +{ + TQStyle::SCFlags ctrls = TQStyle::SC_TitleBarLabel; + if ( testWFlags( WStyle_SysMenu) ) { + if ( testWFlags( WStyle_Tool ) ) { + ctrls |= TQStyle::SC_TitleBarCloseButton; + if ( d->window && testWFlags( WStyle_MinMax ) ) { + if ( d->window->isMinimized() ) + ctrls |= TQStyle::SC_TitleBarUnshadeButton; + else + ctrls |= TQStyle::SC_TitleBarShadeButton; + } + } else { + ctrls |= TQStyle::SC_TitleBarSysMenu | TQStyle::SC_TitleBarCloseButton; + if ( d->window && testWFlags( WStyle_Minimize ) ) { + if( d->window && d->window->isMinimized() ) + ctrls |= TQStyle::SC_TitleBarNormalButton; + else + ctrls |= TQStyle::SC_TitleBarMinButton; + } + if ( d->window && testWFlags( WStyle_Maximize ) && !d->window->isMaximized() ) + ctrls |= TQStyle::SC_TitleBarMaxButton; + } + } + + TQStyle::SCFlags under_mouse = TQStyle::SC_None; + if( autoRaise() && hasMouse() ) { + TQPoint p(mapFromGlobal(TQCursor::pos())); + under_mouse = style().querySubControl(TQStyle::CC_TitleBar, this, p); + ctrls ^= under_mouse; + } + + TQSharedDoubleBuffer buffer( this, rect() ); + style().drawComplexControl(TQStyle::CC_TitleBar, buffer.painter(), this, rect(), + colorGroup(), + isEnabled() ? TQStyle::Style_Enabled : + TQStyle::Style_Default, ctrls, d->buttonDown); + if(under_mouse != TQStyle::SC_None) + style().drawComplexControl(TQStyle::CC_TitleBar, buffer.painter(), this, rect(), + colorGroup(), + TQStyle::Style_MouseOver | + (isEnabled() ? TQStyle::Style_Enabled : 0), + under_mouse, d->buttonDown); +} + +void TQTitleBar::mouseDoubleClickEvent( TQMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + + switch(style().querySubControl(TQStyle::CC_TitleBar, this, e->pos())) { + case TQStyle::SC_TitleBarLabel: + emit doubleClicked(); + break; + + case TQStyle::SC_TitleBarSysMenu: + if ( testWFlags( WStyle_SysMenu ) ) + emit doClose(); + break; + + default: + break; + } +} + +#ifdef QT_NO_WIDGET_TOPEXTRA +// We provide one, since titlebar is useless otherwise. +TQString TQTitleBar::caption() const +{ + return d->cap; +} +#endif + +void TQTitleBar::cutText() +{ + TQFontMetrics fm( font() ); + + int maxw = style().querySubControlMetrics(TQStyle::CC_TitleBar, this, + TQStyle::SC_TitleBarLabel).width(); + if ( !d->window ) + maxw = width() - 20; + const TQString 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 TQTitleBar::setCaption( const TQString& title ) +{ + if( caption() == title) + return; +#ifndef QT_NO_WIDGET_TOPEXTRA + TQWidget::setCaption( title ); +#else + d->cap = title; +#endif + cutText(); + + update(); +} + + +void TQTitleBar::setIcon( const TQPixmap& icon ) +{ +#ifndef QT_NO_WIDGET_TOPEXTRA +#ifndef QT_NO_IMAGE_SMOOTHSCALE + TQRect menur = style().querySubControlMetrics(TQStyle::CC_TitleBar, this, + TQStyle::SC_TitleBarSysMenu); + + TQPixmap 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; + + TQWidget::setIcon( theIcon ); +#else + TQWidget::setIcon( icon ); +#endif + + update(); +#endif +} + +void TQTitleBar::leaveEvent( TQEvent * ) +{ + if(autoRaise() && !d->pressed) + repaint( FALSE ); +} + +void TQTitleBar::enterEvent( TQEvent * ) +{ + if(autoRaise() && !d->pressed) + repaint( FALSE ); + TQEvent e( TQEvent::Leave ); + TQApplication::sendEvent( parentWidget(), &e ); +} + +void TQTitleBar::setActive( bool active ) +{ + if ( d->act == active ) + return ; + + d->act = active; + update(); +} + +bool TQTitleBar::isActive() const +{ + return d->act; +} + +bool TQTitleBar::usesActiveColor() const +{ + return ( isActive() && isActiveWindow() ) || + ( !window() && topLevelWidget()->isActiveWindow() ); +} + +TQString TQTitleBar::visibleText() const +{ + return d->cuttext; +} + +TQWidget *TQTitleBar::window() const +{ + return d->window; +} + +bool TQTitleBar::event( TQEvent* e ) +{ + if ( e->type() == TQEvent::ApplicationPaletteChange ) { + readColors(); + return TRUE; + } else if ( e->type() == TQEvent::WindowActivate ) { + setActive( d->act ); + } else if ( e->type() == TQEvent::WindowDeactivate ) { + bool wasActive = d->act; + setActive( FALSE ); + d->act = wasActive; + } + + return TQWidget::event( e ); +} + +void TQTitleBar::setMovable(bool b) +{ + d->movable = b; +} + +bool TQTitleBar::isMovable() const +{ + return d->movable; +} + +void TQTitleBar::setAutoRaise(bool b) +{ + d->autoraise = b; +} + +bool TQTitleBar::autoRaise() const +{ + return d->autoraise; +} + +TQSize TQTitleBar::sizeHint() const +{ + constPolish(); + TQRect menur = style().querySubControlMetrics(TQStyle::CC_TitleBar, this, + TQStyle::SC_TitleBarSysMenu); + return TQSize( menur.width(), style().pixelMetric( TQStyle::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 000000000..25b66a70d --- /dev/null +++ b/src/widgets/qtitlebar_p.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Definition of some TQt private functions. +** +** Created : 000101 +** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQTITLEBAR_P_H +#define TQTITLEBAR_P_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the TQt 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 TQToolTip; +class TQTitleBarPrivate; +class TQPixmap; + +class Q_EXPORT TQTitleBar : public TQWidget +{ + Q_OBJECT + Q_PROPERTY( bool autoRaise READ autoRaise WRITE setAutoRaise ) + Q_PROPERTY( bool movable READ isMovable WRITE setMovable ) + +public: + TQTitleBar (TQWidget* w, TQWidget* parent, const char* name=0); + ~TQTitleBar(); + + bool isActive() const; + bool usesActiveColor() const; + virtual TQString visibleText() const; + + bool isMovable() const; + void setMovable(bool); + + bool autoRaise() const; + void setAutoRaise(bool); + + TQWidget *window() const; + + TQSize sizeHint() const; + +#ifdef QT_NO_WIDGET_TOPEXTRA + // We provide one, since titlebar is useless otherwise. + TQString caption() const; +#endif + +public slots: + void setActive( bool ); + void setCaption( const TQString& title ); + void setIcon( const TQPixmap& icon ); + +signals: + void doActivate(); + void doNormal(); + void doClose(); + void doMaximize(); + void doMinimize(); + void doShade(); + void showOperationMenu(); + void popupOperationMenu( const TQPoint& ); + void doubleClicked(); + +protected: + bool event( TQEvent *); + void resizeEvent( TQResizeEvent *); + void contextMenuEvent( TQContextMenuEvent * ); + void mousePressEvent( TQMouseEvent * ); + void mouseDoubleClickEvent( TQMouseEvent * ); + void mouseReleaseEvent( TQMouseEvent * ); + void mouseMoveEvent( TQMouseEvent * ); + void enterEvent( TQEvent *e ); + void leaveEvent( TQEvent *e ); + void paintEvent( TQPaintEvent *p ); + + virtual void cutText(); + +private: + void readColors(); + + TQTitleBarPrivate *d; +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + TQTitleBar( const TQTitleBar & ); + TQTitleBar &operator=( const TQTitleBar & ); +#endif +}; + +#endif +#endif //TQTITLEBAR_P_H diff --git a/src/widgets/qtoolbar.cpp b/src/widgets/qtoolbar.cpp new file mode 100644 index 000000000..0b7005738 --- /dev/null +++ b/src/widgets/qtoolbar.cpp @@ -0,0 +1,815 @@ +/**************************************************************************** +** +** Implementation of TQToolBar class +** +** Created : 980315 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQToolBarExtensionWidget; + +class TQToolBarPrivate +{ +public: + TQToolBarPrivate() : moving( FALSE ) { + } + + bool moving; + TQToolBarExtensionWidget *extension; + TQPopupMenu *extensionPopup; +}; + + +class TQToolBarSeparator : public TQWidget +{ + Q_OBJECT +public: + TQToolBarSeparator( Orientation, TQToolBar *parent, const char* name=0 ); + + TQSize sizeHint() const; + Orientation orientation() const { return orient; } +public slots: + void setOrientation( Orientation ); +protected: + void styleChange( TQStyle& ); + void paintEvent( TQPaintEvent * ); + +private: + Orientation orient; +}; + +class TQToolBarExtensionWidget : public TQWidget +{ + Q_OBJECT + +public: + TQToolBarExtensionWidget( TQWidget *w ); + void setOrientation( Orientation o ); + TQToolButton *button() const { return tb; } + +protected: + void resizeEvent( TQResizeEvent *e ) { + TQWidget::resizeEvent( e ); + layOut(); + } + +private: + void layOut(); + TQToolButton *tb; + Orientation orient; + +}; + +TQToolBarExtensionWidget::TQToolBarExtensionWidget( TQWidget *w ) + : TQWidget( w, "qt_dockwidget_internal" ) +{ + tb = new TQToolButton( this, "qt_toolbar_ext_button" ); + tb->setAutoRaise( TRUE ); + setOrientation( Horizontal ); +} + +void TQToolBarExtensionWidget::setOrientation( Orientation o ) +{ + orient = o; + if ( orient == Horizontal ) + tb->setPixmap( TQPixmap( (const char **)arrow_h_xpm ) ); + else + tb->setPixmap( TQPixmap( (const char **)arrow_v_xpm ) ); + layOut(); +} + +void TQToolBarExtensionWidget::layOut() +{ + tb->setGeometry( 2, 2, width() - 4, height() - 4 ); +} + +TQToolBarSeparator::TQToolBarSeparator(Orientation o , TQToolBar *parent, + const char* name ) + : TQWidget( parent, name ) +{ + connect( parent, SIGNAL(orientationChanged(Orientation)), + this, SLOT(setOrientation(Orientation)) ); + setOrientation( o ); + setBackgroundMode( parent->backgroundMode() ); + setBackgroundOrigin( ParentOrigin ); + setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Minimum ) ); +} + + + +void TQToolBarSeparator::setOrientation( Orientation o ) +{ + orient = o; +} + +void TQToolBarSeparator::styleChange( TQStyle& ) +{ + setOrientation( orient ); +} + +TQSize TQToolBarSeparator::sizeHint() const +{ + int extent = style().pixelMetric( TQStyle::PM_DockWindowSeparatorExtent, + this ); + if ( orient == Horizontal ) + return TQSize( extent, 0 ); + else + return TQSize( 0, extent ); +} + +void TQToolBarSeparator::paintEvent( TQPaintEvent * ) +{ + TQPainter p( this ); + TQStyle::SFlags flags = TQStyle::Style_Default; + + if ( orientation() == Horizontal ) + flags |= TQStyle::Style_Horizontal; + + style().drawPrimitive( TQStyle::PE_DockWindowSeparator, &p, rect(), + colorGroup(), flags ); +} + +#include "qtoolbar.moc" + + +/*! + \class TQToolBar qtoolbar.h + \brief The TQToolBar 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 tquick + access to frequently used commands or options. Within a + TQMainWindow the user can drag toolbars within and between the + \link TQDockArea dock areas\endlink. Toolbars can also be dragged + out of any dock area to float freely as top-level windows. + + TQToolBar is a specialization of TQDockWindow, and so provides all + the functionality of a TQDockWindow. + + To use TQToolBar you simply create a TQToolBar as a child of a + TQMainWindow, create a number of TQToolButton 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 TQToolBar + \printuntil fileSaveAction + + This extract from the \l application/application.cpp example shows + the creation of a new toolbar as a child of a TQMainWindow and + adding two TQActions. + + You may use most widgets within a toolbar, with TQToolButton and + TQComboBox being the most common. + + If you create a new widget on an already visible TQToolBar, this + widget will automatically become visible without needing a show() + call. (This differs from every other TQt widget container. We + recommend calling show() anyway since we hope to fix this anomaly + in a future release.) + + TQToolBars, like TQDockWindows, are located in \l{TQDockArea}s or + float as top-level windows. TQMainWindow provides four TQDockAreas + (top, left, right and bottom). When you create a new toolbar (as + in the example above) as a child of a TQMainWindow the toolbar will + be added to the top dock area. You can move it to another dock + area (or float it) by calling TQMainWindow::moveDockWindow(). TQDock + 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. TQToolButtons are represented in the menu using + their textLabel property, other TQButton subclasses are represented + using their text property, and TQComboBoxes 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, TQDockArea 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 TQToolbar (dock window) + + \sa TQToolButton TQMainWindow \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 TQToolBar::TQToolBar( const TQString &label, + TQMainWindow *, 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. +*/ + +TQToolBar::TQToolBar( const TQString &label, + TQMainWindow * parent, TQMainWindow::ToolBarDock dock, + bool newLine, const char * name ) + : TQDockWindow( 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 TQMainWindow::addDockWindow(). \a name and + the widget flags \a f are passed on to the TQDockWindow constructor. + + Use this constructor if you want to create torn-off (undocked, + floating) toolbars or toolbars in the \link TQStatusBar status + bar\endlink. +*/ + +TQToolBar::TQToolBar( const TQString &label, TQMainWindow * mainWindow, + TQWidget * parent, bool newLine, const char * name, + WFlags f ) + : TQDockWindow( InDock, parent, name, f, TRUE ) +{ + mw = mainWindow; + init(); + + clearWFlags( WType_Dialog | WStyle_Customize | WStyle_NoBorder ); + reparent( parent, TQPoint( 0, 0 ), FALSE ); + + if ( mainWindow ) + mainWindow->addToolBar( this, label, TQMainWindow::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 + retquiring a newline. +*/ + +TQToolBar::TQToolBar( TQMainWindow * parent, const char * name ) + : TQDockWindow( InDock, parent, name, 0, TRUE ) +{ + mw = parent; + init(); + + if ( parent ) + parent->addToolBar( this, TQString::null, TQMainWindow::DockTop ); +} + +/*! + \internal + + Common initialization code. Retquires that \c mw and \c o are set. + Does not call TQMainWindow::addDockWindow(). +*/ +void TQToolBar::init() +{ + d = new TQToolBarPrivate; + d->extension = 0; + d->extensionPopup = 0; + sw = 0; + + setBackgroundMode( PaletteButton); + setFocusPolicy( NoFocus ); + setFrameStyle( TQFrame::ToolBarPanel | TQFrame::Raised); + boxLayout()->setSpacing(style().pixelMetric(TQStyle::PM_ToolBarItemSpacing)); +} + +/*! + \reimp +*/ + +TQToolBar::~TQToolBar() +{ + delete d; + d = 0; +} + +/*! + \reimp +*/ + +void TQToolBar::setOrientation( Orientation o ) +{ + TQDockWindow::setOrientation( o ); + if (d->extension) + d->extension->setOrientation( o ); + TQObjectList *childs = queryList( "TQToolBarSeparator" ); + if ( childs ) { + TQObject *ob = 0; + for ( ob = childs->first(); ob; ob = childs->next() ) { + TQToolBarSeparator* w = (TQToolBarSeparator*)ob; + w->setOrientation( o ); + } + } + delete childs; +} + +/*! + Adds a separator to the right/bottom of the toolbar. +*/ + +void TQToolBar::addSeparator() +{ + (void) new TQToolBarSeparator( orientation(), this, "toolbar separator" ); +} + +/*! + \reimp +*/ + +void TQToolBar::styleChange( TQStyle& ) +{ + TQObjectList *childs = queryList( "TQWidget" ); + if ( childs ) { + TQObject *ob = 0; + for ( ob = childs->first(); ob; ob = childs->next() ) { + TQWidget *w = (TQWidget*)ob; + if ( ::qt_cast<TQToolButton*>(w) || ::qt_cast<TQToolBarSeparator*>(w) ) + w->setStyle( &style() ); + } + } + delete childs; + boxLayout()->setSpacing(style().pixelMetric(TQStyle::PM_ToolBarItemSpacing)); +} + +/*! + \reimp. +*/ + +void TQToolBar::show() +{ + TQDockWindow::show(); + if ( mw ) + mw->triggerLayout( FALSE ); + checkForExtension( size() ); +} + + +/*! + \reimp +*/ + +void TQToolBar::hide() +{ + TQDockWindow::hide(); + if ( mw ) + mw->triggerLayout( FALSE ); +} + +/*! + Returns a pointer to the TQMainWindow which manages this toolbar. +*/ + +TQMainWindow * TQToolBar::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 TQMainWindow + 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 TQMainWindow::setRightJustification(), setVerticalStretchable(), + setHorizontalStretchable() +*/ + +void TQToolBar::setStretchableWidget( TQWidget * w ) +{ + sw = w; + boxLayout()->setStretchFactor( w, 1 ); + + if ( !isHorizontalStretchable() && !isVerticalStretchable() ) { + if ( orientation() == Horizontal ) + setHorizontalStretchable( TRUE ); + else + setVerticalStretchable( TRUE ); + } +} + + +/*! + \reimp +*/ + +bool TQToolBar::event( TQEvent * e ) +{ + bool r = TQDockWindow::event( e ); + // After the event filters have dealt with it, do our stuff. + if ( e->type() == TQEvent::ChildInserted ) { + TQObject * child = ((TQChildEvent*)e)->child(); + if ( child && child->isWidgetType() && !((TQWidget*)child)->isTopLevel() + && child->parent() == this + && qstrcmp("qt_dockwidget_internal", child->name()) != 0 ) { + boxLayout()->addWidget( (TQWidget*)child ); + if ( isVisible() ) { + if ( ((TQWidget*)child)->testWState( WState_CreatedHidden ) ) + ((TQWidget*)child)->show(); + checkForExtension( size() ); + } + } + if ( child && child->isWidgetType() && ((TQWidget*)child) == sw ) + boxLayout()->setStretchFactor( (TQWidget*)child, 1 ); + } else if ( e->type() == TQEvent::Show ) { + layout()->activate(); + } else if ( e->type() == TQEvent::LayoutHint && place() == OutsideDock ) { + adjustSize(); + } + return r; +} + + +/*! + \property TQToolBar::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 TQToolBar::setLabel( const TQString & label ) +{ + l = label; + setCaption( l ); +} + +TQString TQToolBar::label() const +{ + return l; +} + + +/*! + Deletes all the toolbar's child widgets. +*/ + +void TQToolBar::clear() +{ + if ( !children() ) + return; + TQObjectListIt it( *children() ); + TQObject * obj; + while( (obj=it.current()) != 0 ) { + ++it; + if ( obj->isWidgetType() && + qstrcmp( "qt_dockwidget_internal", obj->name() ) != 0 ) + delete obj; + } +} + +/*! + \reimp +*/ + +TQSize TQToolBar::minimumSize() const +{ + if ( orientation() == Horizontal ) + return TQSize( 0, TQDockWindow::minimumSize().height() ); + return TQSize( TQDockWindow::minimumSize().width(), 0 ); +} + +/*! + \reimp +*/ + +TQSize TQToolBar::minimumSizeHint() const +{ + if ( orientation() == Horizontal ) + return TQSize( 0, TQDockWindow::minimumSizeHint().height() ); + return TQSize( TQDockWindow::minimumSizeHint().width(), 0 ); +} + +void TQToolBar::createPopup() +{ + if (!d->extensionPopup) { + d->extensionPopup = new TQPopupMenu( this, "qt_dockwidget_internal" ); + connect( d->extensionPopup, SIGNAL( aboutToShow() ), this, SLOT( createPopup() ) ); + } + + if (!d->extension) { + d->extension = new TQToolBarExtensionWidget( 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 + TQObjectList *childlist = d->extensionPopup->queryList( "TQPopupMenu", 0, FALSE, TRUE ); + childlist->setAutoDelete(TRUE); + delete childlist; + + childlist = queryList( "TQWidget", 0, FALSE, TRUE ); + TQObjectListIt 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; + } + TQWidget *w = (TQWidget*)it.current(); +#ifndef QT_NO_COMBOBOX + if ( ::qt_cast<TQComboBox*>(w) ) + j = 1; +#endif + hide = FALSE; + TQPoint 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<TQToolButton*>(w) ) { + TQToolButton *b = (TQToolButton*)w; + TQString 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<TQButton*>(w) ) { + TQButton *b = (TQButton*)w; + TQString 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<TQComboBox*>(w) ) { + TQComboBox *c = (TQComboBox*)w; + if ( c->count() != 0 ) { +#ifndef QT_NO_WIDGET_TOPEXTRA + TQString s = c->caption(); +#else + TQString s; +#endif + if ( s.isEmpty() ) + s = c->currentText(); + uint maxItems = 0; + TQPopupMenu *cp = new TQPopupMenu(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 ) { + TQString 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 = TQApplication::desktop()->height() * 10 / h; + } + } else if ( cp->count() >= maxItems - 1 ) { + TQPopupMenu* sp = new TQPopupMenu(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 TQToolBar::resizeEvent( TQResizeEvent *e ) +{ + checkForExtension( e->size() ); +} + +void TQToolBar::checkForExtension( const TQSize &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 TQToolBar::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 000000000..3d60fd5ed --- /dev/null +++ b/src/widgets/qtoolbar.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Definition of TQToolBar class +** +** Created : 980306 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQTOOLBAR_H +#define TQTOOLBAR_H + +#ifndef QT_H +#include "qdockwindow.h" +#endif // QT_H + +#ifndef QT_NO_TOOLBAR + +class TQMainWindow; +class TQButton; +class TQBoxLayout; +class TQToolBarPrivate; + +class Q_EXPORT TQToolBar: public TQDockWindow +{ + Q_OBJECT + Q_PROPERTY( TQString label READ label WRITE setLabel ) + +public: + TQToolBar( const TQString &label, + TQMainWindow *, ToolBarDock = DockTop, + bool newLine = FALSE, const char* name=0 ); + TQToolBar( const TQString &label, TQMainWindow *, TQWidget *, + bool newLine = FALSE, const char* name=0, WFlags f = 0 ); + TQToolBar( TQMainWindow* parent=0, const char* name=0 ); + ~TQToolBar(); + + void addSeparator(); + + void show(); + void hide(); + + TQMainWindow * mainWindow() const; + + virtual void setStretchableWidget( TQWidget * ); + + bool event( TQEvent * e ); + + virtual void setLabel( const TQString & ); + TQString label() const; + + virtual void clear(); + + TQSize minimumSize() const; + TQSize minimumSizeHint() const; + + void setOrientation( Orientation o ); + void setMinimumSize( int minw, int minh ); + +protected: + void resizeEvent( TQResizeEvent *e ); + void styleChange( TQStyle & ); + +private slots: + void createPopup(); + +private: + void init(); + void checkForExtension( const TQSize &sz ); + TQToolBarPrivate * d; + TQMainWindow * mw; + TQWidget * sw; + TQString l; + + friend class TQMainWindow; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQToolBar( const TQToolBar & ); + TQToolBar& operator=( const TQToolBar & ); +#endif +}; + +#endif // QT_NO_TOOLBAR + +#endif // TQTOOLBAR_H diff --git a/src/widgets/qtoolbox.cpp b/src/widgets/qtoolbox.cpp new file mode 100644 index 000000000..86441878d --- /dev/null +++ b/src/widgets/qtoolbox.cpp @@ -0,0 +1,692 @@ +/**************************************************************************** +** +** Implementation of TQToolBox widget class +** +** Created : 961105 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQToolBoxButton : public TQButton +{ +public: + TQToolBoxButton( TQWidget *parent, const char *name ) + : TQButton( parent, name ), selected( FALSE ) + { + setBackgroundMode(PaletteBackground); + setSizePolicy(TQSizePolicy::Preferred, TQSizePolicy::Minimum); + setFocusPolicy(NoFocus); + } + + inline void setSelected( bool b ) { selected = b; update(); } + inline void setTextLabel( const TQString &text ) { label = text; update(); } + inline TQString textLabel() const { return label; } + inline void setIconSet( const TQIconSet &is ) { icon = is; update(); } + inline TQIconSet iconSet() const { return icon; } + + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + +protected: + void drawButton( TQPainter * ); + +private: + bool selected; + TQString label; + TQIconSet icon; +}; + +class TQToolBoxPrivate +{ +public: + struct Page + { + TQToolBoxButton *button; + TQScrollView *sv; + TQWidget *widget; + TQString toolTip; + + inline void setTextLabel( const TQString &text ) { button->setTextLabel(text); } + inline void setIconSet( const TQIconSet &is ) { button->setIconSet(is); } + inline void setToolTip( const TQString &tip ) + { + toolTip = tip; + TQToolTip::remove( button ); + if ( !tip.isNull() ) + TQToolTip::add( button, tip ); + } + + inline bool operator==(const Page& other) const + { + return widget == other.widget; + } + }; + typedef TQValueList<Page> PageList; + + inline TQToolBoxPrivate() + : currentPage( 0 ) + { + } + + Page *page( TQWidget *widget ); + Page *page( int index ); + + void updateTabs(); + + PageList pageList; + TQVBoxLayout *layout; + Page *currentPage; +}; + +TQToolBoxPrivate::Page *TQToolBoxPrivate::page( TQWidget *widget ) +{ + if ( !widget ) + return 0; + + for ( PageList::ConstIterator i = pageList.constBegin(); i != pageList.constEnd(); ++i ) + if ( (*i).widget == widget ) + return (Page*) &(*i); + return 0; +} + +TQToolBoxPrivate::Page *TQToolBoxPrivate::page( int index ) +{ + if (index >= 0 && index < (int)pageList.size() ) + return &*pageList.at(index); + return 0; +} + +void TQToolBoxPrivate::updateTabs() +{ + TQToolBoxButton *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() != TQt::PaletteBackground ) { + (*i).button->setBackgroundMode( TQt::PaletteBackground ); + (*i).button->update(); + } + after = (*i).button == lastButton; + } +} + +TQSize TQToolBoxButton::sizeHint() const +{ + TQSize iconSize(8, 8); + if ( !icon.isNull() ) + iconSize += icon.pixmap( TQIconSet::Small, TQIconSet::Normal ).size() + TQSize( 2, 0 ); + TQSize textSize = fontMetrics().size( TQt::ShowPrefix, label ) + TQSize(0, 8); + + TQSize total(iconSize.width() + textSize.width(), TQMAX(iconSize.height(), textSize.height())); + return total.expandedTo(TQApplication::globalStrut()); +} + +TQSize TQToolBoxButton::minimumSizeHint() const +{ + if ( icon.isNull() ) + return TQSize(); + return TQSize(8, 8) + icon.pixmap( TQIconSet::Small, TQIconSet::Normal ).size(); +} + +void TQToolBoxButton::drawButton( TQPainter *p ) +{ + TQStyle::SFlags flags = TQStyle::Style_Default; + const TQColorGroup &cg = colorGroup(); + + if ( isEnabled() ) + flags |= TQStyle::Style_Enabled; + if ( selected ) + flags |= TQStyle::Style_Selected; + if ( hasFocus() ) + flags |= TQStyle::Style_HasFocus; + if (isDown()) + flags |= TQStyle::Style_Down; + style().drawControl( TQStyle::CE_ToolBoxTab, p, parentWidget(), rect(), cg, flags ); + + TQPixmap pm = icon.pixmap( TQIconSet::Small, isEnabled() ? TQIconSet::Normal : TQIconSet::Disabled ); + + TQRect cr = style().subRect( TQStyle::SR_ToolBoxTabContents, this ); + TQRect 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 = TQRect( cr.left() + 4, cr.top(), iw + 2, ih ); + tr = TQRect( ir.right(), cr.top(), cr.width() - ir.right() - 4, cr.height() ); + } + + if ( selected && style().styleHint( TQStyle::SH_ToolBox_SelectedPageTitleBold ) ) { + TQFont f( p->font() ); + f.setBold( TRUE ); + p->setFont( f ); + } + + TQString 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 ); + + TQToolBox *tb = (TQToolBox*)parentWidget(); + + const TQColor* fill = 0; + if ( selected && + style().styleHint( TQStyle::SH_ToolBox_SelectedPageTitleBold ) && + tb->backgroundMode() != NoBackground ) + fill = &cg.color( TQPalette::foregroundRoleFromMode( tb->backgroundMode() ) ); + + int alignment = AlignLeft | AlignVCenter | ShowPrefix; + if (!style().styleHint(TQStyle::SH_UnderlineAccelerator, this)) + alignment |= NoAccel; + style().drawItem( p, tr, alignment, cg, + isEnabled(), 0, txt, -1, fill ); + + if ( !txt.isEmpty() && hasFocus() ) + style().drawPrimitive( TQStyle::PE_FocusRect, p, tr, cg ); +} + +/*! + \class TQToolBox + + \brief The TQToolBox 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 TQWidget. + + 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 TQTabWidget +*/ + +/*! + \fn void TQToolBox::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. +*/ + +TQToolBox::TQToolBox( TQWidget *parent, const char *name, WFlags f ) + : TQFrame( parent, name, f ) +{ + d = new TQToolBoxPrivate; + d->layout = new TQVBoxLayout( this ); + TQWidget::setBackgroundMode( PaletteButton ); +} + +/*! \reimp */ + +TQToolBox::~TQToolBox() +{ + delete d; +} + +/*! + \fn int TQToolBox::addItem( TQWidget *w, const TQString &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 TQToolBox::addItem( TQWidget *item, const TQIconSet &iconSet,const TQString &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 TQToolBox::insertItem( int index, TQWidget *item, const TQString &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 TQToolBox::insertItem( int index, TQWidget *item, const TQIconSet &iconSet, + const TQString &label ) +{ + if ( !item ) + return -1; + + connect(item, SIGNAL(destroyed(TQObject*)), this, SLOT(itemDestroyed(TQObject*))); + + TQToolBoxPrivate::Page c; + c.widget = item; + c.button = new TQToolBoxButton( this, label.latin1() ); + connect( c.button, SIGNAL( clicked() ), this, SLOT( buttonClicked() ) ); + + c.sv = new TQScrollView( this ); + c.sv->hide(); + c.sv->setResizePolicy( TQScrollView::AutoOneFit ); + c.sv->addChild( item ); + c.sv->setFrameStyle( TQFrame::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) { + TQWidget *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 TQToolBox::buttonClicked() +{ + TQToolBoxButton *tb = ::qt_cast<TQToolBoxButton*>(sender()); + TQWidget* item = 0; + for ( TQToolBoxPrivate::PageList::ConstIterator i = d->pageList.constBegin(); i != d->pageList.constEnd(); ++i ) + if ( (*i).button == tb ) { + item = (*i).widget; + break; + } + + setCurrentItem( item ); +} + +/*! + \property TQToolBox::count + \brief The number of items contained in the toolbox. +*/ + +int TQToolBox::count() const +{ + return (int)d->pageList.count(); +} + +void TQToolBox::setCurrentIndex( int index ) +{ + setCurrentItem( item( index ) ); +} + +/*! + Sets the current item to be \a item. +*/ + +void TQToolBox::setCurrentItem( TQWidget *item ) +{ + TQToolBoxPrivate::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 TQToolBox::relayout() +{ + delete d->layout; + d->layout = new TQVBoxLayout( this ); + for ( TQToolBoxPrivate::PageList::ConstIterator i = d->pageList.constBegin(); i != d->pageList.constEnd(); ++i ) { + d->layout->addWidget( (*i).button ); + d->layout->addWidget( (*i).sv ); + } +} + +void TQToolBox::itemDestroyed(TQObject *object) +{ + // no verification - vtbl corrupted already + TQWidget *page = (TQWidget*)object; + + TQToolBoxPrivate::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 TQToolBox::removeItem( TQWidget *item ) +{ + int index = indexOf(item); + if (index >= 0) { + disconnect(item, SIGNAL(destroyed(TQObject*)), this, SLOT(itemDestroyed(TQObject*))); + item->reparent( this, TQPoint(0,0) ); + // destroy internal data + itemDestroyed(item); + } + itemRemoved(index); + return index; +} + + +/*! + Returns the toolbox's current item, or 0 if the toolbox is empty. +*/ + +TQWidget *TQToolBox::currentItem() const +{ + return d->currentPage ? d->currentPage->widget : 0; +} + +/*! + \property TQToolBox::currentIndex + \brief the index of the current item, or -1 if the toolbox is empty. + \sa currentItem(), indexOf(), item() +*/ + + +int TQToolBox::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. +*/ + +TQWidget *TQToolBox::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 TQToolBox::indexOf( TQWidget *item ) const +{ + TQToolBoxPrivate::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 TQToolBox::setItemEnabled( int index, bool enabled ) +{ + TQToolBoxPrivate::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 TQToolBox::setItemLabel( int index, const TQString &label ) +{ + TQToolBoxPrivate::Page *c = d->page( index ); + if ( c ) + c->setTextLabel( label ); +} + +/*! + Sets the icon of the item at position \a index to \a iconSet. +*/ + +void TQToolBox::setItemIconSet( int index, const TQIconSet &iconSet ) +{ + TQToolBoxPrivate::Page *c = d->page( index ); + if ( c ) + c->setIconSet( iconSet ); +} + +/*! + Sets the tooltip of the item at position \a index to \a toolTip. +*/ + +void TQToolBox::setItemToolTip( int index, const TQString &toolTip ) +{ + TQToolBoxPrivate::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 TQToolBox::isItemEnabled( int index ) const +{ + TQToolBoxPrivate::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. +*/ + +TQString TQToolBox::itemLabel( int index ) const +{ + TQToolBoxPrivate::Page *c = d->page( index ); + return ( c ? c->button->textLabel() : TQString::null ); +} + +/*! + Returns the icon of the item at position \a index, or a null + icon if \a index is out of range. +*/ + +TQIconSet TQToolBox::itemIconSet( int index ) const +{ + TQToolBoxPrivate::Page *c = d->page( index ); + return ( c ? c->button->iconSet() : TQIconSet() ); +} + +/*! + Returns the tooltip of the item at position \a index, or a null + string if \a index is out of range. +*/ + +TQString TQToolBox::itemToolTip( int index ) const +{ + TQToolBoxPrivate::Page *c = d->page( index ); + return ( c ? c->toolTip : TQString::null ); +} + +/*! \reimp */ +void TQToolBox::showEvent( TQShowEvent *e ) +{ + TQWidget::showEvent( e ); +} + +/*! \reimp */ +void TQToolBox::frameChanged() +{ + d->layout->setMargin( frameWidth() ); + TQFrame::frameChanged(); +} + +/*! \reimp */ +void TQToolBox::styleChange(TQStyle &style) +{ + d->updateTabs(); + TQFrame::styleChange(style); +} + +/*! + This virtual handler is called after a new item was added or + inserted at position \a index. + */ +void TQToolBox::itemInserted( int index ) +{ + Q_UNUSED(index) +} + +/*! + This virtual handler is called after an item was removed from + position \a index. + */ +void TQToolBox::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 000000000..d5ee685fe --- /dev/null +++ b/src/widgets/qtoolbox.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Definition of TQToolBox widget class +** +** Created : 961105 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQTOOLBOX_H +#define TQTOOLBOX_H + +#ifndef QT_H +#include <qframe.h> +#include <qiconset.h> +#endif // QT_H + +#ifndef QT_NO_TOOLBOX + +class TQToolBoxPrivate; +class TQWidgetList; + +class Q_EXPORT TQToolBox : public TQFrame +{ + Q_OBJECT + Q_PROPERTY( int currentIndex READ currentIndex WRITE setCurrentIndex ) + Q_PROPERTY( int count READ count ) + +public: + TQToolBox( TQWidget *parent = 0, const char *name = 0, WFlags f = 0 ); + ~TQToolBox(); + + int addItem( TQWidget *item, const TQString &label ); + int addItem( TQWidget *item, const TQIconSet &iconSet, const TQString &label ); + int insertItem( int index, TQWidget *item, const TQString &label ); + int insertItem( int index, TQWidget *item, const TQIconSet &iconSet, const TQString &label ); + + int removeItem( TQWidget *item ); + + void setItemEnabled( int index, bool enabled ); + bool isItemEnabled( int index ) const; + + void setItemLabel( int index, const TQString &label ); + TQString itemLabel( int index ) const; + + void setItemIconSet( int index, const TQIconSet &iconSet ); + TQIconSet itemIconSet( int index ) const; + + void setItemToolTip( int index, const TQString &toolTip ); + TQString itemToolTip( int index ) const; + + TQWidget *currentItem() const; + void setCurrentItem( TQWidget *item ); + + int currentIndex() const; + TQWidget *item( int index ) const; + int indexOf( TQWidget *item ) const; + int count() const; + +public slots: + void setCurrentIndex( int index ); + +signals: + void currentChanged( int index ); + +private slots: + void buttonClicked(); + void itemDestroyed(TQObject*); + +protected: + virtual void itemInserted( int index ); + virtual void itemRemoved( int index ); + void showEvent( TQShowEvent *e ); + void frameChanged(); + void styleChange(TQStyle&); + +private: + void relayout(); + +private: + TQToolBoxPrivate *d; + +}; + + +inline int TQToolBox::addItem( TQWidget *item, const TQString &label ) +{ return insertItem( -1, item, TQIconSet(), label ); } +inline int TQToolBox::addItem( TQWidget *item, const TQIconSet &iconSet, + const TQString &label ) +{ return insertItem( -1, item, iconSet, label ); } +inline int TQToolBox::insertItem( int index, TQWidget *item, const TQString &label ) +{ return insertItem( index, item, TQIconSet(), label ); } + +#endif // QT_NO_TOOLBOX +#endif diff --git a/src/widgets/qtoolbutton.cpp b/src/widgets/qtoolbutton.cpp new file mode 100644 index 000000000..ac14adc1d --- /dev/null +++ b/src/widgets/qtoolbutton.cpp @@ -0,0 +1,1041 @@ +/**************************************************************************** +** +** Implementation of TQToolButton class +** +** Created : 980320 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#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 TQToolButtonPrivate +{ + // ### add tool tip magic here +public: +#ifndef QT_NO_POPUPMENU + TQGuardedPtr<TQPopupMenu> popup; + TQTimer* popupTimer; + int delay; +#endif + TQt::ArrowType arrow; + uint instantPopup : 1; + uint autoraise : 1; + uint repeat : 1; + uint discardNextMouseEvent : 1; + TQToolButton::TextPosition textPos; +}; + + +/*! + \class TQToolButton qtoolbutton.h + \brief The TQToolButton class provides a tquick-access button to + commands or options, usually used inside a TQToolBar. + + \ingroup basic + \mainclass + + A tool button is a special button that provides tquick-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 TQToolButton as toggle button (see setToggleButton() ). + + TQToolButton 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 TQToolBar. + Change it with setAutoRaise(). + + A tool button's icon is set as TQIconSet. 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 + TQToolBar in a TQMainWindow, the button automatically adjusts to + TQMainWindow's settings (see TQMainWindow::setUsesTextLabel() and + TQMainWindow::setUsesBigPixmaps()). The pixmap set on a TQToolButton + 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 + TQToolButton 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 + TQToolbar with TQToolbuttons + + \sa TQPushButton TQToolBar TQMainWindow \link guibooks.html#fowler + GUI Design Handbook: Push Button\endlink +*/ + +/*! + \enum TQToolButton::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. +*/ + +TQToolButton::TQToolButton( TQWidget * parent, const char *name ) + : TQButton( parent, name ) +{ + init(); +#ifndef QT_NO_TOOLBAR + TQToolBar* tb = ::qt_cast<TQToolBar*>(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 TQWidget + constructor. +*/ +TQToolButton::TQToolButton( ArrowType type, TQWidget *parent, const char *name ) + : TQButton( parent, name ) +{ + init(); + setUsesBigPixmap( FALSE ); + setAutoRepeat( TRUE ); + d->arrow = type; + hasArrow = TRUE; +} + + +/* Set-up code common to all the constructors */ + +void TQToolButton::init() +{ + d = new TQToolButtonPrivate; + 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( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Minimum ) ); +} + +#ifndef QT_NO_TOOLBAR + +/*! + Constructs a tool button called \a name, that is a child of \a + parent (which must be a TQToolBar). + + 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. +*/ + +TQToolButton::TQToolButton( const TQIconSet& iconSet, const TQString &textLabel, + const TQString& grouptext, + TQObject * receiver, const char *slot, + TQToolBar * parent, const char *name ) + : TQButton( 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() ) + TQToolTip::add( this, textLabel, + parent->mainWindow()->toolTipGroup(), grouptext ); + else + TQToolTip::add( this, textLabel ); + } else if ( !grouptext.isEmpty() && parent->mainWindow() ) + TQToolTip::add( this, TQString::null, + parent->mainWindow()->toolTipGroup(), grouptext ); +#endif +} + +#endif + + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQToolButton::~TQToolButton() +{ +#ifndef QT_NO_POPUPMENU + d->popupTimer = 0; + d->popup = 0; +#endif + delete d; + delete s; +} + + +/*! + \property TQToolButton::backgroundMode + \brief the toolbutton's background mode + + Get this property with backgroundMode(). + + \sa TQWidget::setBackgroundMode() +*/ + +/*! + \property TQToolButton::toggleButton + \brief whether this tool button is a toggle button. + + Toggle buttons have an on/off state similar to \link TQCheckBox + check boxes. \endlink A tool button is not a toggle button by + default. + + \sa setOn(), toggle() +*/ + +void TQToolButton::setToggleButton( bool enable ) +{ + TQButton::setToggleButton( enable ); +} + + +/*! + \reimp +*/ +TQSize TQToolButton::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() ) { + TQPixmap pm = iconSet().pixmap( TQIconSet::Large, TQIconSet::Normal ); + w = pm.width(); + h = pm.height(); + TQSize iconSize = TQIconSet::iconSize( TQIconSet::Large ); + if ( w < iconSize.width() ) + w = iconSize.width(); + if ( h < iconSize.height() ) + h = iconSize.height(); + } else if ( !iconSet().isNull() ) { + // ### in 3.1, use TQIconSet::iconSize( TQIconSet::Small ); + TQPixmap pm = iconSet().pixmap( TQIconSet::Small, TQIconSet::Normal ); + w = pm.width(); + h = pm.height(); + if ( w < 16 ) + w = 16; + if ( h < 16 ) + h = 16; + } + + if ( usesTextLabel() ) { + TQSize textSize = fontMetrics().size( TQt::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(TQStyle::PM_MenuButtonIndicator, this); +#endif + return (style().sizeFromContents(TQStyle::CT_ToolButton, this, TQSize(w, h)). + expandedTo(TQApplication::globalStrut())); +} + +/*! + \reimp + */ +TQSize TQToolButton::minimumSizeHint() const +{ + return sizeHint(); +} + +/*! + \property TQToolButton::usesBigPixmap + \brief whether this toolbutton uses big pixmaps. + + TQToolButton automatically connects this property to the relevant + signal in the TQMainWindow in which it resides. We strongly + recommend that you use TQMainWindow::setUsesBigPixmaps() instead. + + This property's default is TRUE. + + \warning If you set some buttons (in a TQMainWindow) to have big + pixmaps and others to have small pixmaps, TQMainWindow may not get + the geometry right. +*/ + +void TQToolButton::setUsesBigPixmap( bool enable ) +{ + if ( (bool)ubp == enable ) + return; + + ubp = enable; + if ( isVisible() ) { + update(); + updateGeometry(); + } +} + + +/*! + \property TQToolButton::usesTextLabel + \brief whether the toolbutton displays a text label below the button pixmap. + + The default is FALSE. + + TQToolButton automatically connects this slot to the relevant + signal in the TQMainWindow in which is resides. +*/ + +void TQToolButton::setUsesTextLabel( bool enable ) +{ + if ( (bool)utl == enable ) + return; + + utl = enable; + if ( isVisible() ) { + update(); + updateGeometry(); + } +} + + +/*! + \property TQToolButton::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 TQToolButton::setOn( bool enable ) +{ + if ( !isToggleButton() ) + return; + TQButton::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 TQToolButton::toggle() +{ + if ( !isToggleButton() ) + return; + TQButton::setOn( !isOn() ); +} + + +/*! + \reimp +*/ +void TQToolButton::drawButton( TQPainter * p ) +{ + TQStyle::SCFlags controls = TQStyle::SC_ToolButton; + TQStyle::SCFlags active = TQStyle::SC_None; + + TQt::ArrowType arrowtype = d->arrow; + + if (isDown()) + active |= TQStyle::SC_ToolButton; + +#ifndef QT_NO_POPUPMENU + if (d->popup && !d->delay) { + controls |= TQStyle::SC_ToolButtonMenu; + if (d->instantPopup || isDown()) + active |= TQStyle::SC_ToolButtonMenu; + } +#endif + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + if (isDown()) + flags |= TQStyle::Style_Down; + if (isOn()) + flags |= TQStyle::Style_On; + if (autoRaise()) { + flags |= TQStyle::Style_AutoRaise; + if (uses3D()) { + flags |= TQStyle::Style_MouseOver; + if (! isOn() && ! isDown()) + flags |= TQStyle::Style_Raised; + } + } else if (! isOn() && ! isDown()) + flags |= TQStyle::Style_Raised; + + style().drawComplexControl(TQStyle::CC_ToolButton, p, this, rect(), colorGroup(), + flags, controls, active, + hasArrow ? TQStyleOption(arrowtype) : + TQStyleOption()); + drawButtonLabel(p); +} + + +/*! + \reimp +*/ +void TQToolButton::drawButtonLabel(TQPainter *p) +{ + TQRect r = + TQStyle::visualRect(style().subRect(TQStyle::SR_ToolButtonContents, this), this); + + TQt::ArrowType arrowtype = d->arrow; + + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + if (hasFocus()) + flags |= TQStyle::Style_HasFocus; + if (isDown()) + flags |= TQStyle::Style_Down; + if (isOn()) + flags |= TQStyle::Style_On; + if (autoRaise()) { + flags |= TQStyle::Style_AutoRaise; + if (uses3D()) { + flags |= TQStyle::Style_MouseOver; + if (! isOn() && ! isDown()) + flags |= TQStyle::Style_Raised; + } + } else if (! isOn() && ! isDown()) + flags |= TQStyle::Style_Raised; + + style().drawControl(TQStyle::CE_ToolButtonLabel, p, this, r, + colorGroup(), flags, + hasArrow ? TQStyleOption(arrowtype) : + TQStyleOption()); +} + + +/*! + \reimp + */ +void TQToolButton::enterEvent( TQEvent * e ) +{ + if ( autoRaise() && isEnabled() ) + repaint(FALSE); + + TQButton::enterEvent( e ); +} + + +/*! + \reimp + */ +void TQToolButton::leaveEvent( TQEvent * e ) +{ + if ( autoRaise() && isEnabled() ) + repaint(FALSE); + + TQButton::leaveEvent( e ); +} + +/*! + \reimp + */ +void TQToolButton::moveEvent( TQMoveEvent * ) +{ + // Reimplemented to handle pseudo transparency in case the toolbars + // has a fancy pixmap background. + if ( parentWidget() && parentWidget()->backgroundPixmap() && + autoRaise() && !uses3D() ) + repaint( FALSE ); +} + +/*! + \reimp +*/ +void TQToolButton::mousePressEvent( TQMouseEvent *e ) +{ + TQRect popupr = + TQStyle::visualRect( style().querySubControlMetrics(TQStyle::CC_ToolButton, this, + TQStyle::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; + TQButton::mousePressEvent( e ); +} + +/*! + \reimp +*/ +bool TQToolButton::eventFilter( TQObject *o, TQEvent *e ) +{ +#ifndef QT_NO_POPUPMENU + if ( o != d->popup ) + return TQButton::eventFilter( o, e ); + switch ( e->type() ) { + case TQEvent::MouseButtonPress: + case TQEvent::MouseButtonDblClick: + { + TQMouseEvent *me = (TQMouseEvent*)e; + TQPoint p = me->globalPos(); + if ( TQApplication::widgetAt( p, TRUE ) == this ) + d->discardNextMouseEvent = TRUE; + } + break; + default: + break; + } +#endif + return TQButton::eventFilter( o, e ); +} + +/*! + Returns TRUE if this button should be drawn using raised edges; + otherwise returns FALSE. + + \sa drawButton() +*/ + +bool TQToolButton::uses3D() const +{ + return style().styleHint(TQStyle::SH_ToolButton_Uses3D) + && (!autoRaise() || ( hasMouse() && isEnabled() ) +#ifndef QT_NO_POPUPMENU + || ( d->popup && d->popup->isVisible() && d->delay <= 0 ) || d->instantPopup +#endif + ); +} + + +/*! + \property TQToolButton::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 TQToolButton::setTextLabel( const TQString &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 TQToolButton::setTextLabel( const TQString &newLabel , bool tipToo ) +{ + if ( tl == newLabel ) + return; + +#ifndef QT_NO_TOOLTIP + if ( tipToo ) { + TQToolTip::remove( this ); + TQToolTip::add( this, newLabel ); + } +#endif + + tl = newLabel; + if ( usesTextLabel() && isVisible() ) { + update(); + updateGeometry(); + } + +} + +#ifndef QT_NO_COMPAT + +TQIconSet TQToolButton::onIconSet() const +{ + return iconSet(); +} + +TQIconSet TQToolButton::offIconSet( ) const +{ + return iconSet(); +} + + +/*! + \property TQToolButton::onIconSet + \brief the icon set that is used when the button is in an "on" state + + \obsolete + + Since TQt 3.0, TQIconSet contains both the On and Off icons. There is + now an \l TQToolButton::iconSet property that replaces both \l + TQToolButton::onIconSet and \l TQToolButton::offIconSet. + + For ease of porting, this property is a synonym for \l + TQToolButton::iconSet. You probably want to go over your application + code and use the TQIconSet On/Off mechanism. + + \sa iconSet TQIconSet::State +*/ +void TQToolButton::setOnIconSet( const TQIconSet& set ) +{ + setIconSet( set ); + /* + ### Get rid of all qWarning in this file in 4.0. + Also consider inlining the obsolete functions then. + */ + qWarning( "TQToolButton::setOnIconSet(): This function is not supported" + " anymore" ); +} + +/*! + \property TQToolButton::offIconSet + \brief the icon set that is used when the button is in an "off" state + + \obsolete + + Since TQt 3.0, TQIconSet contains both the On and Off icons. There is + now an \l TQToolButton::iconSet property that replaces both \l + TQToolButton::onIconSet and \l TQToolButton::offIconSet. + + For ease of porting, this property is a synonym for \l + TQToolButton::iconSet. You probably want to go over your application + code and use the TQIconSet On/Off mechanism. + + \sa iconSet TQIconSet::State +*/ +void TQToolButton::setOffIconSet( const TQIconSet& set ) +{ + setIconSet( set ); +} + +#endif + +/*! \property TQToolButton::pixmap + \brief the pixmap of the button + + The pixmap property has no meaning for tool buttons. Use the + iconSet property instead. +*/ + +/*! + \property TQToolButton::iconSet + \brief the icon set providing the icon shown on the button + + Setting this property sets \l TQToolButton::pixmap to a null + pixmap. There is no default iconset. + + \sa pixmap(), setToggleButton(), isOn() +*/ +void TQToolButton::setIconSet( const TQIconSet & set ) +{ + if ( s ) + delete s; + setPixmap( TQPixmap() ); + s = new TQIconSet( set ); + if ( isVisible() ) + update(); +} + +/*! \overload + \obsolete + + Since TQt 3.0, TQIconSet 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 TQIconSet On/Off + mechanism. + + \sa iconSet TQIconSet::State +*/ + +#ifndef QT_NO_COMPAT + +void TQToolButton::setIconSet( const TQIconSet & set, bool /* on */ ) +{ + setIconSet( set ); + qWarning( "TQToolButton::setIconSet(): 'on' parameter ignored" ); +} + +#endif + +TQIconSet TQToolButton::iconSet() const +{ + TQToolButton *that = (TQToolButton *) this; + + if ( pixmap() && !pixmap()->isNull() && + (!that->s || (that->s->pixmap().serialNumber() != + pixmap()->serialNumber())) ) { + if ( that->s ) + delete that->s; + that->s = new TQIconSet( *pixmap() ); + } + if ( that->s ) + return *that->s; + /* + In 2.x, we used to return a temporary nonnull TQIconSet. If you + revert to the old behavior, you will break calls to + TQIconSet::isNull() in this file. + */ + return TQIconSet(); +} + +#ifndef QT_NO_COMPAT +/*! \overload + \obsolete + + Since TQt 3.0, TQIconSet 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 TQIconSet + On/Off mechanism. +*/ +TQIconSet TQToolButton::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 TQToolButton::setPopup( TQPopupMenu* popup ) +{ + if ( popup && !d->popupTimer ) { + connect( this, SIGNAL( pressed() ), this, SLOT( popupPressed() ) ); + d->popupTimer = new TQTimer( 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() +*/ +TQPopupMenu* TQToolButton::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 TQToolButton::openPopup() +{ + if ( !d->popup ) + return; + + d->instantPopup = TRUE; + repaint( FALSE ); + if ( d->popupTimer ) + d->popupTimer->stop(); + TQGuardedPtr<TQToolButton> that = this; + popupTimerDone(); + if ( !that ) + return; + d->instantPopup = FALSE; + repaint( FALSE ); +} + +void TQToolButton::popupPressed() +{ + if ( d->popupTimer && d->delay > 0 ) + d->popupTimer->start( d->delay, TRUE ); +} + +void TQToolButton::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 + TQToolBar *tb = ::qt_cast<TQToolBar*>(parentWidget()); + if ( tb && tb->orientation() == Vertical ) + horizontal = FALSE; +#endif + TQPoint p; + TQRect screen = qApp->desktop()->availableGeometry( this ); + if ( horizontal ) { + if ( TQApplication::reverseLayout() ) { + if ( mapToGlobal( TQPoint( 0, rect().bottom() ) ).y() + d->popup->sizeHint().height() <= screen.height() ) { + p = mapToGlobal( rect().bottomRight() ); + } else { + p = mapToGlobal( rect().topRight() - TQPoint( 0, d->popup->sizeHint().height() ) ); + } + p.rx() -= d->popup->sizeHint().width(); + } else { + if ( mapToGlobal( TQPoint( 0, rect().bottom() ) ).y() + d->popup->sizeHint().height() <= screen.height() ) { + p = mapToGlobal( rect().bottomLeft() ); + } else { + p = mapToGlobal( rect().topLeft() - TQPoint( 0, d->popup->sizeHint().height() ) ); + } + } + } else { + if ( TQApplication::reverseLayout() ) { + if ( mapToGlobal( TQPoint( 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( TQPoint( rect().right(), 0 ) ).x() + d->popup->sizeHint().width() <= screen.width() ) { + p = mapToGlobal( rect().topRight() ); + } else { + p = mapToGlobal( rect().topLeft() - TQPoint( d->popup->sizeHint().width(), 0 ) ); + } + } + } + TQGuardedPtr<TQToolButton> that = this; + d->popup->exec( p, -1 ); + if ( !that ) + return; + + setDown( FALSE ); + if ( d->repeat ) + setAutoRepeat( TRUE ); +} + +/*! + \property TQToolButton::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 TQToolButton::setPopupDelay( int delay ) +{ + d->delay = delay; + + update(); +} + +int TQToolButton::popupDelay() const +{ + return d->delay; +} +#endif + + +/*! + \property TQToolButton::autoRaise + \brief whether auto-raising is enabled or not. + + The default is disabled (i.e. FALSE). +*/ +void TQToolButton::setAutoRaise( bool enable ) +{ + d->autoraise = enable; + + update(); +} + +bool TQToolButton::autoRaise() const +{ + return d->autoraise; +} + +/*! + \property TQToolButton::textPosition + \brief the position of the text label of this button. +*/ + +TQToolButton::TextPosition TQToolButton::textPosition() const +{ + return d->textPos; +} + +void TQToolButton::setTextPosition( TextPosition pos ) +{ + d->textPos = pos; + updateGeometry(); + update(); +} + +/*! \reimp */ + +void TQToolButton::setText( const TQString &txt ) +{ + TQButton::setText( txt ); + if ( !text().isEmpty() ) { + delete s; + s = 0; + } +} + +#ifndef QT_NO_PALETTE +/*! + \reimp +*/ +void TQToolButton::paletteChange( const TQPalette & ) +{ + if ( s ) + s->clearGenerated(); +} +#endif + +#endif diff --git a/src/widgets/qtoolbutton.h b/src/widgets/qtoolbutton.h new file mode 100644 index 000000000..442632470 --- /dev/null +++ b/src/widgets/qtoolbutton.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Definition of TQToolButton class +** +** Created : 979899 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQTOOLBUTTON_H +#define TQTOOLBUTTON_H + +#ifndef QT_H +#include "qbutton.h" +#include "qstring.h" +#include "qpixmap.h" +#include "qiconset.h" +#endif // QT_H + +#ifndef QT_NO_TOOLBUTTON + +class TQToolButtonPrivate; +class TQToolBar; +class TQPopupMenu; + +class Q_EXPORT TQToolButton : public TQButton +{ + Q_OBJECT + Q_ENUMS( TextPosition ) + + Q_PROPERTY( TQIconSet iconSet READ iconSet WRITE setIconSet ) + Q_PROPERTY( TQIconSet onIconSet READ onIconSet WRITE setOnIconSet DESIGNABLE false STORED false ) + Q_PROPERTY( TQIconSet 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( TQString 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( TQPixmap pixmap DESIGNABLE false STORED false ) + Q_OVERRIDE( BackgroundMode backgroundMode DESIGNABLE true) + +public: + enum TextPosition { + BesideIcon, + BelowIcon, + Right = BesideIcon, // obsolete + Under = BelowIcon // obsolete + }; + TQToolButton( TQWidget * parent, const char* name=0 ); +#ifndef QT_NO_TOOLBAR + TQToolButton( const TQIconSet& s, const TQString &textLabel, + const TQString& grouptext, + TQObject * receiver, const char* slot, + TQToolBar * parent, const char* name=0 ); +#endif + TQToolButton( ArrowType type, TQWidget *parent, const char* name=0 ); + ~TQToolButton(); + + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + +#ifndef QT_NO_COMPAT + void setOnIconSet( const TQIconSet& ); + void setOffIconSet( const TQIconSet& ); + void setIconSet( const TQIconSet &, bool on ); + TQIconSet onIconSet() const; + TQIconSet offIconSet( ) const; + TQIconSet iconSet( bool on ) const; +#endif + virtual void setIconSet( const TQIconSet & ); + TQIconSet iconSet() const; + + bool usesBigPixmap() const { return ubp; } + bool usesTextLabel() const { return utl; } + TQString textLabel() const { return tl; } + +#ifndef QT_NO_POPUPMENU + void setPopup( TQPopupMenu* popup ); + TQPopupMenu* 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 TQString &txt ); + +public slots: + virtual void setUsesBigPixmap( bool enable ); + virtual void setUsesTextLabel( bool enable ); + virtual void setTextLabel( const TQString &, bool ); + + virtual void setToggleButton( bool enable ); + + virtual void setOn( bool enable ); + void toggle(); + void setTextLabel( const TQString & ); + void setTextPosition( TextPosition pos ); + +protected: + void mousePressEvent( TQMouseEvent * ); + void drawButton( TQPainter * ); + void drawButtonLabel(TQPainter *); + + void enterEvent( TQEvent * ); + void leaveEvent( TQEvent * ); + void moveEvent( TQMoveEvent * ); + + // ### Make virtual in 4.0, maybe act like TQPushButton with + // regards to setFlat() instead? Andy + bool uses3D() const; +#if (QT_VERSION >= 0x040000) +#error "Some functions need to be changed to virtual for TQt 4.0" +#endif + + bool eventFilter( TQObject *o, TQEvent *e ); + +#ifndef QT_NO_PALETTE + void paletteChange( const TQPalette & ); +#endif + +private slots: + void popupTimerDone(); + void popupPressed(); + +private: + void init(); + + TQPixmap bp; + int bpID; + TQPixmap sp; + int spID; + + TQString tl; + + TQToolButtonPrivate *d; + TQIconSet *s; + + uint utl : 1; + uint ubp : 1; + uint hasArrow : 1; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQToolButton( const TQToolButton & ); + TQToolButton& operator=( const TQToolButton & ); +#endif +}; + +#endif // QT_NO_TOOLBUTTON + +#endif // TQTOOLBUTTON_H diff --git a/src/widgets/qtooltip.cpp b/src/widgets/qtooltip.cpp new file mode 100644 index 000000000..ec2df8a15 --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQRect entireWidget() +{ + return TQRect( -TQWIDGETSIZE_MAX, -TQWIDGETSIZE_MAX, + 2*TQWIDGETSIZE_MAX, 2*TQWIDGETSIZE_MAX ); +} + +// Internal class - don't touch + +class TQTipLabel : public TQLabel +{ + Q_OBJECT +public: + TQTipLabel( TQWidget* parent, const TQString& text) : TQLabel( parent, "toolTipTip", + WStyle_StaysOnTop | WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WX11BypassWM ) + { + setMargin(1); + setAutoMask( FALSE ); + setFrameStyle( TQFrame::Plain | TQFrame::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 TQTipManager : public TQObject +{ + Q_OBJECT +public: + TQTipManager(); + ~TQTipManager(); + + struct Tip + { + TQRect rect; + TQString text; + TQString groupText; + TQToolTipGroup *group; + TQToolTip *tip; + bool autoDelete; + TQRect geometry; + Tip *next; + }; + + bool eventFilter( TQObject * o, TQEvent * e ); + void add( const TQRect &gm, TQWidget *, const TQRect &, const TQString& , + TQToolTipGroup *, const TQString& , TQToolTip *, bool ); + void add( TQWidget *, const TQRect &, const TQString& , + TQToolTipGroup *, const TQString& , TQToolTip *, bool ); + void remove( TQWidget *, const TQRect &, bool delayhide = FALSE ); + void remove( TQWidget * ); + + void removeFromGroup( TQToolTipGroup * ); + + void hideTipAndSleep(); + + TQString find( TQWidget *, const TQPoint& ); + void setWakeUpDelay(int); + +public slots: + void hideTip(); + +private slots: + void labelDestroyed(); + void clientWidgetDestroyed(); + void showTip(); + void allowAnimation(); + +private: + TQTimer wakeUp; + int wakeUpDelay; + TQTimer fallAsleep; + + TQPtrDict<Tip> *tips; + TQTipLabel *label; + TQPoint pos; + TQGuardedPtr<TQWidget> widget; + Tip *currentTip; + Tip *previousTip; + bool preventAnimation; + bool isApplicationFilter; + TQTimer *removeTimer; +}; + + +// We have a global, internal TQTipManager object + +static TQTipManager *tipManager = 0; + +static void initTipManager() +{ + if ( !tipManager ) { + tipManager = new TQTipManager; + Q_CHECK_PTR( tipManager ); + } +} + + +TQTipManager::TQTipManager() + : TQObject( qApp, "toolTipManager" ) +{ + wakeUpDelay = 700; + tips = new TQPtrDict<TQTipManager::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 TQTimer( this ); +} + + +TQTipManager::~TQTipManager() +{ + if ( isApplicationFilter && !qApp->closingDown() ) { + qApp->setGlobalMouseTracking( FALSE ); + qApp->removeEventFilter( tipManager ); + } + + delete label; + label = 0; + + if ( tips ) { + TQPtrDictIterator<TQTipManager::Tip> i( *tips ); + TQTipManager::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 TQTipManager::add( const TQRect &gm, TQWidget *w, + const TQRect &r, const TQString &s, + TQToolTipGroup *g, const TQString& gs, + TQToolTip *tt, bool a ) +{ + remove( w, r, TRUE ); + TQTipManager::Tip *h = (*tips)[ w ]; + TQTipManager::Tip *t = new TQTipManager::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 TQTipManager::add( TQWidget *w, const TQRect &r, const TQString &s, + TQToolTipGroup *g, const TQString& gs, + TQToolTip *tt, bool a ) +{ + add( TQRect( -1, -1, -1, -1 ), w, r, s, g, gs, tt, a ); +} + + +void TQTipManager::remove( TQWidget *w, const TQRect & r, bool delayhide ) +{ + TQTipManager::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 ) { + TQTipManager::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 TQTipManager::labelDestroyed() +{ + label = 0; +} + + +/* + Remove sender() from the tool tip data structures. +*/ + +void TQTipManager::clientWidgetDestroyed() +{ + const TQObject *s = sender(); + if ( s ) + remove( (TQWidget*) s ); +} + + +void TQTipManager::remove( TQWidget *w ) +{ + TQTipManager::Tip *t = (*tips)[ w ]; + if ( t == 0 ) + return; + + tips->take( w ); + TQTipManager::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 TQTipManager::removeFromGroup( TQToolTipGroup *g ) +{ + TQPtrDictIterator<TQTipManager::Tip> i( *tips ); + TQTipManager::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 TQTipManager::eventFilter( TQObject *obj, TQEvent *e ) +{ + // avoid dumping core in case of application madness, and return + // tquickly for some common but irrelevant events + if ( e->type() == TQEvent::WindowDeactivate && + qApp && !qApp->activeWindow() && + label && label->isVisible() ) + hideTipAndSleep(); + + if ( !qApp + || !obj || !obj->isWidgetType() // isWidgetType() catches most stuff + || e->type() == TQEvent::Paint + || e->type() == TQEvent::Timer + || e->type() == TQEvent::SockAct + || !tips ) + return FALSE; + TQWidget *w = (TQWidget *)obj; + + if ( e->type() == TQEvent::FocusOut || e->type() == TQEvent::FocusIn ) { + // user moved focus somewhere - hide the tip and sleep + if ( ((TQFocusEvent*)e)->reason() != TQFocusEvent::Popup ) + hideTipAndSleep(); + return FALSE; + } + + TQTipManager::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() != TQEvent::MouseMove) { + if ( ( e->type() >= TQEvent::MouseButtonPress && + e->type() <= TQEvent::FocusOut) || e->type() == TQEvent::Leave ) + hideTip(); + return FALSE; + } + + // with that out of the way, let's get down to action + + switch( e->type() ) { + case TQEvent::MouseButtonPress: + case TQEvent::MouseButtonRelease: + case TQEvent::MouseButtonDblClick: + case TQEvent::KeyPress: + case TQEvent::KeyRelease: + // input - turn off tool tip mode + hideTipAndSleep(); + break; + case TQEvent::MouseMove: + { + TQMouseEvent * m = (TQMouseEvent *)e; + TQPoint 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 TQEvent::Leave: + case TQEvent::Hide: + case TQEvent::Destroy: + if ( w == widget ) + hideTip(); + break; + default: + break; + } + return FALSE; +} + + + +void TQTipManager::showTip() +{ + if ( !widget || !globally_enabled +#ifndef Q_WS_X11 + || !widget->isActiveWindow() +#endif + ) + return; + + TQTipManager::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 ( TQApplication::desktop()->isVirtualDesktop() ) + scr = TQApplication::desktop()->screenNumber( widget->mapToGlobal( pos ) ); + else + scr = TQApplication::desktop()->screenNumber( widget ); + + if ( label +#if defined(Q_WS_X11) + && label->x11Screen() == widget->x11Screen() +#endif + ) { + // the next two lines are a workaround for TQLabel being too intelligent. + // TQLabel 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 != TQRect( -1, -1, -1, -1 ) ) + label->resize( t->geometry.size() ); + } else { + delete label; + label = new TQTipLabel( TQApplication::desktop()->screen( scr ), t->text); + if ( t->geometry != TQRect( -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 TQTipLabel 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 + TQRect screen = TQApplication::desktop()->availableGeometry( scr ); +#else + TQRect screen = TQApplication::desktop()->screenGeometry( scr ); +#endif + TQPoint p; + if ( t->geometry == TQRect( -1, -1, -1, -1 ) ) { + p = widget->mapToGlobal( pos ) + +#ifdef Q_WS_WIN + TQPoint( 2, 24 ); +#else + TQPoint( 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 ( TQApplication::isEffectEnabled( UI_AnimateTooltip ) == FALSE || + previousTip || preventAnimation ) + label->show(); + else if ( TQApplication::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 TQTipManager::hideTip() +{ + TQTimer::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 TQTipManager::hideTipAndSleep() +{ + hideTip(); + fallAsleep.stop(); +} + + +void TQTipManager::allowAnimation() +{ + preventAnimation = FALSE; +} + +TQString TQTipManager::find( TQWidget *w, const TQPoint& pos ) +{ + Tip *t = (*tips)[ w ]; + while ( t && !t->rect.contains( pos ) ) + t = t->next; + + return t ? t->text : TQString::null; +} + +void TQTipManager::setWakeUpDelay ( int i ) +{ + wakeUpDelay = i; +} + +/*! + \class TQToolTip qtooltip.h + \brief The TQToolTip 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. + + TQToolTipGroup provides a way for tool tips to display another text + elsewhere (most often in a \link TQStatusBar status bar\endlink). + + At any point in time, TQToolTip 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. + + TQToolTip switches from dormant to active mode when the user hovers + the mouse on a tip-etquipped 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-etquipped regions for at least a second. + + The TQToolTip 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 + TQToolTip::add() with the widget and tip as arguments: + + \code + TQToolTip::add( tquitButton, "Leave the application" ); + \endcode + + This is the simplest and most common use of TQToolTip. The tip + will be deleted automatically when \e tquitButton is deleted, but + you can remove it yourself, too: + + \code + TQToolTip::remove( tquitButton ); + \endcode + + You can also display another text (typically in a \link TQStatusBar + status bar),\endlink courtesy of \l{TQToolTipGroup}. This example + assumes that \e grp is a \c{TQToolTipGroup *} and is already + connected to the appropriate status bar: + + \code + TQToolTip::add( tquitButton, "Leave the application", grp, + "Leave the application, prompting to save if necessary" ); + TQToolTip::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 TQToolTip::add() with the widget, rectangle and tip as + arguments. (See the \c tooltip/tooltip.cpp example.) Again, you + can supply a \c{TQToolTipGroup *} 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 TQToolTip retquires 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 TQToolTip like this, you must subclass TQToolTip and + reimplement maybeTip(). TQToolTip calls maybeTip() when a tip + should pop up, and maybeTip() decides whether to show a tip. + + Tool tips can be globally disabled using + TQToolTip::setGloballyEnabled() or disabled in groups with + TQToolTipGroup::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 TQStatusBar TQWhatsThis TQToolTipGroup + \link guibooks.html#fowler GUI Design Handbook: Tool Tip\endlink +*/ + + +/*! + Returns the font common to all tool tips. + + \sa setFont() +*/ + +TQFont TQToolTip::font() +{ + TQTipLabel l(0,""); + return TQApplication::font( &l ); +} + + +/*! + Sets the font for all tool tips to \a font. + + \sa font() +*/ + +void TQToolTip::setFont( const TQFont &font ) +{ + TQApplication::setFont( font, TRUE, "TQTipLabel" ); +} + + +/*! + Returns the palette common to all tool tips. + + \sa setPalette() +*/ + +TQPalette TQToolTip::palette() +{ + TQTipLabel l(0,""); + return TQApplication::palette( &l ); +} + + +/*! + Sets the palette for all tool tips to \a palette. + + \sa palette() +*/ + +void TQToolTip::setPalette( const TQPalette &palette ) +{ + TQApplication::setPalette( palette, TRUE, "TQTipLabel" ); +} + +/*! + 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 TQToolTip is not a subclass of TQObject, so the instance of + TQToolTip 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 TQToolTip destructor. + + \code + MyToolTip::~MyToolTip() + { + remove( widget ); + } + \endcode + + \sa maybeTip(). +*/ + +TQToolTip::TQToolTip( TQWidget * widget, TQToolTipGroup * group ) +{ + p = widget; + g = group; + initTipManager(); + tipManager->add( p, entireWidget(), + TQString::null, g, TQString::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 TQToolTip class; it is + suitable for adding tool tips to buttons, checkboxes, comboboxes + and so on. +*/ + +void TQToolTip::add( TQWidget *widget, const TQString &text ) +{ + initTipManager(); + tipManager->add( widget, entireWidget(), + text, 0, TQString::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 TQStatusBar status + bar\endlink or similar. +*/ + +void TQToolTip::add( TQWidget *widget, const TQString &text, + TQToolTipGroup *group, const TQString& 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 TQToolTip::remove( TQWidget * 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 TQToolTip::add( TQWidget * widget, const TQRect & rect, const TQString &text ) +{ + initTipManager(); + tipManager->add( widget, rect, text, 0, TQString::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 TQStatusBar status + bar\endlink or similar. +*/ + +void TQToolTip::add( TQWidget *widget, const TQRect &rect, + const TQString& text, + TQToolTipGroup *group, const TQString& 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 TQToolTip::remove( TQWidget * widget, const TQRect & rect ) +{ + if ( tipManager ) + tipManager->remove( widget, rect ); +} + +/*! + Returns the tool tip text for \a widget at position \a pos, or + TQString::null if there is no tool tip for the given widget and + position. +*/ + +TQString TQToolTip::textFor( TQWidget *widget, const TQPoint& pos ) +{ + if ( tipManager ) + return tipManager->find( widget, pos ); + return TQString::null; +} + +/*! + Hides any tip that is currently being shown. + + Normally, there is no need to call this function; TQToolTip takes + care of showing and hiding the tips as the user moves the mouse. +*/ + +void TQToolTip::hide() +{ + if ( tipManager ) + tipManager->hideTipAndSleep(); +} + +/*! + \fn virtual void TQToolTip::maybeTip( const TQPoint & p); + + This pure virtual function is half of the most versatile interface + TQToolTip 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 TQToolTip object relates to. If so, + maybeTip() must call tip() with the rectangle the tip applies to, + the tip's text and optionally the TQToolTipGroup 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 + TQToolTip'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 TQToolTip relates to). + + The tip will not reappear if the cursor moves back; your + maybeTip() must reinstate it each time. +*/ + +void TQToolTip::tip( const TQRect & rect, const TQString &text ) +{ + initTipManager(); + tipManager->add( parentWidget(), rect, text, 0, TQString::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 TQToolTip 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 TQToolTip::tip( const TQRect & rect, const TQString &text, + const TQString& 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 TQToolTip 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 TQToolTip::tip( const TQRect &rect, const TQString &text, const TQRect &geometry ) +{ + initTipManager(); + tipManager->add( geometry, parentWidget(), rect, text, 0, TQString::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 + TQToolTip 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 TQToolTip::tip( const TQRect &rect, const TQString &text, const TQString& groupText, const TQRect &geometry ) +{ + initTipManager(); + tipManager->add( geometry, parentWidget(), rect, text, group(), groupText, 0, TRUE ); +} + + + +/*! + Immediately removes all tool tips for this tooltip's parent + widget. +*/ + +void TQToolTip::clear() +{ + if ( tipManager ) + tipManager->remove( parentWidget() ); +} + + +/*! + \fn TQWidget * TQToolTip::parentWidget() const + + Returns the widget this TQToolTip applies to. + + The tool tip is destroyed automatically when the parent widget is + destroyed. + + \sa group() +*/ + + +/*! + \fn TQToolTipGroup * TQToolTip::group() const + + Returns the tool tip group this TQToolTip 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 TQStatusBar status + bar\endlink or something else which can show the longer help text. + + \sa parentWidget(), TQToolTipGroup +*/ + + +/*! + \class TQToolTipGroup qtooltip.h + \brief The TQToolTipGroup 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 TQStatusBar status + bar\endlink. TQToolTipGroup provides a way to link tool tips to + this status bar. + + TQToolTipGroup has practically no API; it is only used as an + argument to TQToolTip's member functions, for example like this: + + \code + TQToolTipGroup * grp = new TQToolTipGroup( this, "tool tip relay" ); + connect( grp, SIGNAL(showTip(const TQString&)), + myLabel, SLOT(setText(const TQString&)) ); + connect( grp, SIGNAL(removeTip()), + myLabel, SLOT(clear()) ); + TQToolTip::add( giraffeButton, "feed giraffe", + grp, "Give the giraffe a meal" ); + TQToolTip::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 TQToolTipGroup::showTip (const TQString &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 TQToolTipGroup::removeTip () + + This signal is emitted when a tool tip in this group is hidden. + See the TQToolTipGroup documentation for an example of use. + + \sa showTip() +*/ + + +/*! + Constructs a tool tip group called \a name, with parent \a parent. +*/ + +TQToolTipGroup::TQToolTipGroup( TQObject *parent, const char *name ) + : TQObject( parent, name ) +{ + del = TRUE; + ena = TRUE; +} + + +/*! + Destroys this tool tip group and all tool tips in it. +*/ + +TQToolTipGroup::~TQToolTipGroup() +{ + if ( tipManager ) + tipManager->removeFromGroup( this ); +} + + +/*! + \property TQToolTipGroup::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 TQToolTipGroup::delay() const +{ + return del; +} + +void TQToolTipGroup::setDelay( bool enable ) +{ +#if 0 + if ( enable && !del ) { + // maybe we should show the text at once? + } +#endif + del = enable; +} + +/*! + \fn static void TQToolTip::setEnabled( bool enable ) + + \obsolete +*/ +/*! + \fn static bool TQToolTip::enabled() + + \obsolete +*/ +/*! + \property TQToolTipGroup::enabled + \brief whether tool tips in the group are enabled. + + This property's default is TRUE. +*/ + +void TQToolTipGroup::setEnabled( bool enable ) +{ + ena = enable; +} + +bool TQToolTipGroup::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 TQToolTipGroup::setEnabled() +*/ + +void TQToolTip::setGloballyEnabled( bool enable ) +{ + globally_enabled = enable; +} + +/*! + Returns whether tool tips are enabled globally. + + \sa setGloballyEnabled() +*/ +bool TQToolTip::isGloballyEnabled() +{ + return globally_enabled; +} + +/*! + Sets the wakeup delay for all tooltips to \a i + milliseconds. +*/ +void TQToolTip::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 000000000..5efe98e67 --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQTOOLTIP_H +#define TQTOOLTIP_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_TOOLTIP + +#if __GNUC__ - 0 > 3 +#pragma GCC system_header +#endif + +class TQTipManager; +class TQIconViewToolTip; +class TQListViewToolTip; + +class Q_EXPORT TQToolTipGroup: public TQObject +{ + Q_OBJECT + Q_PROPERTY( bool delay READ delay WRITE setDelay ) + Q_PROPERTY( bool enabled READ enabled WRITE setEnabled ) + +public: + TQToolTipGroup( TQObject *parent, const char *name = 0 ); + ~TQToolTipGroup(); + + bool delay() const; + bool enabled() const; + +public slots: + void setDelay( bool ); + void setEnabled( bool ); + +signals: + void showTip( const TQString &); + void removeTip(); + +private: + uint del:1; + uint ena:1; + + friend class TQTipManager; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQToolTipGroup( const TQToolTipGroup & ); + TQToolTipGroup& operator=( const TQToolTipGroup & ); +#endif +}; + + +class Q_EXPORT TQToolTip: public TQt +{ +public: + TQToolTip( TQWidget *, TQToolTipGroup * = 0 ); + //### add virtual d'tor for 4.0 + + static void add( TQWidget *, const TQString &); + static void add( TQWidget *, const TQString &, + TQToolTipGroup *, const TQString& ); + static void remove( TQWidget * ); + + static void add( TQWidget *, const TQRect &, const TQString &); + static void add( TQWidget *, const TQRect &, const TQString &, + TQToolTipGroup *, const TQString& ); + static void remove( TQWidget *, const TQRect & ); + + static TQString textFor( TQWidget *, const TQPoint & pos = TQPoint() ); + + static void hide(); + + static TQFont font(); + static void setFont( const TQFont & ); + static TQPalette palette(); + static void setPalette( const TQPalette & ); + +#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 TQPoint & ) = 0; + void tip( const TQRect &, const TQString &); + void tip( const TQRect &, const TQString& , const TQString &); + void tip( const TQRect &, const TQString &, const TQRect & ); + void tip( const TQRect &, const TQString&, const TQString &, const TQRect &); + + void clear(); + +public: + TQWidget *parentWidget() const { return p; } + TQToolTipGroup *group() const { return g; } + +private: + TQWidget *p; + TQToolTipGroup *g; + static TQFont *ttFont; + static TQPalette *ttPalette; + + friend class TQTipManager; +}; + + +#endif // QT_NO_TOOLTIP + +#endif // TQTOOLTIP_H diff --git a/src/widgets/qvalidator.cpp b/src/widgets/qvalidator.cpp new file mode 100644 index 000000000..d560205de --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qvalidator.h" +#ifndef QT_NO_VALIDATOR + +#include <limits.h> +#include <math.h> + +/*! + \class TQValidator + \brief The TQValidator class provides validation of input text. + + \ingroup misc + \mainclass + + The class itself is abstract. Two subclasses, \l TQIntValidator and + \l TQDoubleValidator, provide basic numeric-range checking, and \l + TQRegExpValidator provides general checking using a custom regular + expression. + + If the built-in validators aren't sufficient, you can subclass + TQValidator. 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 retquire 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). TQValidator 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. TQLineEdit, 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. + + TQValidator is typically used with TQLineEdit, TQSpinBox and + TQComboBox. +*/ + + +/*! + \enum TQValidator::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 TQObject constructor. +*/ + +TQValidator::TQValidator( TQObject * parent, const char *name ) + : TQObject( parent, name ) +{ +} + + +/*! + Destroys the validator, freeing any storage and other resources + used. +*/ + +TQValidator::~TQValidator() +{ +} + + +/*! + \fn TQValidator::State TQValidator::validate( TQString& 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 TQValidator::fixup( TQString & 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 TQValidator::fixup( TQString & ) const +{ +} + + +/*! + \class TQIntValidator + \brief The TQIntValidator class provides a validator which ensures + that a string contains a valid integer within a specified range. + + \ingroup misc + + Example of use: + + \code + TQValidator* validator = new TQIntValidator( 100, 999, this ); + TQLineEdit* edit = new TQLineEdit( 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 + TQString str; + int pos = 0; + TQIntValidator 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 TQDoubleValidator TQRegExpValidator +*/ + + +/*! + Constructs a validator called \a name with parent \a parent, that + accepts all integers. +*/ + +TQIntValidator::TQIntValidator( TQObject * parent, const char *name ) + : TQValidator( 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. +*/ + +TQIntValidator::TQIntValidator( int minimum, int maximum, + TQObject * parent, const char* name ) + : TQValidator( parent, name ) +{ + b = minimum; + t = maximum; +} + + +/*! + Destroys the validator, freeing any resources allocated. +*/ + +TQIntValidator::~TQIntValidator() +{ + // 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 +*/ + +TQValidator::State TQIntValidator::validate( TQString & input, int & ) const +{ + TQString 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 TQIntValidator::setRange( int bottom, int top ) +{ + b = bottom; + t = top; +} + + +/*! + \property TQIntValidator::bottom + \brief the validator's lowest acceptable value + + \sa setRange() +*/ +void TQIntValidator::setBottom( int bottom ) +{ + setRange( bottom, top() ); +} + +/*! + \property TQIntValidator::top + \brief the validator's highest acceptable value + + \sa setRange() +*/ +void TQIntValidator::setTop( int top ) +{ + setRange( bottom(), top ); +} + + +#ifndef QT_NO_REGEXP + +/*! + \class TQDoubleValidator + + \brief The TQDoubleValidator class provides range checking of + floating-point numbers. + + \ingroup misc + + TQDoubleValidator 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 TQIntValidator TQRegExpValidator +*/ + +/*! + Constructs a validator object with parent \a parent, called \a + name, which accepts any double. +*/ + +TQDoubleValidator::TQDoubleValidator( TQObject * parent, const char *name ) + : TQValidator( 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. +*/ + +TQDoubleValidator::TQDoubleValidator( double bottom, double top, int decimals, + TQObject * parent, const char* name ) + : TQValidator( parent, name ) +{ + b = bottom; + t = top; + d = decimals; +} + + +/*! + Destroys the validator, freeing any resources used. +*/ + +TQDoubleValidator::~TQDoubleValidator() +{ +} + + +/*! + 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. +*/ + +TQValidator::State TQDoubleValidator::validate( TQString & input, int & ) const +{ + TQRegExp empty( TQString::fromLatin1(" *-?\\.? *") ); + if ( b >= 0 && + input.stripWhiteSpace().startsWith(TQString::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 + TQRegExp expexpexp( TQString::fromLatin1("[Ee][+-]?\\d*$") ); + int eeePos = expexpexp.search( input ); + if ( eeePos > 0 && nume == 1 ) { + TQString 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 TQDoubleValidator::setRange( double minimum, double maximum, int decimals ) +{ + b = minimum; + t = maximum; + d = decimals; +} + +/*! + \property TQDoubleValidator::bottom + \brief the validator's minimum acceptable value + + \sa setRange() +*/ + +void TQDoubleValidator::setBottom( double bottom ) +{ + setRange( bottom, top(), decimals() ); +} + + +/*! + \property TQDoubleValidator::top + \brief the validator's maximum acceptable value + + \sa setRange() +*/ + +void TQDoubleValidator::setTop( double top ) +{ + setRange( bottom(), top, decimals() ); +} + +/*! + \property TQDoubleValidator::decimals + \brief the validator's maximum number of digits after the decimal point + + \sa setRange() +*/ + +void TQDoubleValidator::setDecimals( int decimals ) +{ + setRange( bottom(), top(), decimals ); +} + + +/*! + \class TQRegExpValidator + \brief The TQRegExpValidator class is used to check a string + against a regular expression. + + \ingroup misc + + TQRegExpValidator 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 TQt's regexp engine see \l TQRegExp. + + Example of use: + \code + // regexp: optional '-' followed by between 1 and 3 digits + TQRegExp rx( "-?\\d{1,3}" ); + TQValidator* validator = new TQRegExpValidator( rx, this ); + + TQLineEdit* edit = new TQLineEdit( 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 + TQRegExp rx( "[1-9]\\d{0,3}" ); + // the validator treats the regexp as "^[1-9]\\d{0,3}$" + TQRegExpValidator v( rx, 0 ); + TQString 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 TQRegExp TQIntValidator TQDoubleValidator +*/ + +/*! + 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. +*/ + +TQRegExpValidator::TQRegExpValidator( TQObject *parent, const char *name ) + : TQValidator( parent, name ), r( TQString::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>. +*/ + +TQRegExpValidator::TQRegExpValidator( const TQRegExp& rx, TQObject *parent, + const char *name ) + : TQValidator( parent, name ), r( rx ) +{ +} + +/*! + Destroys the validator, freeing any resources allocated. +*/ + +TQRegExpValidator::~TQRegExpValidator() +{ +} + +/*! + 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 TQRegExp::match() TQRegExp::search() +*/ + +TQValidator::State TQRegExpValidator::validate( TQString& input, int& pos ) const +{ + if ( r.exactMatch(input) ) { + return Acceptable; + } else { + if ( ((TQRegExp&) 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 TQRegExpValidator::setRegExp( const TQRegExp& rx ) +{ + r = rx; +} + +/*! + \fn const TQRegExp& TQRegExpValidator::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 000000000..e693d98aa --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQVALIDATOR_H +#define TQVALIDATOR_H + +#ifndef QT_H +#include "qobject.h" +#include "qstring.h" // char*->TQString conversion +#include "qregexp.h" // TQString->TQRegExp conversion +#endif // QT_H + +#ifndef QT_NO_VALIDATOR + + +class Q_EXPORT TQValidator : public TQObject +{ + Q_OBJECT +public: + TQValidator( TQObject * parent, const char *name = 0 ); + ~TQValidator(); + + enum State { Invalid, Intermediate, Valid=Intermediate, Acceptable }; + + virtual State validate( TQString &, int & ) const = 0; + virtual void fixup( TQString & ) const; + +private: +#if defined(Q_DISABLE_COPY) + TQValidator( const TQValidator & ); + TQValidator& operator=( const TQValidator & ); +#endif +}; + + +class Q_EXPORT TQIntValidator : public TQValidator +{ + Q_OBJECT + Q_PROPERTY( int bottom READ bottom WRITE setBottom ) + Q_PROPERTY( int top READ top WRITE setTop ) + +public: + TQIntValidator( TQObject * parent, const char *name = 0 ); + TQIntValidator( int bottom, int top, + TQObject * parent, const char *name = 0 ); + ~TQIntValidator(); + + TQValidator::State validate( TQString &, 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) + TQIntValidator( const TQIntValidator & ); + TQIntValidator& operator=( const TQIntValidator & ); +#endif + + int b, t; +}; + +#ifndef QT_NO_REGEXP + +class Q_EXPORT TQDoubleValidator : public TQValidator +{ + 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: + TQDoubleValidator( TQObject * parent, const char *name = 0 ); + TQDoubleValidator( double bottom, double top, int decimals, + TQObject * parent, const char *name = 0 ); + ~TQDoubleValidator(); + + TQValidator::State validate( TQString &, 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) + TQDoubleValidator( const TQDoubleValidator & ); + TQDoubleValidator& operator=( const TQDoubleValidator & ); +#endif + + double b, t; + int d; +}; + + +class Q_EXPORT TQRegExpValidator : public TQValidator +{ + Q_OBJECT + // Q_PROPERTY( TQRegExp regExp READ regExp WRITE setRegExp ) + +public: + TQRegExpValidator( TQObject *parent, const char *name = 0 ); + TQRegExpValidator( const TQRegExp& rx, TQObject *parent, + const char *name = 0 ); + ~TQRegExpValidator(); + + virtual TQValidator::State validate( TQString& input, int& pos ) const; + + void setRegExp( const TQRegExp& rx ); + const TQRegExp& regExp() const { return r; } + +private: +#if defined(Q_DISABLE_COPY) + TQRegExpValidator( const TQRegExpValidator& ); + TQRegExpValidator& operator=( const TQRegExpValidator& ); +#endif + + TQRegExp r; +}; +#endif // QT_NO_REGEXP + + +#endif // QT_NO_VALIDATOR + +#endif // TQVALIDATOR_H diff --git a/src/widgets/qvbox.cpp b/src/widgets/qvbox.cpp new file mode 100644 index 000000000..7150a55dc --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + + +#include "qvbox.h" +#ifndef QT_NO_VBOX + +/*! + \class TQVBox qvbox.h + \brief The TQVBox 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 TQVBox + + \sa TQHBox +*/ + + +/*! + Constructs a vbox widget called \a name with parent \a parent and + widget flags \a f. + */ +TQVBox::TQVBox( TQWidget *parent, const char *name, WFlags f ) + :TQHBox( FALSE, parent, name, f ) +{ +} +#endif diff --git a/src/widgets/qvbox.h b/src/widgets/qvbox.h new file mode 100644 index 000000000..c50721bbc --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQVBOX_H +#define TQVBOX_H + +#ifndef QT_H +#include "qhbox.h" +#endif // QT_H + +#ifndef QT_NO_VBOX + +class Q_EXPORT TQVBox : public TQHBox +{ + Q_OBJECT +public: + TQVBox( TQWidget* parent=0, const char* name=0, WFlags f=0 ); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQVBox( const TQVBox & ); + TQVBox& operator=( const TQVBox & ); +#endif +}; + +#endif // QT_NO_VBOX + +#endif // TQVBOX_H diff --git a/src/widgets/qvbuttongroup.cpp b/src/widgets/qvbuttongroup.cpp new file mode 100644 index 000000000..302252618 --- /dev/null +++ b/src/widgets/qvbuttongroup.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Implementation of TQVButtonGroup class +** +** Created : 990602 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qvbuttongroup.h" +#ifndef QT_NO_VBUTTONGROUP + +/*! + \class TQVButtonGroup qvbuttongroup.h + \brief The TQVButtonGroup widget organizes TQButton widgets in a + vertical column. + + \ingroup geomanagement + \ingroup organizers + \ingroup appearance + + TQVButtonGroup is a convenience class that offers a thin layer on top + of TQButtonGroup. Think of it as a TQVBox that offers a frame with a + title and is specifically designed for buttons. + + \img qbuttongroup-v.png TQButtonGroup + + \sa TQHButtonGroup +*/ + +/*! + Constructs a vertical button group with no title. + + The \a parent and \a name arguments are passed on to the TQWidget + constructor. +*/ +TQVButtonGroup::TQVButtonGroup( TQWidget *parent, const char *name ) + : TQButtonGroup( 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 TQWidget + constructor. +*/ + +TQVButtonGroup::TQVButtonGroup( const TQString &title, TQWidget *parent, + const char *name ) + : TQButtonGroup( 1, Horizontal /* sic! */, title, parent, name ) +{ +} + +/*! + Destroys the vertical button group, deleting its child widgets. +*/ +TQVButtonGroup::~TQVButtonGroup() +{ +} +#endif diff --git a/src/widgets/qvbuttongroup.h b/src/widgets/qvbuttongroup.h new file mode 100644 index 000000000..7660989b0 --- /dev/null +++ b/src/widgets/qvbuttongroup.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Definition of TQVButtonGroup class +** +** Created : 990602 +** +** Copyright (C) 1999-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQVBUTTONGROUP_H +#define TQVBUTTONGROUP_H + +#ifndef QT_H +#include "qbuttongroup.h" +#endif // QT_H + +#ifndef QT_NO_VBUTTONGROUP + +class Q_EXPORT TQVButtonGroup : public TQButtonGroup +{ + Q_OBJECT +public: + TQVButtonGroup( TQWidget* parent=0, const char* name=0 ); + TQVButtonGroup( const TQString &title, TQWidget* parent=0, const char* name=0 ); + + ~TQVButtonGroup(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQVButtonGroup( const TQVButtonGroup & ); + TQVButtonGroup &operator=( const TQVButtonGroup & ); +#endif +}; + + +#endif // QT_NO_VBUTTONGROUP + +#endif // TQVBUTTONGROUP_H diff --git a/src/widgets/qvgroupbox.cpp b/src/widgets/qvgroupbox.cpp new file mode 100644 index 000000000..9ccecb53c --- /dev/null +++ b/src/widgets/qvgroupbox.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Implementation of TQVGroupBox class +** +** Created : 990602 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qvgroupbox.h" +#ifndef QT_NO_VGROUPBOX + +/*! + \class TQVGroupBox qvgroupbox.h + \brief The TQVGroupBox widget organizes a group of widgets in a + vertical column. + + \ingroup geomanagement + \ingroup appearance + \ingroup organizers + + TQVGroupBox is a convenience class that offers a thin layer on top of + TQGroupBox. Think of it as a TQVBox that offers a frame with a title. + + \img qgroupboxes.png Group Boxes + + \sa TQHGroupBox +*/ + +/*! + Constructs a vertical group box with no title. + + The \a parent and \a name arguments are passed on to the TQWidget + constructor. +*/ +TQVGroupBox::TQVGroupBox( TQWidget *parent, const char *name ) + : TQGroupBox( 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 TQWidget + constructor. +*/ + +TQVGroupBox::TQVGroupBox( const TQString &title, TQWidget *parent, + const char *name ) + : TQGroupBox( 1, Horizontal /* sic! */, title, parent, name ) +{ +} + +/*! + Destroys the vertical group box, deleting its child widgets. +*/ +TQVGroupBox::~TQVGroupBox() +{ +} +#endif diff --git a/src/widgets/qvgroupbox.h b/src/widgets/qvgroupbox.h new file mode 100644 index 000000000..9edc7f9e4 --- /dev/null +++ b/src/widgets/qvgroupbox.h @@ -0,0 +1,68 @@ +/********************************************************************** +** +** Definition of TQVGroupBox widget class +** +** Created : 990602 +** +** Copyright (C) 1999-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQVGROUPBOX_H +#define TQVGROUPBOX_H + +#ifndef QT_H +#include "qgroupbox.h" +#endif // QT_H + +#ifndef QT_NO_VGROUPBOX + +class Q_EXPORT TQVGroupBox : public TQGroupBox +{ + Q_OBJECT +public: + TQVGroupBox( TQWidget* parent=0, const char* name=0 ); + TQVGroupBox( const TQString &title, TQWidget* parent=0, const char* name=0 ); + + ~TQVGroupBox(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQVGroupBox( const TQVGroupBox & ); + TQVGroupBox &operator=( const TQVGroupBox & ); +#endif +}; + +#endif // QT_NO_VGROUPBOX + +#endif // TQVGROUPBOX_H diff --git a/src/widgets/qwhatsthis.cpp b/src/widgets/qwhatsthis.cpp new file mode 100644 index 000000000..baa9f11f2 --- /dev/null +++ b/src/widgets/qwhatsthis.cpp @@ -0,0 +1,1001 @@ +/**************************************************************************** +** +** Implementation of TQWhatsThis class +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQWhatsThis qwhatsthis.h + \brief The TQWhatsThis 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. + + TQWhatsThis 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 + TQWhatsThis::add() for the widget. For example, to assign text to a + menu item, call TQMenuData::setWhatsThis(); for a global + accelerator key, call TQAccel::setWhatsThis() and If you're using + actions, use TQAction::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 + TQStyleSheet::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 + TQWhatsThis::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 TQMainWindow you can also use the + TQMainWindow::whatsThis() slot to invoke the mode from a menu item. + + For more control you can create a dedicated TQWhatsThis object for + a special widget. By subclassing and reimplementing + TQWhatsThis::text() it is possible to have different help texts, + depending on the position of the mouse click. By reimplementing + TQWhatsThis::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 TQWidget::customWhatsThis(). + + The What's This object can be removed using TQWhatsThis::remove(), + although this is rarely necessary because it is automatically + removed when the widget is destroyed. + + \sa TQToolTip +*/ + +// a special button +class TQWhatsThisButton: public TQToolButton +{ + Q_OBJECT + +public: + TQWhatsThisButton( TQWidget * parent, const char * name ); + ~TQWhatsThisButton(); + +public slots: + void mouseReleased(); + +}; + + +class TQWhatsThat : public TQWidget +{ + Q_OBJECT +public: + TQWhatsThat( TQWidget* w, const TQString& txt, TQWidget* parent, const char* name ); + ~TQWhatsThat() ; + +public slots: + void hide(); + inline void widgetDestroyed() { widget = 0; } + +protected: + void mousePressEvent( TQMouseEvent* ); + void mouseReleaseEvent( TQMouseEvent* ); + void mouseMoveEvent( TQMouseEvent* ); + void keyPressEvent( TQKeyEvent* ); + void paintEvent( TQPaintEvent* ); + +private: + TQString text; +#ifndef QT_NO_RICHTEXT + TQSimpleRichText* doc; +#endif + TQString anchor; + bool pressed; + TQWidget* widget; +}; + + +class TQWhatsThisPrivate: public TQObject +{ + Q_OBJECT +public: + + // an item for storing texts + struct WhatsThisItem: public TQShared + { + WhatsThisItem(): TQShared() { whatsthis = 0; } + ~WhatsThisItem(); + TQString s; + TQWhatsThis* whatsthis; + }; + + // the (these days pretty small) state machine + enum State { Inactive, Waiting }; + + TQWhatsThisPrivate(); + ~TQWhatsThisPrivate(); + + bool eventFilter( TQObject *, TQEvent * ); + + WhatsThisItem* newItem( TQWidget * widget ); + void add( TQWidget * widget, TQWhatsThis* special ); + void add( TQWidget * widget, const TQString& text ); + + // say it. + void say( TQWidget *, const TQString&, const TQPoint& ); + + // setup and teardown + static void setUpWhatsThis(); + + void enterWhatsThisMode(); + void leaveWhatsThisMode(); + + // variables + TQWhatsThat * whatsThat; + TQPtrDict<WhatsThisItem> * dict; + TQPtrDict<TQWidget> * tlw; + TQPtrDict<TQWhatsThisButton> * buttons; + State state; + +private slots: + void cleanupWidget() + { + const TQObject* o = sender(); + if ( o->isWidgetType() ) // sanity + TQWhatsThis::remove((TQWidget*)o); + } + +}; + +// static, but static the less-typing way +static TQWhatsThisPrivate * 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 TQPopupMenu destroy the TQWhatsThat. +void qWhatsThisBDH() +{ + if ( wt && wt->whatsThat ) + wt->whatsThat->hide(); +} + + +TQWhatsThat::TQWhatsThat( TQWidget* w, const TQString& txt, TQWidget* parent, const char* name ) + : TQWidget( parent, name, WType_Popup ), text( txt ), pressed( FALSE ), widget( w ) +{ + + setBackgroundMode( NoBackground ); + setPalette( TQToolTip::palette() ); + setMouseTracking( TRUE ); +#ifndef QT_NO_CURSOR + setCursor( arrowCursor ); +#endif + + if ( widget ) + connect( widget, SIGNAL( destroyed() ), this, SLOT( widgetDestroyed() ) ); + + + TQRect r; +#ifndef QT_NO_RICHTEXT + doc = 0; + if ( TQStyleSheet::mightBeRichText( text ) ) { + TQFont f = TQApplication::font( this ); + doc = new TQSimpleRichText( text, f ); + doc->adjustSize(); + r.setRect( 0, 0, doc->width(), doc->height() ); + } + else +#endif + { + int sw = TQApplication::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 ); +} + +TQWhatsThat::~TQWhatsThat() +{ + if ( wt && wt->whatsThat == this ) + wt->whatsThat = 0; +#ifndef QT_NO_RICHTEXT + if ( doc ) + delete doc; +#endif +} + +void TQWhatsThat::hide() +{ + TQWidget::hide(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ContextHelpEnd ); +#endif +} + +void TQWhatsThat::mousePressEvent( TQMouseEvent* e ) +{ + pressed = TRUE; + if ( e->button() == LeftButton && rect().contains( e->pos() ) ) { +#ifndef QT_NO_RICHTEXT + if ( doc ) + anchor = doc->anchorAt( e->pos() - TQPoint( hMargin, vMargin) ); +#endif + return; + } + hide(); +} + +void TQWhatsThat::mouseReleaseEvent( TQMouseEvent* e ) +{ + if ( !pressed ) + return; +#ifndef QT_NO_RICHTEXT + if ( e->button() == LeftButton && doc && rect().contains( e->pos() ) ) { + TQString a = doc->anchorAt( e->pos() - TQPoint( hMargin, vMargin ) ); + TQString href; + if ( anchor == a ) + href = a; + anchor = TQString::null; + if ( widget && wt && wt->dict ) { + TQWhatsThisPrivate::WhatsThisItem * i = wt->dict->find( widget ); + if ( i && i->whatsthis && !i->whatsthis->clicked( href ) ) + return; + } + } +#endif + hide(); +} + +void TQWhatsThat::mouseMoveEvent( TQMouseEvent* e) +{ +#ifndef QT_NO_RICHTEXT +#ifndef QT_NO_CURSOR + if ( !doc ) + return; + TQString a = doc->anchorAt( e->pos() - TQPoint( hMargin, vMargin ) ); + if ( !a.isEmpty() ) + setCursor( pointingHandCursor ); + else + setCursor( arrowCursor ); +#endif +#endif +} + + +void TQWhatsThat::keyPressEvent( TQKeyEvent* ) +{ + hide(); +} + + + +void TQWhatsThat::paintEvent( TQPaintEvent* ) +{ + 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 + + TQRect r = rect(); + if ( drawShadow ) + r.addCoords( 0, 0, -shadowWidth, -shadowWidth ); + TQPainter p( this); + p.setPen( colorGroup().foreground() ); + p.drawRect( r ); + p.setPen( colorGroup().mid() ); + p.setBrush( colorGroup().brush( TQColorGroup::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 +TQWhatsThisPrivate::WhatsThisItem::~WhatsThisItem() +{ + if ( count ) + qFatal( "TQWhatsThis: 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 +TQWhatsThisButton::TQWhatsThisButton( TQWidget * parent, const char * name ) + : TQToolButton( parent, name ) +{ + TQPixmap 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() ) ); +} + + +TQWhatsThisButton::~TQWhatsThisButton() +{ + if ( wt && wt->buttons ) + wt->buttons->take( (void *)this ); +} + + +void TQWhatsThisButton::mouseReleased() +{ + if ( wt->state == TQWhatsThisPrivate::Inactive && isOn() ) { + TQWhatsThisPrivate::setUpWhatsThis(); +#ifndef QT_NO_CURSOR + TQApplication::setOverrideCursor( whatsThisCursor, FALSE ); +#endif + wt->state = TQWhatsThisPrivate::Waiting; + qApp->installEventFilter( wt ); + } +} + +static void qWhatsThisPrivateCleanup() +{ + if( wt ) { + delete wt; + wt = 0; + } +} + +// the what's this manager class +TQWhatsThisPrivate::TQWhatsThisPrivate() + : TQObject( 0, "global what's this object" ) +{ + whatsThat = 0; + dict = new TQPtrDict<TQWhatsThisPrivate::WhatsThisItem>; + tlw = new TQPtrDict<TQWidget>; + wt = this; + buttons = new TQPtrDict<TQWhatsThisButton>; + state = Inactive; +} + +TQWhatsThisPrivate::~TQWhatsThisPrivate() +{ +#ifndef QT_NO_CURSOR + if ( state == Waiting && qApp ) + TQApplication::restoreOverrideCursor(); +#endif + // the two straight-and-simple dicts + delete tlw; + delete buttons; + + // then delete the complex one. + TQPtrDictIterator<WhatsThisItem> it( *dict ); + WhatsThisItem * i; + TQWidget * w; + while( (i=it.current()) != 0 ) { + w = (TQWidget *)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 TQWhatsThisPrivate::eventFilter( TQObject * o, TQEvent * e ) +{ + switch( state ) { + case Waiting: + if ( e->type() == TQEvent::MouseButtonPress && o->isWidgetType() ) { + TQWidget * w = (TQWidget *) o; + if ( ( (TQMouseEvent*)e)->button() == RightButton ) + return FALSE; // ignore RMB + if ( w->customWhatsThis() ) + return FALSE; + TQWhatsThisPrivate::WhatsThisItem * i = 0; + TQMouseEvent* me = (TQMouseEvent*) e; + TQPoint 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) + TQAccessible::updateAccessibility( this, 0, TQAccessible::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() == TQEvent::MouseButtonRelease ) { + if ( ( (TQMouseEvent*)e)->button() == RightButton ) + return FALSE; // ignore RMB + return !o->isWidgetType() || !((TQWidget*)o)->customWhatsThis(); + } else if ( e->type() == TQEvent::MouseMove ) { + return !o->isWidgetType() || !((TQWidget*)o)->customWhatsThis(); + } else if ( e->type() == TQEvent::KeyPress ) { + TQKeyEvent* kev = (TQKeyEvent*)e; + + if ( kev->key() == TQt::Key_Escape ) { + leaveWhatsThisMode(); + return TRUE; + } else if ( o->isWidgetType() && ((TQWidget*)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() == TQEvent::MouseButtonDblClick ) { + return TRUE; + } + break; + case Inactive: + if ( e->type() == TQEvent::Accel && + ((TQKeyEvent *)e)->key() == Key_F1 && + o->isWidgetType() && + ((TQKeyEvent *)e)->state() == ShiftButton ) { + TQWidget * w = ((TQWidget *)o)->focusWidget(); + if ( !w ) + break; + TQString s = TQWhatsThis::textFor( w, TQPoint(0,0), TRUE ); + if ( !s.isNull() ) { + say ( w, s, w->mapToGlobal( w->rect().center() ) ); + ((TQKeyEvent *)e)->accept(); + return TRUE; + } + } + break; + } + return FALSE; +} + + + +void TQWhatsThisPrivate::setUpWhatsThis() +{ + if ( !wt ) { + wt = new TQWhatsThisPrivate(); + + // 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 TQWhatsThisPrivate::enterWhatsThisMode() +{ +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::ContextHelpStart ); +#endif +} + + +void TQWhatsThisPrivate::leaveWhatsThisMode() +{ + if ( state == Waiting ) { + TQPtrDictIterator<TQWhatsThisButton> it( *(wt->buttons) ); + TQWhatsThisButton * b; + while( (b=it.current()) != 0 ) { + ++it; + b->setOn( FALSE ); + } +#ifndef QT_NO_CURSOR + TQApplication::restoreOverrideCursor(); +#endif + state = Inactive; + qApp->removeEventFilter( this ); + } +} + + + +void TQWhatsThisPrivate::say( TQWidget * widget, const TQString &text, const TQPoint& ppos) +{ + if ( text.isEmpty() ) + return; + // make a fresh widget, and set it up + delete whatsThat; + whatsThat = new TQWhatsThat( + widget, text, +#if defined(Q_WS_X11) + TQApplication::desktop()->screen( widget ? + widget->x11Screen() : + TQCursor::x11Screen() ), +#else + 0, +#endif + "automatic what's this? widget" ); + + + // okay, now to find a suitable location + + int scr = ( widget ? + TQApplication::desktop()->screenNumber( widget ) : +#if defined(Q_WS_X11) + TQCursor::x11Screen() +#else + TQApplication::desktop()->screenNumber( ppos ) +#endif // Q_WS_X11 + ); + TQRect screen = TQApplication::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. + TQPoint pos; + if ( widget ) + pos = widget->mapToGlobal( TQPoint( 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? (TQMIN(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 ? (TQMIN(screen.height(), + pos.y() + widget->height()) + ) : screen.height() ) + - h; + if ( y < sy ) + y = sy; + + whatsThat->move( x, y ); + whatsThat->show(); +} + +TQWhatsThisPrivate::WhatsThisItem* TQWhatsThisPrivate::newItem( TQWidget * widget ) +{ + WhatsThisItem * i = dict->find( (void *)widget ); + if ( i ) + TQWhatsThis::remove( widget ); + i = new WhatsThisItem; + dict->insert( (void *)widget, i ); + TQWidget * 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 TQWhatsThisPrivate::add( TQWidget * widget, TQWhatsThis* special ) +{ + newItem( widget )->whatsthis = special; +} + +void TQWhatsThisPrivate::add( TQWidget * widget, const TQString &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 TQStyleSheet::defaultSheet(). + + The text is destroyed if the widget is later destroyed, so it need + not be explicitly removed. + + \sa remove() +*/ +void TQWhatsThis::add( TQWidget * widget, const TQString &text ) +{ + if ( text.isEmpty() ) + return; // pointless + TQWhatsThisPrivate::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 TQWhatsThis::remove( TQWidget * widget ) +{ + TQWhatsThisPrivate::setUpWhatsThis(); + TQWhatsThisPrivate::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 TQString::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() +*/ +TQString TQWhatsThis::textFor( TQWidget * w, const TQPoint& pos, bool includeParents ) +{ + TQWhatsThisPrivate::setUpWhatsThis(); + TQWhatsThisPrivate::WhatsThisItem * i = 0; + TQPoint 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 TQString::null; + if ( i->whatsthis ) + return i->whatsthis->text( p ); + return i->s; +} + + +/*! + Creates a TQToolButton preconfigured to enter "What's this?" mode + when clicked. You will often use this with a tool bar as \a + parent: + \code + (void) TQWhatsThis::whatsThisButton( my_help_tool_bar ); + \endcode +*/ +TQToolButton * TQWhatsThis::whatsThisButton( TQWidget * parent ) +{ + TQWhatsThisPrivate::setUpWhatsThis(); + return new TQWhatsThisButton( 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 + TQWhatsThis will be called to provide the appropriate text, rather + than using the text assigned by add(). +*/ +TQWhatsThis::TQWhatsThis( TQWidget * widget) +{ + TQWhatsThisPrivate::setUpWhatsThis(); + wt->add(widget,this); +} + + +/*! + Destroys the object and frees any allocated resources. +*/ +TQWhatsThis::~TQWhatsThis() +{ +} + + +/*! + 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, TQString::null is returned. + + The default implementation returns TQString::null. +*/ +TQString TQWhatsThis::text( const TQPoint & ) +{ + return TQString::null; +} + +/*! + \fn bool TQWhatsThis::clicked( const TQString& 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 + TQString::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 TQWhatsThis::clicked( const TQString& ) +{ + return TRUE; +} + + +/*! + Enters "What's this?" mode and returns immediately. + + TQt 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, TQt 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 TQWhatsThis::enterWhatsThisMode() +{ + TQWhatsThisPrivate::setUpWhatsThis(); + if ( wt->state == TQWhatsThisPrivate::Inactive ) { + wt->enterWhatsThisMode(); +#ifndef QT_NO_CURSOR + TQApplication::setOverrideCursor( whatsThisCursor, FALSE ); +#endif + wt->state = TQWhatsThisPrivate::Waiting; + qApp->installEventFilter( wt ); + } +} + + +/*! + Returns TRUE if the application is in "What's this?" mode; + otherwise returns FALSE. + + \sa enterWhatsThisMode(), leaveWhatsThisMode() +*/ +bool TQWhatsThis::inWhatsThisMode() +{ + if (!wt) + return FALSE; + return wt->state == TQWhatsThisPrivate::Waiting; +} + + +/*! + Leaves "What's this?" question mode. + + This function is used internally by widgets that support + TQWidget::customWhatsThis(); applications do not usually call it. + An example of such a widget is TQPopupMenu: menus still work + normally in "What's this?" mode but also provide help texts for + individual menu items. + + If \a text is not TQString::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 TQWhatsThis object, this object + will receive clicked() messages when the user clicks on hyperlinks + inside the help text. + + \sa inWhatsThisMode(), enterWhatsThisMode(), TQWhatsThis::clicked() +*/ +void TQWhatsThis::leaveWhatsThisMode( const TQString& text, const TQPoint& pos, TQWidget* 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 TQWhatsThis + object, this object will receive clicked() messages when the user + clicks on hyperlinks inside the help text. + + \sa TQWhatsThis::clicked() +*/ +void TQWhatsThis::display( const TQString& text, const TQPoint& pos, TQWidget* w ) +{ + if ( inWhatsThisMode() ) { + leaveWhatsThisMode( text, pos, w ); + return; + } + TQWhatsThisPrivate::setUpWhatsThis(); + wt->say( w, text, pos ); +} + +/*! + Sets the font for all "What's this?" helps to \a font. +*/ +void TQWhatsThis::setFont( const TQFont &font ) +{ + TQApplication::setFont( font, TRUE, "TQWhatsThat" ); +} + +#include "qwhatsthis.moc" +#endif diff --git a/src/widgets/qwhatsthis.h b/src/widgets/qwhatsthis.h new file mode 100644 index 000000000..0c68159c8 --- /dev/null +++ b/src/widgets/qwhatsthis.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Definition of TQWhatsThis class +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQWHATSTHIS_H +#define TQWHATSTHIS_H + +#ifndef QT_H +#include "qobject.h" +#endif // QT_H + +#ifndef QT_NO_WHATSTHIS + +#include "qcursor.h" + +class TQToolButton; +class TQPopupMenu; +class TQStyleSheet; + +class Q_EXPORT TQWhatsThis: public TQt +{ +public: + TQWhatsThis( TQWidget *); + virtual ~TQWhatsThis(); + + virtual TQString text( const TQPoint & ); + virtual bool clicked( const TQString& href ); + + // the common static functions + static void setFont( const TQFont &font ); + + static void add( TQWidget *, const TQString &); + static void remove( TQWidget * ); + static TQString textFor( TQWidget *, const TQPoint & pos = TQPoint(), bool includeParents = FALSE ); + + static TQToolButton * whatsThisButton( TQWidget * parent ); + + static void enterWhatsThisMode(); + static bool inWhatsThisMode(); + static void leaveWhatsThisMode( const TQString& = TQString::null, const TQPoint& pos = TQCursor::pos(), TQWidget* w = 0 ); + + static void display( const TQString& text, const TQPoint& pos = TQCursor::pos(), TQWidget* w = 0 ); +}; + +#endif // QT_NO_WHATSTHIS + +#endif // TQWHATSTHIS_H diff --git a/src/widgets/qwidgetinterface_p.h b/src/widgets/qwidgetinterface_p.h new file mode 100644 index 000000000..7d2864e23 --- /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 TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQWIDGETINTERFACE_P_H +#define TQWIDGETINTERFACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the TQt API. It exists for the convenience +// of a number of TQt 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 TQWidget; + +// {55184143-f18f-42c0-a8eb-71c01516019a} +#ifndef IID_QWidgetFactory +#define IID_QWidgetFactory TQUuid( 0x55184143, 0xf18f, 0x42c0, 0xa8, 0xeb, 0x71, 0xc0, 0x15, 0x16, 0x1, 0x9a ) +#endif + +/*! To add custom widgets to the TQt Designer, implement that interface + in your custom widget plugin. + + You also have to implement the function featureList() (\sa + TQFeatureListInterface) and return there all widgets (names of it) + which this interface provides. +*/ + +struct TQWidgetFactoryInterface : public TQFeatureListInterface +{ +public: + + /*! In the implementation create and return the widget \a widget + here, use \a parent and \a name when creating the widget */ + virtual TQWidget* create( const TQString &widget, TQWidget* parent = 0, const char* name = 0 ) = 0; + + /*! In the implementation return the name of the group of the + widget \a widget */ + virtual TQString group( const TQString &widget ) const = 0; + + /*! In the implementation return the iconset, which should be used + in the TQt Designer menubar and toolbar to represent the widget + \a widget */ + virtual TQIconSet iconSet( const TQString &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 TQString includeFile( const TQString &widget ) const = 0; + + /*! In the implementation return the text which should be + displayed as tooltip for the widget \a widget */ + virtual TQString toolTip( const TQString &widget ) const = 0; + + /*! In the implementation return the text which should be used for + what's this help for the widget \a widget. */ + virtual TQString whatsThis( const TQString &widget ) const = 0; + + /*! In the implementation return TRUE here, of the \a widget + should be able to contain other widget in the TQt Designer, else + FALSE. */ + virtual bool isContainer( const TQString &widget ) const = 0; +}; + +#ifdef QT_CONTAINER_CUSTOM_WIDGETS +// {15976628-e3c3-47f4-b525-d124a3caf30e} +#ifndef IID_QWidgetContainer +#define IID_QWidgetContainer TQUuid( 0x15976628, 0xe3c3, 0x47f4, 0xb5, 0x25, 0xd1, 0x24, 0xa3, 0xca, 0xf3, 0x0e ) +#endif + +struct TQWidgetContainerInterfacePrivate : public TQUnknownInterface +{ +public: + virtual TQWidget *containerOfWidget( const TQString &f, TQWidget *container ) const = 0; + virtual bool isPassiveInteractor( const TQString &f, TQWidget *container ) const = 0; + + virtual bool supportsPages( const TQString &f ) const = 0; + + virtual TQWidget *addPage( const TQString &f, TQWidget *container, + const TQString &name, int index ) const = 0; + virtual void insertPage( const TQString &f, TQWidget *container, + const TQString &name, int index, TQWidget *page ) const = 0; + virtual void removePage( const TQString &f, TQWidget *container, int index ) const = 0; + virtual void movePage( const TQString &f, TQWidget *container, int fromIndex, int toIndex ) const = 0; + virtual int count( const TQString &key, TQWidget *container ) const = 0; + virtual int currentIndex( const TQString &key, TQWidget *container ) const = 0; + virtual TQString pageLabel( const TQString &key, TQWidget *container, int index ) const = 0; + virtual TQWidget *page( const TQString &key, TQWidget *container, int index ) const = 0; + virtual void renamePage( const TQString &key, TQWidget *container, + int index, const TQString &newName ) const = 0; + virtual TQWidgetList pages( const TQString &f, TQWidget *container ) const = 0; + virtual TQString createCode( const TQString &f, const TQString &container, + const TQString &page, const TQString &pageName ) const = 0; +}; + +#endif // QT_CONTAINER_CUSTOM_WIDGETS +#endif // QT_NO_WIDGETPLUGIN +#endif // TQWIDGETINTERFACE_P_H diff --git a/src/widgets/qwidgetplugin.cpp b/src/widgets/qwidgetplugin.cpp new file mode 100644 index 000000000..d0d34a469 --- /dev/null +++ b/src/widgets/qwidgetplugin.cpp @@ -0,0 +1,729 @@ +/**************************************************************************** +** +** Implementation of TQWidgetPlugin class +** +** Created : 010920 +** +** Copyright (C) 2001-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQWidgetPlugin qwidgetplugin.h + \brief The TQWidgetPlugin class provides an abstract base for custom TQWidget 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 TQt 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 TQt Designer manual's\endlink, + 'Creating Custom Widgets' section in the 'Creating Custom Widgets' + chapter, for a complete example of a TQWidgetPlugin. + + See also the \link plugins-howto.html Plugins + documentation\endlink and the \l{TQWidgetFactory} class that is + supplied with \link designer-manual.book TQt Designer\endlink. +*/ + +class TQWidgetPluginPrivate : public TQWidgetFactoryInterface, +#ifdef QT_CONTAINER_CUSTOM_WIDGETS + public TQWidgetContainerInterfacePrivate, +#endif + private TQLibraryInterface +{ +public: + TQWidgetPluginPrivate( TQWidgetPlugin *p ) + : plugin( p ) + { + } + + virtual ~TQWidgetPluginPrivate(); + + TQRESULT queryInterface( const TQUuid &iid, TQUnknownInterface **iface ); + Q_REFCOUNT; + + TQStringList featureList() const; + TQWidget *create( const TQString &key, TQWidget *parent, const char *name ); + TQString group( const TQString &widget ) const; + TQIconSet iconSet( const TQString &widget ) const; + TQString includeFile( const TQString &widget ) const; + TQString toolTip( const TQString &widget ) const; + TQString whatsThis( const TQString &widget ) const; + bool isContainer( const TQString &widget ) const; +#ifdef QT_CONTAINER_CUSTOM_WIDGETS + TQWidget* containerOfWidget( const TQString &key, TQWidget *widget ) const; + bool isPassiveInteractor( const TQString &key, TQWidget *widget ) const; + bool supportsPages( const TQString &key ) const; + TQWidget *addPage( const TQString &key, TQWidget *container, const TQString &name, int index ) const; + void insertPage( const TQString &key, TQWidget *container, + const TQString &name, int index, TQWidget *page ) const; + void Page( const TQString &key, TQWidget *container, + const TQString &name, int index, TQWidget *page ) const; + void removePage( const TQString &key, TQWidget *container, int index ) const; + void movePage( const TQString &key, TQWidget *container, int fromIndex, int toIndex ) const; + int count( const TQString &key, TQWidget *container ) const; + int currentIndex( const TQString &key, TQWidget *container ) const; + TQString pageLabel( const TQString &key, TQWidget *container, int index ) const; + TQWidget *page( const TQString &key, TQWidget *container, int index ) const; + void renamePage( const TQString &key, TQWidget *container, int index, const TQString &newName ) const; + TQWidgetList pages( const TQString &key, TQWidget *container ) const; + TQString createCode( const TQString &key, const TQString &container, + const TQString &page, const TQString &pageName ) const; +#endif // QT_CONTAINER_CUSTOM_WIDGETS + bool init(); + void cleanup(); + bool canUnload() const; + +private: + TQWidgetPlugin *plugin; + TQObjectCleanupHandler widgets; +}; + +TQRESULT TQWidgetPluginPrivate::queryInterface( const TQUuid &iid, TQUnknownInterface **iface ) +{ + *iface = 0; + + if ( iid == IID_QUnknown ) + *iface = (TQWidgetFactoryInterface*)this; + else if ( iid == IID_QFeatureList ) + *iface = (TQFeatureListInterface*)this; + else if ( iid == IID_QWidgetFactory ) + *iface = (TQWidgetFactoryInterface*)this; + else if ( iid == IID_QLibrary ) + *iface = (TQLibraryInterface*)this; +#ifdef QT_CONTAINER_CUSTOM_WIDGETS + else if ( iid == IID_QWidgetContainer ) + *iface = (TQWidgetContainerInterfacePrivate*)this; +#endif + else + return TQE_NOINTERFACE; + + (*iface)->addRef(); + return TQS_OK; +} + +/*! + \fn TQStringList TQWidgetPlugin::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 TQWidget *TQWidgetPlugin::create( const TQString &, TQWidget *, const char * ) + + Creates and returns a TQWidget object for the widget key \a key. + The widget key is the class name of the retquired widget. The \a + name and \a parent arguments are passed to the custom widget's + constructor. + + \sa keys() +*/ + +TQWidgetPluginPrivate::~TQWidgetPluginPrivate() +{ + delete plugin; +} + +TQStringList TQWidgetPluginPrivate::featureList() const +{ + return plugin->keys(); +} + +TQWidget *TQWidgetPluginPrivate::create( const TQString &key, TQWidget *parent, const char *name ) +{ + TQWidget *w = plugin->create( key, parent, name ); + widgets.add( w ); + return w; +} + +TQString TQWidgetPluginPrivate::group( const TQString &widget ) const +{ + return plugin->group( widget ); +} + +TQIconSet TQWidgetPluginPrivate::iconSet( const TQString &widget ) const +{ + return plugin->iconSet( widget ); +} + +TQString TQWidgetPluginPrivate::includeFile( const TQString &widget ) const +{ + return plugin->includeFile( widget ); +} + +TQString TQWidgetPluginPrivate::toolTip( const TQString &widget ) const +{ + return plugin->toolTip( widget ); +} + +TQString TQWidgetPluginPrivate::whatsThis( const TQString &widget ) const +{ + return plugin->whatsThis( widget ); +} + +bool TQWidgetPluginPrivate::isContainer( const TQString &widget ) const +{ + return plugin->isContainer( widget ); +} + +bool TQWidgetPluginPrivate::init() +{ + return TRUE; +} + +void TQWidgetPluginPrivate::cleanup() +{ + widgets.clear(); +} + +bool TQWidgetPluginPrivate::canUnload() const +{ + return widgets.isEmpty(); +} + +#ifdef QT_CONTAINER_CUSTOM_WIDGETS +TQWidget* TQWidgetPluginPrivate::containerOfWidget( const TQString &key, TQWidget *widget ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + return p->containerOfWidget( key, widget ); + return widget; +} + +int TQWidgetPluginPrivate::count( const TQString &key, TQWidget *container ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + return p->count( key, container ); + return 0; +} + +int TQWidgetPluginPrivate::currentIndex( const TQString &key, TQWidget *container ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + return p->currentIndex( key, container ); + return -1; +} + +TQString TQWidgetPluginPrivate::pageLabel( const TQString &key, TQWidget *container, int index ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + return p->pageLabel( key, container, index ); + return TQString::null; +} + +TQWidget *TQWidgetPluginPrivate::page( const TQString &key, TQWidget *container, int index ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + return p->page( key, container, index ); + return 0; +} + +bool TQWidgetPluginPrivate::isPassiveInteractor( const TQString &key, TQWidget *widget ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + return p->isPassiveInteractor( key, widget ); + return FALSE; +} + +bool TQWidgetPluginPrivate::supportsPages( const TQString &key ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + return p->supportsPages( key ); + return 0; +} + +TQWidget *TQWidgetPluginPrivate::addPage( const TQString &key, TQWidget *container, + const TQString &name, int index ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + return p->addPage( key, container, name, index ); + return 0; +} + +void TQWidgetPluginPrivate::insertPage( const TQString &key, TQWidget *container, + const TQString &name, int index, TQWidget *page ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + p->insertPage( key, container, name, index, page ); +} + +void TQWidgetPluginPrivate::removePage( const TQString &key, TQWidget *container, int index ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + p->removePage( key, container, index ); +} + +void TQWidgetPluginPrivate::movePage( const TQString &key, TQWidget *container, + int fromIndex, int toIndex ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + p->movePage( key, container, fromIndex, toIndex ); +} + +void TQWidgetPluginPrivate::renamePage( const TQString &key, TQWidget *container, + int index, const TQString &newName ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + p->renamePage( key, container, index, newName ); +} + +TQWidgetList TQWidgetPluginPrivate::pages( const TQString &key, TQWidget *container ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + return p->pages( key, container ); + return TQWidgetList(); +} + +TQString TQWidgetPluginPrivate::createCode( const TQString &key, const TQString &container, + const TQString &page, const TQString &pageName ) const +{ + TQWidgetContainerPlugin *p = (TQWidgetContainerPlugin*)plugin->qt_cast( "TQWidgetContainerPlugin" ); + if ( p ) + return p->createCode( key, container, page, pageName ); + return TQString::null; +} +#endif // QT_CONTAINER_CUSTOM_WIDGETS + +/*! + Constructs a widget plugin. This is invoked automatically by the + \c Q_EXPORT_PLUGIN macro. +*/ +TQWidgetPlugin::TQWidgetPlugin() + : TQGPlugin( (TQWidgetFactoryInterface*)(d = new TQWidgetPluginPrivate( this )) ) +{ +} + +/*! + Destroys the widget plugin. + + You never have to call this explicitly. TQt destroys a plugin + automatically when it is no longer used. +*/ +TQWidgetPlugin::~TQWidgetPlugin() +{ + // 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{TQt Designer} loads it. + + The default implementation returns TQString::null. +*/ +TQString TQWidgetPlugin::group( const TQString & ) const +{ + return TQString::null; +} + +/*! + Returns the iconset that \e{TQt Designer} should use to represent + the custom widget of class \a key in the toolbar. + + The default implementation returns an null iconset. +*/ +TQIconSet TQWidgetPlugin::iconSet( const TQString & ) const +{ + return TQIconSet(); +} + +/*! + Returns the name of the include file that \e{TQt Designer} and \c + uic should use to include the custom widget of class \a key in + generated code. + + The default implementation returns TQString::null. +*/ +TQString TQWidgetPlugin::includeFile( const TQString & ) const +{ + return TQString::null; +} + +/*! + Returns the text of the tooltip that \e{TQt Designer} should use + for the custom widget of class \a key's toolbar button. + + The default implementation returns TQString::null. +*/ +TQString TQWidgetPlugin::toolTip( const TQString & ) const +{ + return TQString::null; +} + +/*! + Returns the text of the whatsThis text that \e{TQt Designer} should + use when the user requests whatsThis help for the custom widget of + class \a key. + + The default implementation returns TQString::null. +*/ +TQString TQWidgetPlugin::whatsThis( const TQString & ) const +{ + return TQString::null; +} + +/*! + Returns TRUE if the custom widget of class \a key can contain + other widgets, e.g. like TQFrame; otherwise returns FALSE. + + The default implementation returns FALSE. +*/ +bool TQWidgetPlugin::isContainer( const TQString & ) const +{ + return FALSE; +} + +#ifdef QT_CONTAINER_CUSTOM_WIDGETS + +/*! + \class TQWidgetContainerPlugin qwidgetplugin.h + \brief The TQWidgetContainerPlugin class provides an abstract base + for complex custom container TQWidget plugins. + + \internal + + \ingroup plugins + + The widget container plugin is a subclass of TQWidgetPlugin 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: + TQTabWidget, TQWidgetStack and TQToolBox. + + Writing a complex container widget plugin is achieved by + subclassing this base class. First by reimplementing + TQWidgetPlugin'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 TQWidgetPlugin +*/ + +/*! + Constructs a complex container widget plugin. This is invoked + automatically by the \c Q_EXPORT_PLUGIN macro. +*/ + +TQWidgetContainerPlugin::TQWidgetContainerPlugin() + : TQWidgetPlugin() +{ +} + +/*! + Destroys the complex container widget plugin. + + You never have to call this explicitly. TQt destroys a plugin + automatically when it is no longer used. +*/ + +TQWidgetContainerPlugin::~TQWidgetContainerPlugin() +{ +} + +/*! + 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. +*/ + +TQWidget* TQWidgetContainerPlugin::containerOfWidget( const TQString &, + TQWidget *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 TQWidgetContainerPlugin::count( const TQString &, TQWidget * ) 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 TQWidgetContainerPlugin::currentIndex( const TQString &, TQWidget * ) 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. +*/ + +TQString TQWidgetContainerPlugin::pageLabel( const TQString &, TQWidget *, int ) const +{ + return TQString::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. +*/ + +TQWidget *TQWidgetContainerPlugin::page( const TQString &, TQWidget *, 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{TQt Designer}'s design + mode, no widget receives any mouse or key events, since \e{TQt + 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{TQt 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 TQWidgetContainerPlugin::isPassiveInteractor( const TQString &, + TQWidget *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 TQWidgetContainerPlugin::supportsPages( const TQString & ) 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. +*/ + +TQWidget* TQWidgetContainerPlugin::addPage( const TQString &, TQWidget *, + const TQString &, 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 TQWidgetContainerPlugin::insertPage( const TQString &, TQWidget *, + const TQString &, int, TQWidget * ) 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 TQWidgetContainerPlugin::removePage( const TQString &, TQWidget *, 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 TQWidgetContainerPlugin::movePage( const TQString &, TQWidget *, 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 TQWidgetContainerPlugin::renamePage( const TQString &, TQWidget *, + int, const TQString & ) const +{ +} + +/*! + Operates on the plugin's \a key class. + + This function should return a list of the \a container custom + widget's pages. +*/ + +TQWidgetList TQWidgetContainerPlugin::pages( const TQString &, TQWidget * ) const +{ + return TQWidgetList(); +} + +/*! + Operates on the plugin's \a key class. + + This function is called from \e{TQt 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 TQTabWidget, 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. +*/ + +TQString TQWidgetContainerPlugin::createCode( const TQString &, const TQString &, + const TQString &, const TQString & ) const +{ + return TQString::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 000000000..86167f511 --- /dev/null +++ b/src/widgets/qwidgetplugin.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Definition of TQWidgetPlugin class +** +** Created : 010920 +** +** Copyright (C) 2001-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQWIDGETPLUGIN_H +#define TQWIDGETPLUGIN_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 TQWidgetPluginPrivate; +class TQWidget; + +class Q_EXPORT TQWidgetPlugin : public TQGPlugin +{ + Q_OBJECT +public: + TQWidgetPlugin(); + ~TQWidgetPlugin(); + + virtual TQStringList keys() const = 0; + virtual TQWidget *create( const TQString &key, TQWidget *parent = 0, const char *name = 0 ) = 0; + + virtual TQString group( const TQString &key ) const; + virtual TQIconSet iconSet( const TQString &key ) const; + virtual TQString includeFile( const TQString &key ) const; + virtual TQString toolTip( const TQString &key ) const; + virtual TQString whatsThis( const TQString &key ) const; + virtual bool isContainer( const TQString &key ) const; + +private: + TQWidgetPluginPrivate *d; +}; + +#ifdef QT_CONTAINER_CUSTOM_WIDGETS + +class TQWidgetContainerPluginPrivate; + +class Q_EXPORT TQWidgetContainerPlugin : public TQWidgetPlugin +{ + +public: + TQWidgetContainerPlugin(); + ~TQWidgetContainerPlugin(); + + virtual TQWidget* containerOfWidget( const TQString &key, TQWidget *container ) const; + virtual bool isPassiveInteractor( const TQString &key, TQWidget *container ) const; + + virtual bool supportsPages( const TQString &key ) const; + + virtual TQWidget *addPage( const TQString &key, TQWidget *container, + const TQString &name, int index ) const; + virtual void insertPage( const TQString &key, TQWidget *container, + const TQString &name, int index, TQWidget *page ) const; + virtual void removePage( const TQString &key, TQWidget *container, int index ) const; + virtual void movePage( const TQString &key, TQWidget *container, int fromIndex, int toIndex ) const; + virtual int count( const TQString &key, TQWidget *container ) const; + virtual int currentIndex( const TQString &key, TQWidget *container ) const; + virtual TQString pageLabel( const TQString &key, TQWidget *container, int index ) const; + virtual TQWidget *page( const TQString &key, TQWidget *container, int index ) const; + virtual void renamePage( const TQString &key, TQWidget *container, + int index, const TQString &newName ) const; + virtual TQWidgetList pages( const TQString &key, TQWidget *container ) const; + virtual TQString createCode( const TQString &key, const TQString &container, + const TQString &page, const TQString &pageName ) const; +}; + +#endif // QT_CONTAINER_CUSTOM_WIDGETS +#endif // QT_NO_WIDGETPLUGIN +#endif // TQWIDGETPLUGIN_H diff --git a/src/widgets/qwidgetresizehandler.cpp b/src/widgets/qwidgetresizehandler.cpp new file mode 100644 index 000000000..6ce1e7789 --- /dev/null +++ b/src/widgets/qwidgetresizehandler.cpp @@ -0,0 +1,516 @@ +/**************************************************************************** +** +** Implementation of the TQWidgetResizeHandler class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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; + +TQWidgetResizeHandler::TQWidgetResizeHandler( TQWidget *parent, TQWidget *cw, const char *name ) + : TQObject( parent, name ), widget( parent ), childWidget( cw ? cw : parent ), + extrahei( 0 ), buttonDown( FALSE ), moveResizeMode( FALSE ), sizeprotect( TRUE ), moving( TRUE ) +{ + mode = Nowhere; + widget->setMouseTracking( TRUE ); + TQFrame *frame = ::qt_cast<TQFrame*>(widget); + range = frame ? frame->frameWidth() : RANGE; + range = TQMAX( RANGE, range ); + activeForMove = activeForResize = TRUE; + qApp->installEventFilter( this ); +} + +void TQWidgetResizeHandler::setActive( Action ac, bool b ) +{ + if ( ac & Move ) + activeForMove = b; + if ( ac & Resize ) + activeForResize = b; + + if ( !isActive() ) + setMouseCursor( Nowhere ); +} + +bool TQWidgetResizeHandler::isActive( Action ac ) const +{ + bool b = FALSE; + if ( ac & Move ) b = activeForMove; + if ( ac & Resize ) b |= activeForResize; + + return b; +} + +static TQWidget *childOf( TQWidget *w, TQWidget *child ) +{ + while ( child ) { + if ( child == w ) + return child; + if ( child->isTopLevel() ) + break; + child = child->parentWidget(); + } + return 0; +} + +bool TQWidgetResizeHandler::eventFilter( TQObject *o, TQEvent *ee ) +{ + if ( !isActive() || !o->isWidgetType() || !ee->spontaneous()) + return FALSE; + + if ( ee->type() != TQEvent::MouseButtonPress && + ee->type() != TQEvent::MouseButtonRelease && + ee->type() != TQEvent::MouseMove && + ee->type() != TQEvent::KeyPress && + ee->type() != TQEvent::AccelOverride ) + return FALSE; + + TQWidget *w = childOf( widget, (TQWidget*)o ); + if ( !w +#ifndef QT_NO_SIZEGRIP + || ::qt_cast<TQSizeGrip*>(o) +#endif + || qApp->activePopupWidget() ) { + if ( buttonDown && ee->type() == TQEvent::MouseButtonRelease ) + buttonDown = FALSE; + return FALSE; + } + + TQMouseEvent *e = (TQMouseEvent*)ee; + switch ( e->type() ) { + case TQEvent::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 TQEvent::MouseButtonRelease: + if ( w->isMaximized() ) + break; + if ( e->button() == LeftButton ) { + moveResizeMode = FALSE; + buttonDown = FALSE; + widget->releaseMouse(); + widget->releaseKeyboard(); + } + break; + case TQEvent::MouseMove: { + if ( w->isMaximized() ) + break; + bool me = isMovingEnabled(); + setMovingEnabled( me && o == widget ); + mouseMoveEvent( e ); + setMovingEnabled( me ); + if ( buttonDown && mode != Center ) + return TRUE; + } break; + case TQEvent::KeyPress: + keyPressEvent( (TQKeyEvent*)e ); + break; + case TQEvent::AccelOverride: + if ( buttonDown ) { + ((TQKeyEvent*)ee)->accept(); + return TRUE; + } + break; + default: + break; + } + return FALSE; +} + +void TQWidgetResizeHandler::mouseMoveEvent( TQMouseEvent *e ) +{ + TQPoint 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; + + TQPoint 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(); + } + + TQPoint p = globalPos + invertedMoveOffset; + TQPoint pp = globalPos - moveOffset; + + int fw = 0; + int mw = TQMAX( childWidget->minimumSizeHint().width(), + childWidget->minimumWidth() ); + int mh = TQMAX( childWidget->minimumSizeHint().height(), + childWidget->minimumHeight() ); + if ( childWidget != widget ) { + TQFrame *frame = ::qt_cast<TQFrame*>(widget); + if ( frame ) + fw = frame->frameWidth(); + mw += 2 * fw; + mh += 2 * fw + extrahei; + } + + TQSize mpsize( widget->geometry().right() - pp.x() + 1, + widget->geometry().bottom() - pp.y() + 1 ); + mpsize = mpsize.expandedTo( widget->minimumSize() ).expandedTo( TQSize(mw, mh) ); + TQPoint mp( widget->geometry().right() - mpsize.width() + 1, + widget->geometry().bottom() - mpsize.height() + 1 ); + + TQRect geom = widget->geometry(); + + switch ( mode ) { + case TopLeft: + geom = TQRect( mp, widget->geometry().bottomRight() ) ; + break; + case BottomRight: + geom = TQRect( widget->geometry().topLeft(), p ) ; + break; + case BottomLeft: + geom = TQRect( TQPoint(mp.x(), widget->geometry().y() ), TQPoint( widget->geometry().right(), p.y()) ) ; + break; + case TopRight: + geom = TQRect( TQPoint( widget->geometry().x(), mp.y() ), TQPoint( p.x(), widget->geometry().bottom()) ) ; + break; + case Top: + geom = TQRect( TQPoint( widget->geometry().left(), mp.y() ), widget->geometry().bottomRight() ) ; + break; + case Bottom: + geom = TQRect( widget->geometry().topLeft(), TQPoint( widget->geometry().right(), p.y() ) ) ; + break; + case Left: + geom = TQRect( TQPoint( mp.x(), widget->geometry().top() ), widget->geometry().bottomRight() ) ; + break; + case Right: + geom = TQRect( widget->geometry().topLeft(), TQPoint( p.x(), widget->geometry().bottom() ) ) ; + break; + case Center: + if ( isMovingEnabled() || moveResizeMode ) + geom.moveTopLeft( pp ); + break; + default: + break; + } + + TQSize maxsize( childWidget->maximumSize() ); + if ( childWidget != widget ) + maxsize += TQSize( 2 * fw, 2 * fw + extrahei ); + + geom = TQRect( geom.topLeft(), + geom.size().expandedTo( widget->minimumSize() ) + .expandedTo( TQSize(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 + + TQApplication::syncX(); +} + +void TQWidgetResizeHandler::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 TQWidgetResizeHandler::keyPressEvent( TQKeyEvent * e ) +{ + if ( !isMove() && !isResize() ) + return; + bool is_control = e->state() & ControlButton; + int delta = is_control?1:8; + TQPoint pos = TQCursor::pos(); + switch ( e->key() ) { + case Key_Left: + pos.rx() -= delta; + if ( pos.x() <= TQApplication::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() >= TQApplication::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() <= TQApplication::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() >= TQApplication::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; + } + TQCursor::setPos( pos ); +} + + +void TQWidgetResizeHandler::doResize() +{ + if ( !activeForResize ) + return; + + moveResizeMode = TRUE; + buttonDown = TRUE; + moveOffset = widget->mapFromGlobal( TQCursor::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 TQWidgetResizeHandler::doMove() +{ + if ( !activeForMove ) + return; + + mode = Center; + moveResizeMode = TRUE; + buttonDown = TRUE; + moveOffset = widget->mapFromGlobal( TQCursor::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 000000000..89c5a0d7a --- /dev/null +++ b/src/widgets/qwidgetresizehandler_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Definition of the TQWidgetResizeHandler class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQWIDGETRESIZEHANDLER_P_H +#define TQWIDGETRESIZEHANDLER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the TQt 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 TQMouseEvent; +class TQKeyEvent; + +class Q_EXPORT TQWidgetResizeHandler : public TQObject +{ + Q_OBJECT + +public: + enum Action { + Move = 0x01, + Resize = 0x02, + Any = Move|Resize + }; + + TQWidgetResizeHandler( TQWidget *parent, TQWidget *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( TQObject *o, TQEvent *e ); + void mouseMoveEvent( TQMouseEvent *e ); + void keyPressEvent( TQKeyEvent *e ); + +private: + enum MousePosition { + Nowhere, + TopLeft, BottomRight, BottomLeft, TopRight, + Top, Bottom, Left, Right, + Center + }; + + TQWidget *widget; + TQWidget *childWidget; + TQPoint moveOffset; + TQPoint 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) + TQWidgetResizeHandler( const TQWidgetResizeHandler & ); + TQWidgetResizeHandler& operator=( const TQWidgetResizeHandler & ); +#endif + +}; + +#endif //QT_NO_RESIZEHANDLER +#endif diff --git a/src/widgets/qwidgetstack.cpp b/src/widgets/qwidgetstack.cpp new file mode 100644 index 000000000..5f7707124 --- /dev/null +++ b/src/widgets/qwidgetstack.cpp @@ -0,0 +1,610 @@ +/**************************************************************************** +** +** Implementation of TQWidgetStack class +** +** Created : 980128 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "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 TQWidgetStackPrivate { +public: + class Invisible: public TQWidget + { + public: + Invisible( TQWidgetStack * parent ): TQWidget( parent, "qt_invisible_widgetstack" ) + { + setBackgroundMode( NoBackground ); + } + const char * className() const + { + return "TQWidgetStackPrivate::Invisible"; + } + }; +}; + + +#if (QT_VERSION-0 >= 0x040000) +#if defined(Q_CC_GNU) +#warning "Remove TQWidgetStackEventFilter" +#endif +#endif +class TQWidgetStackEventFilter : public TQObject +{ + /* For binary compatibility, since we cannot implement virtual + functions and rely on them being called. This is what we should + have + + bool TQWidgetStack::event( TQEvent* e ) + { + if ( e->type() == TQEvent::LayoutHint ) + updateGeometry(); // propgate layout hints to parent + return TQFrame::event( e ); + } + */ +public: + + TQWidgetStackEventFilter( TQObject *parent = 0, const char * name = 0 ) + : TQObject( parent, name ) {} + bool eventFilter( TQObject *o, TQEvent * e ) { + if ( e->type() == TQEvent::LayoutHint && o->isWidgetType() ) + ((TQWidget*)o)->updateGeometry(); + return FALSE; + } +}; + + +/*! + \class TQWidgetStack + \brief The TQWidgetStack 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. + + TQWidgetStack 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 + TQWidgetStack, not globally), but TQWidgetStack does not attach any + additional meaning to them. + + The default widget stack is frameless, but you can use the usual + TQFrame functions (such as setFrameStyle()) to add a frame. + + TQWidgetStack provides a signal, aboutToShow(), which is emitted + just before a managed widget is shown. + + \sa TQTabDialog TQTabBar TQFrame +*/ + + +/*! + Constructs an empty widget stack. + + The \a parent and \a name arguments are passed to the TQFrame + constructor. +*/ + +TQWidgetStack::TQWidgetStack( TQWidget * parent, const char *name ) + : TQFrame( parent, name ) +{ + init(); +} + +/*! + Constructs an empty widget stack. + + The \a parent, \a name and \a f arguments are passed to the TQFrame + constructor. +*/ +TQWidgetStack::TQWidgetStack( TQWidget * parent, const char *name, WFlags f ) + : TQFrame( parent, name, f ) //## merge constructors in 4.0 +{ + init(); +} + +void TQWidgetStack::init() +{ + d = 0; + TQWidgetStackEventFilter *ef = new TQWidgetStackEventFilter( this ); + installEventFilter( ef ); + dict = new TQIntDict<TQWidget>; + focusWidgets = 0; + topWidget = 0; + invisible = new TQWidgetStackPrivate::Invisible( this ); + invisible->hide(); +} + + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQWidgetStack::~TQWidgetStack() +{ + 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 TQWidgetStack moves it using + reparent(). +*/ + +int TQWidgetStack::addWidget( TQWidget * 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 = TQMAX(pseq_no, id + 1); + // use id >= 0 as-is + + dict->insert( id, w ); + + // preserve existing focus + TQWidget * f = w->focusWidget(); + while( f && f != w ) + f = f->parentWidget(); + if ( f ) { + if ( !focusWidgets ) + focusWidgets = new TQPtrDict<TQWidget>( 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 TQWidgetStack::removeWidget( TQWidget * 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 TQWidgetStack::raiseWidget( int id ) +{ + if ( id == -1 ) + return; + TQWidget * w = dict->find( id ); + if ( w ) + raiseWidget( w ); +} + +static bool isChildOf( TQWidget* child, TQWidget *parent ) +{ + const TQObjectList *list = parent->children(); + if ( !child || !list ) + return FALSE; + TQObjectListIt it(*list); + TQObject *obj; + while ( (obj = it.current()) ) { + ++it; + if ( !obj->isWidgetType() || ((TQWidget *)obj)->isTopLevel() ) + continue; + TQWidget *widget = (TQWidget *)obj; + if ( widget == child || isChildOf( child, widget ) ) + return TRUE; + } + return FALSE; +} + +/*! + \overload + + Raises widget \a w to the top of the widget stack. +*/ + +void TQWidgetStack::raiseWidget( TQWidget *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(); + TQApplication::sendPostedEvents( invisible, TQEvent::ShowWindowRequest ); + } + + // try to move focus onto the incoming widget if focus + // was somewhere on the outgoing widget. + if ( topWidget ) { + TQWidget * fw = focusWidget(); + TQWidget* p = fw; + while ( p && p != topWidget ) + p = p->parentWidget(); + if ( p == topWidget ) { // focus was on old page + if ( !focusWidgets ) + focusWidgets = new TQPtrDict<TQWidget>( 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 + TQFocusData *f = focusData(); + TQWidget* home = f->home(); + TQWidget *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 TQObjectList * c = children(); + TQObjectListIt it( *c ); + TQObject * o; + + while( (o=it.current()) != 0 ) { + ++it; + if ( o->isWidgetType() && o != w && o != invisible ) + ((TQWidget *)o)->hide(); + } + + w->setGeometry( invisible->geometry() ); + w->show(); +} + +/*! + \reimp +*/ + +void TQWidgetStack::frameChanged() +{ + TQFrame::frameChanged(); + setChildGeometries(); +} + + +/*! + \reimp +*/ + +void TQWidgetStack::setFrameRect( const TQRect & r ) +{ + TQFrame::setFrameRect( r ); + setChildGeometries(); +} + + +/*! + Fixes up the children's geometries. +*/ + +void TQWidgetStack::setChildGeometries() +{ + invisible->setGeometry( contentsRect() ); + if ( topWidget ) + topWidget->setGeometry( invisible->geometry() ); +} + + +/*! + \reimp +*/ +void TQWidgetStack::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 TQObjectList * c = children(); + TQObjectListIt it( *c ); + TQObject * o; + + while( (o=it.current()) != 0 ) { + ++it; + if ( o->isWidgetType() ) { + if ( !topWidget && o != invisible ) + topWidget = (TQWidget*)o; + if ( o == topWidget ) + ((TQWidget *)o)->show(); + else + ((TQWidget *)o)->hide(); + } + } + setChildGeometries(); + } + TQFrame::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() +*/ + +TQWidget * TQWidgetStack::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 TQWidgetStack::id( TQWidget * widget ) const +{ + if ( !widget ) + return -1; + + TQIntDictIterator<TQWidget> 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() +*/ + +TQWidget * TQWidgetStack::visibleWidget() const +{ + return topWidget; +} + + +/*! + \fn void TQWidgetStack::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 TQWidgetStack::aboutToShow( TQWidget * ) + + \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 TQWidgetStack::resizeEvent( TQResizeEvent * e ) +{ + TQFrame::resizeEvent( e ); + setChildGeometries(); +} + + +/*! + \reimp +*/ + +TQSize TQWidgetStack::sizeHint() const +{ + constPolish(); + + TQSize size( 0, 0 ); + + TQIntDictIterator<TQWidget> it( *dict ); + TQWidget *w; + + while ( (w = it.current()) != 0 ) { + ++it; + TQSize sh = w->sizeHint(); + if ( w->sizePolicy().horData() == TQSizePolicy::Ignored ) + sh.rwidth() = 0; + if ( w->sizePolicy().verData() == TQSizePolicy::Ignored ) + sh.rheight() = 0; +#ifndef QT_NO_LAYOUT + size = size.expandedTo( sh ).expandedTo( qSmartMinSize(w) ); +#endif + } + if ( size.isNull() ) + size = TQSize( 128, 64 ); + size += TQSize( 2*frameWidth(), 2*frameWidth() ); + return size; +} + + +/*! + \reimp +*/ +TQSize TQWidgetStack::minimumSizeHint() const +{ + constPolish(); + + TQSize size( 0, 0 ); + + TQIntDictIterator<TQWidget> it( *dict ); + TQWidget *w; + + while ( (w = it.current()) != 0 ) { + ++it; + TQSize sh = w->minimumSizeHint(); + if ( w->sizePolicy().horData() == TQSizePolicy::Ignored ) + sh.rwidth() = 0; + if ( w->sizePolicy().verData() == TQSizePolicy::Ignored ) + sh.rheight() = 0; +#ifndef QT_NO_LAYOUT + size = size.expandedTo( sh ).expandedTo( w->minimumSize() ); +#endif + } + if ( size.isNull() ) + size = TQSize( 64, 32 ); + size += TQSize( 2*frameWidth(), 2*frameWidth() ); + return size; +} + +/*! + \reimp +*/ +void TQWidgetStack::childEvent( TQChildEvent * e) +{ + if ( e->child()->isWidgetType() && e->removed() ) + removeWidget( (TQWidget*) e->child() ); +} +#endif diff --git a/src/widgets/qwidgetstack.h b/src/widgets/qwidgetstack.h new file mode 100644 index 000000000..63e3d8a35 --- /dev/null +++ b/src/widgets/qwidgetstack.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Definition of TQWidgetStack class +** +** Created : 980306 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#ifndef TQWIDGETSTACK_H +#define TQWIDGETSTACK_H + +#ifndef QT_H +#include "qframe.h" +#include "qintdict.h" +#include "qptrdict.h" +#endif // QT_H + +#ifndef QT_NO_WIDGETSTACK + + +class TQWidgetStackPrivate; + + +class Q_EXPORT TQWidgetStack: public TQFrame +{ + Q_OBJECT +public: + TQWidgetStack( TQWidget* parent=0, const char* name=0 ); + TQWidgetStack( TQWidget* parent, const char* name, WFlags f); + + ~TQWidgetStack(); + + int addWidget( TQWidget *, int = -1 ); + void removeWidget( TQWidget * ); + + TQSize sizeHint() const; + TQSize minimumSizeHint() const; + void show(); + + TQWidget * widget( int ) const; + int id( TQWidget * ) const; + + TQWidget * visibleWidget() const; + + void setFrameRect( const TQRect & ); + +signals: + void aboutToShow( int ); + void aboutToShow( TQWidget * ); + +public slots: + void raiseWidget( int ); + void raiseWidget( TQWidget * ); + +protected: + void frameChanged(); + void resizeEvent( TQResizeEvent * ); + + virtual void setChildGeometries(); + void childEvent( TQChildEvent * ); + +private: + void init(); + + TQWidgetStackPrivate * d; + TQIntDict<TQWidget> * dict; + TQPtrDict<TQWidget> * focusWidgets; + TQWidget * topWidget; + TQWidget * invisible; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + TQWidgetStack( const TQWidgetStack & ); + TQWidgetStack& operator=( const TQWidgetStack & ); +#endif +}; + +#endif // QT_NO_WIDGETSTACK + +#endif // TQWIDGETSTACK_H |