diff options
Diffstat (limited to 'src/qt3support/widgets')
53 files changed, 28786 insertions, 0 deletions
diff --git a/src/qt3support/widgets/q3action.cpp b/src/qt3support/widgets/q3action.cpp new file mode 100644 index 0000000..4e1a1bf --- /dev/null +++ b/src/qt3support/widgets/q3action.cpp @@ -0,0 +1,2236 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3action.h" + +#ifndef QT_NO_ACTION + +#include "qevent.h" +#include "q3toolbar.h" +#include "qlist.h" +#include "q3popupmenu.h" +#include "q3accel.h" +#include "qtoolbutton.h" +#include "qcombobox.h" +#include "qtooltip.h" +#include "qwhatsthis.h" +#include "qstatusbar.h" +#include "qaction.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Q3Action + \brief The Q3Action class provides an abstract user interface + action that can appear both in menus and tool bars. + + \compat + + 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 Q3Action may contain an icon, a menu text, an accelerator, a + status text, a "What's 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(). Note that when using Q3ToolBar and + Q3PopupMenu, their actions must be Q3Actions. + + Once a Q3Action has been created it should be added to the relevant + menu and toolbar and then connected to the slot which will perform + the action. + + 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 Q3ActionPrivate +{ +public: + Q3ActionPrivate(Q3Action *act); + ~Q3ActionPrivate(); + QIcon *icon; + QString text; + QString menutext; + QString tooltip; + QString statustip; + QString whatsthis; +#ifndef QT_NO_ACCEL + QKeySequence key; + Q3Accel* accel; + int accelid; +#endif + uint enabled : 1; + uint visible : 1; + uint toggleaction : 1; + uint on : 1; + uint forceDisabled : 1; + uint forceInvisible : 1; + Q3ActionGroupPrivate* d_group; + Q3Action *action; + + struct MenuItem { + MenuItem():popup(0),id(0){} + Q3PopupMenu* popup; + int id; + }; + // ComboItem is only necessary for actions that are + // in dropdown/exclusive actiongroups. The actiongroup + // will clean this up + struct ComboItem { + ComboItem():combo(0), id(0) {} + QComboBox *combo; + int id; + }; + //just bindings to the Qt4.0 widgets + struct Action4Item { + Action4Item():widget(0){} + QWidget* widget; + static QAction *action; + }; + QList<Action4Item *> action4items; + QList<MenuItem *> menuitems; + QList<QToolButton *> toolbuttons; + QList<ComboItem *> comboitems; + + enum Update { Icons = 1, Visibility = 2, State = 4, EverythingElse = 8 }; + void update(uint upd = EverythingElse); + + QString menuText() const; + QString toolTip() const; + QString statusTip() const; +}; +QAction *Q3ActionPrivate::Action4Item::action = 0; + +Q3ActionPrivate::Q3ActionPrivate(Q3Action *act) + : icon(0), +#ifndef QT_NO_ACCEL + key(0), accel(0), accelid(0), +#endif + enabled(true), visible(true), toggleaction(false), on(false), + forceDisabled(false), forceInvisible(false) + , d_group(0), action(act) +{ +} + +Q3ActionPrivate::~Q3ActionPrivate() +{ + QList<QToolButton*>::Iterator ittb(toolbuttons.begin()); + QToolButton *tb; + + while (ittb != toolbuttons.end()) { + tb = *ittb; + ++ittb; + delete tb; + } + + QList<Q3ActionPrivate::MenuItem*>::Iterator itmi(menuitems.begin()); + Q3ActionPrivate::MenuItem* mi; + while (itmi != menuitems.end()) { + mi = *itmi; + ++itmi; + Q3PopupMenu* menu = mi->popup; + if (menu->findItem(mi->id)) + menu->removeItem(mi->id); + } + qDeleteAll(menuitems); + + QList<Q3ActionPrivate::Action4Item*>::Iterator itmi4(action4items.begin()); + Q3ActionPrivate::Action4Item* mi4; + while (itmi4 != action4items.end()) { + mi4 = *itmi4; + ++itmi4; + mi4->widget->removeAction(mi4->action); + } + delete Q3ActionPrivate::Action4Item::action; + Q3ActionPrivate::Action4Item::action = 0; + qDeleteAll(action4items); + + QList<Q3ActionPrivate::ComboItem*>::Iterator itci(comboitems.begin()); + Q3ActionPrivate::ComboItem* ci; + while (itci != comboitems.end()) { + ci = *itci; + ++itci; + QComboBox* combo = ci->combo; + combo->clear(); + Q3ActionGroup *group = qobject_cast<Q3ActionGroup*>(action->parent()); + if (group) { + QObjectList siblings = group->queryList("Q3Action"); + + for (int i = 0; i < siblings.size(); ++i) { + Q3Action *sib = qobject_cast<Q3Action*>(siblings.at(i)); + sib->removeFrom(combo); + } + for (int i = 0; i < siblings.size(); ++i) { + Q3Action *sib = qobject_cast<Q3Action*>(siblings.at(i)); + if (sib == action) + continue; + sib->addTo(combo); + } + } + } + qDeleteAll(comboitems); + +#ifndef QT_NO_ACCEL + delete accel; +#endif + delete icon; +} + +class Q3ActionGroupPrivate +{ +public: + uint exclusive: 1; + uint dropdown: 1; + QList<Q3Action*> actions; + Q3Action* selected; + Q3Action* separatorAction; + + struct MenuItem { + MenuItem():popup(0),id(0){} + Q3PopupMenu* popup; + int id; + }; + struct Action4Item { + Action4Item():widget(0){} + QWidget* widget; + static QAction *action; + }; + QList<Action4Item *> action4items; + QList<QComboBox*> comboboxes; + QList<QToolButton*> menubuttons; + QList<MenuItem*> menuitems; + QList<Q3PopupMenu*> popupmenus; + + void update(const Q3ActionGroup *); +}; +QAction *Q3ActionGroupPrivate::Action4Item::action = 0; + +void Q3ActionPrivate::update(uint upd) +{ + for (QList<MenuItem*>::Iterator it(menuitems.begin()); it != menuitems.end(); ++it) { + MenuItem* mi = *it; + QString t = menuText(); +#ifndef QT_NO_ACCEL + if (key) + t += QLatin1Char('\t') + (QString)QKeySequence(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 (icon) + mi->popup->changeItem(mi->id, *icon, t); + else + mi->popup->changeItem(mi->id, QIcon(), 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); + } + } + } + if(QAction *act = Action4Item::action) { + if (upd & Visibility) + act->setVisible(visible); + if (upd & Icons) { + if (icon) + act->setIcon(*icon); + else + act->setIcon(QIcon()); + } + if (upd & EverythingElse) { + QString text = action->menuText(); +#ifndef QT_NO_ACCEL + if (key) + text += QLatin1Char('\t') + (QString)QKeySequence(key); +#endif + act->setText(text); + act->setToolTip(statusTip()); + act->setWhatsThis(whatsthis); + } + } + for (QList<QToolButton*>::Iterator it2(toolbuttons.begin()); it2 != toolbuttons.end(); ++it2) { + QToolButton* btn = *it2; + if (upd & State) { + btn->setEnabled(enabled); + if (toggleaction) + btn->setOn(on); + } + if (upd & Visibility) + visible ? btn->show() : btn->hide(); + if (upd & Icons) { + if (icon) + btn->setIconSet(*icon); + else + btn->setIconSet(QIcon()); + } + if (upd & EverythingElse) { + btn->setToggleButton(toggleaction); + if (!text.isEmpty()) + btn->setTextLabel(text, false); +#ifndef QT_NO_TOOLTIP + btn->setToolTip(toolTip()); +#endif +#ifndef QT_NO_STATUSTIP + btn->setStatusTip(statusTip()); +#endif +#ifndef QT_NO_WHATSTHIS + QWhatsThis::remove(btn); + if (!whatsthis.isEmpty()) + QWhatsThis::add(btn, whatsthis); +#endif + } + } +#ifndef QT_NO_ACCEL + if (accel) { + accel->setEnabled(enabled && visible); + if (!whatsthis.isEmpty()) + accel->setWhatsThis(accelid, whatsthis); + } +#endif + // Only used by actiongroup + for (QList<ComboItem*>::Iterator it3(comboitems.begin()); it3 != comboitems.end(); ++it3) { + ComboItem *ci = *it3; + if (!ci->combo) + return; + if (ci->id == -1) { + ci->id = ci->combo->count(); + if (icon) + ci->combo->insertItem(icon->pixmap(), text); + else + ci->combo->insertItem(text); + } else { + if (icon) + ci->combo->changeItem(icon->pixmap(), text, ci->id); + else + ci->combo->changeItem(text, ci->id); + } + } +} + +QString Q3ActionPrivate::menuText() const +{ + if (menutext.isNull()) { + QString t(text); + t.replace(QLatin1Char('&'), QLatin1String("&&")); + return t; + } + return menutext; +} + +QString Q3ActionPrivate::toolTip() const +{ + if (tooltip.isNull()) { +#ifndef QT_NO_ACCEL + if (accel) + return text + QLatin1String(" (") + (QString)QKeySequence(accel->key(accelid)) + QLatin1Char(')'); +#endif + return text; + } + return tooltip; +} + +QString Q3ActionPrivate::statusTip() const +{ + if (statustip.isNull()) + return toolTip(); + return statustip; +} + +/* + internal: guesses a descriptive text from a menu text + */ +static QString qt_stripMenuText(QString s) +{ + s.remove(QLatin1String("...")); + s.remove(QLatin1Char('&')); + return s.trimmed(); +}; + +/*! + Constructs an action called \a name with parent \a parent. + + If \a parent is a Q3ActionGroup, 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. +*/ +Q3Action::Q3Action(QObject* parent, const char* name) + : QObject(parent, name) +{ + d = new Q3ActionPrivate(this); + init(); +} + +/*! + 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 Q3ActionGroup, 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. +*/ +Q3Action::Q3Action(QObject* parent, const char* name, bool toggle) + : QObject(parent, name) +{ + d = new Q3ActionPrivate(this); + d->toggleaction = toggle; + init(); +} + + +#ifndef QT_NO_ACCEL + +/*! + This constructor creates an action with the following properties: + the icon or icon \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 Q3ActionGroup, 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 + tool buttons. 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. +*/ +Q3Action::Q3Action(const QIcon& icon, const QString& menuText, QKeySequence accel, + QObject* parent, const char* name) + : QObject(parent, name) +{ + d = new Q3ActionPrivate(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 Q3ActionGroup, 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 + tool buttons. 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. +*/ +Q3Action::Q3Action(const QString& menuText, QKeySequence accel, + QObject* parent, const char* name) + : QObject(parent, name) +{ + d = new Q3ActionPrivate(this); + d->text = qt_stripMenuText(menuText); + d->menutext = menuText; + setAccel(accel); + init(); +} + +/*! + This constructor creates an action with the following properties: + the description \a text, the icon or icon \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 Q3ActionGroup, 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(). +*/ +Q3Action::Q3Action(const QString& text, const QIcon& icon, const QString& menuText, QKeySequence accel, QObject* parent, const char* name, bool toggle) + : QObject(parent, name) +{ + d = new Q3ActionPrivate(this); + d->toggleaction = toggle; + if (!icon.isNull()) + setIconSet(icon); + + d->text = text; + d->menutext = menuText; + setAccel(accel); + init(); +} + +/*! + 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 Q3ActionGroup. + + 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(). +*/ +Q3Action::Q3Action(const QString& text, const QString& menuText, QKeySequence accel, QObject* parent, const char* name, bool toggle) + : QObject(parent, name) +{ + d = new Q3ActionPrivate(this); + d->toggleaction = toggle; + d->text = text; + d->menutext = menuText; + setAccel(accel); + init(); +} +#endif + +/*! + \internal +*/ +void Q3Action::init() +{ + if (qobject_cast<Q3ActionGroup*>(parent())) + ((Q3ActionGroup*) parent())->add(this); // insert into action group +} + +/*! + Destroys the object and frees allocated resources. +*/ + +Q3Action::~Q3Action() +{ + delete d; +} + +/*! + \property Q3Action::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 (QIcon::isNull() is passed into this function, + the icon of the action is cleared. + + (See the action/toggleaction/toggleaction.cpp example.) + +*/ +void Q3Action::setIconSet(const QIcon& icon) +{ + register QIcon *i = d->icon; + if (!icon.isNull()) + d->icon = new QIcon(icon); + else + d->icon = 0; + delete i; + d->update(Q3ActionPrivate::Icons); +} + +QIcon Q3Action::iconSet() const +{ + if (d->icon) + return *d->icon; + return QIcon(); +} + +/*! + \property Q3Action::text + \brief the action's descriptive text + + \sa setMenuText() setToolTip() setStatusTip() +*/ +void Q3Action::setText(const QString& text) +{ + d->text = text; + d->update(); +} + +QString Q3Action::text() const +{ + return d->text; +} + + +/*! + \property Q3Action::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 Q3Action::setMenuText(const QString& text) +{ + if (d->menutext == text) + return; + + d->menutext = text; + d->update(); +} + +QString Q3Action::menuText() const +{ + return d->menuText(); +} + +/*! + \property Q3Action::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 Q3Action::setToolTip(const QString& tip) +{ + if (d->tooltip == tip) + return; + + d->tooltip = tip; + d->update(); +} + +QString Q3Action::toolTip() const +{ + return d->toolTip(); +} + +/*! + \property Q3Action::statusTip + \brief the action's status tip + + The statusTip is displayed on all status bars that this action's + top-level parent widget provides. + + If no status tip is defined, the action uses the tool tip text. + + There is no default statusTip text. + + \sa setToolTip() +*/ +void Q3Action::setStatusTip(const QString& tip) +{ + // Old comment: ### Please reimp for Q3ActionGroup! + // For consistency reasons even action groups should show + // status tips (as they already do with tool tips) + // Please change Q3ActionGroup class doc appropriately after + // reimplementation. + + if (d->statustip == tip) + return; + + d->statustip = tip; + d->update(); +} + +QString Q3Action::statusTip() const +{ + return d->statusTip(); +} + +/*! + \property Q3Action::whatsThis + \brief the action's "What's This?" help text + + The whats this text is used to provide a brief description of the + action. The text may contain rich text (HTML-like tags -- see + QStyleSheet for the list of supported tags). There is no default + "What's This?" text. + + \sa QWhatsThis +*/ +void Q3Action::setWhatsThis(const QString& whatsThis) +{ + if (d->whatsthis == whatsThis) + return; + d->whatsthis = whatsThis; + d->update(); +} + +QString Q3Action::whatsThis() const +{ + return d->whatsthis; +} + + +#ifndef QT_NO_ACCEL +/*! + \property Q3Action::accel + \brief the action's accelerator key + + The keycodes can be found in \l Qt::Key and \l Qt::Modifier. There + is no default accelerator key. +*/ +//#### Please reimp for Q3ActionGroup! +//#### For consistency reasons even Q3ActionGroups should respond to +//#### their accelerators and e.g. open the relevant submenu. +//#### Please change appropriate Q3ActionGroup class doc after +//#### reimplementation. +void Q3Action::setAccel(const QKeySequence& key) +{ + if (d->key == key) + return; + + d->key = key; + delete d->accel; + d->accel = 0; + + if (!(int)key) { + d->update(); + return; + } + + QObject* p = parent(); + while (p && !p->isWidgetType()) { + p = p->parent(); + } + if (p) { + d->accel = new Q3Accel((QWidget*)p, this, "qt_action_accel"); + d->accelid = d->accel->insertItem(d->key); + d->accel->connectItem(d->accelid, this, SLOT(internalActivation())); + } else + qWarning("Q3Action::setAccel() (%s) requires widget in parent chain", objectName().toLocal8Bit().data()); + d->update(); +} + + +QKeySequence Q3Action::accel() const +{ + return d->key; +} +#endif + + +/*! + \property Q3Action::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 + Q3ActionGroup with the \l Q3ActionGroup::exclusive property set to + true. +*/ +void Q3Action::setToggleAction(bool enable) +{ + if (enable == (bool)d->toggleaction) + return; + + if (!enable) + d->on = false; + + d->toggleaction = enable; + d->update(); +} + +bool Q3Action::isToggleAction() const +{ + return d->toggleaction; +} + +/*! + Activates the action and executes all connected slots. + This only works for actions that are not toggle actions. + + \sa toggle() +*/ +void Q3Action::activate() +{ + if (isToggleAction()) { +#if defined(QT_CHECK_STATE) + qWarning("Q3Action::%s() (%s) Toggle actions " + "can not be activated", "activate", objectName().toLocal8Bit().data()); +#endif + return; + } + emit activated(); +} + +/*! + Toggles the state of a toggle action. + + \sa on, activate(), toggled(), isToggleAction() +*/ +void Q3Action::toggle() +{ + if (!isToggleAction()) { + qWarning("Q3Action::%s() (%s) Only toggle actions " + "can be switched", "toggle", objectName().toLocal8Bit().data()); + return; + } + setOn(!isOn()); +} + +/*! + \property Q3Action::on + \brief whether a toggle action is on + + This property is always on (true) for command actions and + \l{Q3ActionGroup}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 Q3Action::setOn(bool enable) +{ + if (!isToggleAction()) { + if (enable) + qWarning("Q3Action::%s() (%s) Only toggle actions " + "can be switched", "setOn", objectName().toLocal8Bit().data()); + return; + } + if (enable == (bool)d->on) + return; + d->on = enable; + d->update(Q3ActionPrivate::State); + emit toggled(enable); +} + +bool Q3Action::isOn() const +{ + return d->on; +} + +/*! + \property Q3Action::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 Q3Action::whatsThis property is set. +*/ +void Q3Action::setEnabled(bool enable) +{ + d->forceDisabled = !enable; + + if ((bool)d->enabled == enable) + return; + + d->enabled = enable; + d->update(Q3ActionPrivate::State); +} + +bool Q3Action::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 Q3Action::setDisabled(bool disable) +{ + setEnabled(!disable); +} + +/*! + \property Q3Action::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 Q3Action::setVisible(bool visible) +{ + d->forceInvisible = !visible; + + if ((bool)d->visible == visible) + return; + + d->visible = visible; + d->update(Q3ActionPrivate::Visibility); +} + +/* + Returns true if the action is visible (e.g. in menus and + toolbars); otherwise returns false. +*/ +bool Q3Action::isVisible() const +{ + return d->visible; +} + +/*! \internal +*/ +void Q3Action::internalActivation() +{ + if (isToggleAction()) + setOn(!isOn()); + emit activated(); +} + +/*! \internal +*/ +void Q3Action::toolButtonToggled(bool on) +{ + if (!isToggleAction()) + return; + setOn(on); +} + +/*! + Adds this action to widget \a w. + + Currently actions may be added to Q3ToolBar and Q3PopupMenu 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 Q3ToolBar or Q3PopupMenu the + action will not be added and false will be returned.) + + \sa removeFrom() +*/ +bool Q3Action::addTo(QWidget* w) +{ +#ifndef QT_NO_TOOLBAR + if (qobject_cast<Q3ToolBar*>(w)) { + if (objectName() == QLatin1String("qt_separator_action")) { + ((Q3ToolBar*)w)->addSeparator(); + } else { + QString bname = objectName() + QLatin1String("_action_button"); + QToolButton* btn = new QToolButton((Q3ToolBar*) w); + btn->setObjectName(bname); + addedTo(btn, w); + btn->setToggleButton(d->toggleaction); + d->toolbuttons.append(btn); + if (d->icon) + btn->setIconSet(*d->icon); + d->update(Q3ActionPrivate::State | Q3ActionPrivate::Visibility | Q3ActionPrivate::EverythingElse) ; + connect(btn, SIGNAL(clicked()), this, SIGNAL(activated())); + connect(btn, SIGNAL(toggled(bool)), this, SLOT(toolButtonToggled(bool))); + connect(btn, SIGNAL(destroyed()), this, SLOT(objectDestroyed())); + } + } else +#endif + if (qobject_cast<Q3PopupMenu*>(w)) { + Q3ActionPrivate::MenuItem* mi = new Q3ActionPrivate::MenuItem; + mi->popup = (Q3PopupMenu*) w; + QIcon* dicon = d->icon; + if (objectName() == QLatin1String("qt_separator_action")) + mi->id = ((Q3PopupMenu*)w)->insertSeparator(); + else if (dicon) + mi->id = mi->popup->insertItem(*dicon, QString::fromLatin1("")); + else + mi->id = mi->popup->insertItem(QString::fromLatin1("")); + addedTo(mi->popup->indexOf(mi->id), mi->popup); + mi->popup->connectItem(mi->id, this, SLOT(internalActivation())); + d->menuitems.append(mi); + d->update(Q3ActionPrivate::State | Q3ActionPrivate::Visibility | Q3ActionPrivate::EverythingElse); + 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 Q3ActionGroup::addTo + } else if (qobject_cast<QComboBox*>(w)) { + Q3ActionPrivate::ComboItem *ci = new Q3ActionPrivate::ComboItem; + ci->combo = (QComboBox*)w; + connect(ci->combo, SIGNAL(destroyed()), this, SLOT(objectDestroyed())); + ci->id = ci->combo->count(); + if (objectName() == QLatin1String("qt_separator_action")) { + if (d->icon) + ci->combo->insertItem(d->icon->pixmap(), text()); + else + ci->combo->insertItem(text()); + } else { + ci->id = -1; + } + d->comboitems.append(ci); + d->update(Q3ActionPrivate::State | Q3ActionPrivate::EverythingElse); + } else if(qobject_cast<QMenu*>(w)) { + Q3ActionPrivate::Action4Item *act = new Q3ActionPrivate::Action4Item; + if(!act->action) { //static + act->action = new QAction(this); + if (objectName() == QLatin1String("qt_separator_action")) + act->action->setSeparator(true); + } + act->widget = w; + act->widget->addAction(act->action); + d->action4items.append(act); + d->update(Q3ActionPrivate::State | Q3ActionPrivate::EverythingElse); + } else { + qWarning("Q3Action::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 Q3Action::addedTo(QWidget *actionWidget, QWidget *container) +{ + Q_UNUSED(actionWidget); + Q_UNUSED(container); +} + +/*! + \overload + + This function is called from the addTo() function when it has + created a menu item at the index position \a index in the popup + menu \a menu. +*/ + +void Q3Action::addedTo(int index, Q3PopupMenu *menu) +{ + Q_UNUSED(index); + Q_UNUSED(menu); +} + +/*! + Sets the status message to \a text +*/ +void Q3Action::showStatusText(const QString& text) +{ +#ifndef QT_NO_STATUSBAR + // find out whether we are clearing the status bar by the popup that actually set the text + static Q3PopupMenu *lastmenu = 0; + QObject *s = (QObject*)sender(); + if (s) { + Q3PopupMenu *menu = qobject_cast<Q3PopupMenu*>(s); + if (menu && text.size()) + lastmenu = menu; + else if (menu && text.isEmpty()) { + if (lastmenu && menu != lastmenu) + return; + lastmenu = 0; + } + } + + QObject* par = parent(); + QObject* lpar = 0; + QStatusBar *bar = 0; + while (par && !bar) { + lpar = par; + bar = (QStatusBar*)par->child(0, "QStatusBar", false); + par = par->parent(); + } + if (!bar && lpar) { + QObjectList l = lpar->queryList("QStatusBar"); + if (l.isEmpty()) + return; + // #### hopefully the last one is the one of the mainwindow... + bar = static_cast<QStatusBar*>(l.at(l.size()-1)); + } + if (bar) { + if (text.isEmpty()) + bar->clearMessage(); + else + bar->showMessage(text); + } +#endif +} + +/*! + Sets the status message to the menu item's status text, or to the + tooltip, if there is no status text. +*/ +void Q3Action::menuStatusText(int id) +{ + static int lastId = 0; + QString text; + QList<Q3ActionPrivate::MenuItem*>::Iterator it(d->menuitems.begin()); + while (it != d->menuitems.end()) { + if ((*it)->id == id) { + text = statusTip(); + break; + } + ++it; + } + + if (!text.isEmpty()) + showStatusText(text); + else if (id != lastId) + clearStatusText(); + lastId = id; +} + +/*! + Clears the status text. +*/ +void Q3Action::clearStatusText() +{ + if (!statusTip().isEmpty()) + showStatusText(QString()); +} + +/*! + Removes the action from widget \a w. + + Returns true if the action was removed successfully; otherwise + returns false. + + \sa addTo() +*/ +bool Q3Action::removeFrom(QWidget* w) +{ +#ifndef QT_NO_TOOLBAR + if (qobject_cast<Q3ToolBar*>(w)) { + QList<QToolButton*>::Iterator it(d->toolbuttons.begin()); + QToolButton* btn; + while (it != d->toolbuttons.end()) { + btn = *it; + ++it; + if (btn->parentWidget() == w) { + d->toolbuttons.removeAll(btn); + disconnect(btn, SIGNAL(destroyed()), this, SLOT(objectDestroyed())); + delete btn; + // no need to disconnect from status bar + } + } + } else +#endif + if (qobject_cast<Q3PopupMenu*>(w)) { + QList<Q3ActionPrivate::MenuItem*>::Iterator it(d->menuitems.begin()); + Q3ActionPrivate::MenuItem* mi; + while (it != d->menuitems.end()) { + mi = *it; + ++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.removeAll(mi); + delete mi; + } + } + } else if (qobject_cast<QComboBox*>(w)) { + QList<Q3ActionPrivate::ComboItem*>::Iterator it(d->comboitems.begin()); + Q3ActionPrivate::ComboItem *ci; + while (it != d->comboitems.end()) { + ci = *it; + ++it; + if (ci->combo == w) { + disconnect(ci->combo, SIGNAL(destroyed()), this, SLOT(objectDestroyed())); + d->comboitems.removeAll(ci); + delete ci; + } + } + } else if (qobject_cast<QMenu*>(w)) { + QList<Q3ActionPrivate::Action4Item*>::Iterator it(d->action4items.begin()); + Q3ActionPrivate::Action4Item *a4i; + while (it != d->action4items.end()) { + a4i = *it; + ++it; + if (a4i->widget == w) { + a4i->widget->removeAction(a4i->action); + d->action4items.removeAll(a4i); + delete a4i; + } + } + } else { + qWarning("Q3Action::removeFrom(), unknown object"); + return false; + } + return true; +} + +/*! + \internal +*/ +void Q3Action::objectDestroyed() +{ + const QObject* obj = sender(); + Q3ActionPrivate::MenuItem* mi; + for (int i = 0; i < d->menuitems.size();) { + mi = d->menuitems.at(i); + ++i; + if (mi->popup == obj) { + d->menuitems.removeAll(mi); + delete mi; + } + } + Q3ActionPrivate::ComboItem *ci; + QList<Q3ActionPrivate::ComboItem*>::Iterator it2(d->comboitems.begin()); + while (it2 != d->comboitems.end()) { + ci = *it2; + ++it2; + if (ci->combo == obj) { + d->comboitems.removeAll(ci); + delete ci; + } + } + d->toolbuttons.removeAll((QToolButton *)obj); +} + +/*! + \fn void Q3Action::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 Q3Action::toggled(bool on) + + This signal is emitted when a toggle action changes state; command + actions and \l{Q3ActionGroup}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. + + \sa activated() setToggleAction() setOn() +*/ + +void Q3ActionGroupPrivate::update(const Q3ActionGroup* that) +{ + for (QList<Q3Action*>::Iterator it(actions.begin()); it != actions.end(); ++it) { + if (that->isEnabled() && !(*it)->d->forceDisabled) + (*it)->setEnabled(true); + else if (!that->isEnabled() && (*it)->isEnabled()) { + (*it)->setEnabled(false); + (*it)->d->forceDisabled = false; + } + if (that->isVisible() && !(*it)->d->forceInvisible) { + (*it)->setVisible(true); + } else if (!that->isVisible() && (*it)->isVisible()) { + (*it)->setVisible(false); + (*it)->d->forceInvisible = false; + } + } + for (QList<QComboBox*>::Iterator cb(comboboxes.begin()); cb != comboboxes.end(); ++cb) { + QComboBox *combobox = *cb; + combobox->setEnabled(that->isEnabled()); + combobox->setShown(that->isVisible()); + +#ifndef QT_NO_TOOLTIP + QToolTip::remove(combobox); + if (that->toolTip().size()) + QToolTip::add(combobox, that->toolTip()); +#endif +#ifndef QT_NO_WHATSTHIS + QWhatsThis::remove(combobox); + if (that->whatsThis().size()) + QWhatsThis::add(combobox, that->whatsThis()); +#endif + + } + for (QList<QToolButton*>::Iterator mb(menubuttons.begin()); mb != menubuttons.end(); ++mb) { + QToolButton *button = *mb; + button->setEnabled(that->isEnabled()); + button->setShown(that->isVisible()); + + if (!that->text().isNull()) + button->setTextLabel(that->text()); + if (!that->iconSet().isNull()) + button->setIconSet(that->iconSet()); + +#ifndef QT_NO_TOOLTIP + QToolTip::remove(*mb); + if (that->toolTip().size()) + QToolTip::add(button, that->toolTip()); +#endif +#ifndef QT_NO_WHATSTHIS + QWhatsThis::remove(button); + if (that->whatsThis().size()) + QWhatsThis::add(button, that->whatsThis()); +#endif + } + if(QAction *act = Q3ActionGroupPrivate::Action4Item::action) { + act->setVisible(that->isVisible()); + act->setEnabled(that->isEnabled()); + } + for (QList<Q3ActionGroupPrivate::MenuItem*>::Iterator pu(menuitems.begin()); pu != menuitems.end(); ++pu) { + QWidget* parent = (*pu)->popup->parentWidget(); + if (qobject_cast<Q3PopupMenu*>(parent)) { + Q3PopupMenu* ppopup = (Q3PopupMenu*)parent; + ppopup->setItemEnabled((*pu)->id, that->isEnabled()); + ppopup->setItemVisible((*pu)->id, that->isVisible()); + } else { + (*pu)->popup->setEnabled(that->isEnabled()); + } + } + for (QList<Q3PopupMenu*>::Iterator pm(popupmenus.begin()); pm != popupmenus.end(); ++pm) { + Q3PopupMenu *popup = *pm; + Q3PopupMenu *parent = qobject_cast<Q3PopupMenu*>(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 Q3ActionGroup + \brief The Q3ActionGroup class groups actions together. + + \compat + + 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. + + 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. +*/ +Q3ActionGroup::Q3ActionGroup(QObject* parent, const char* name) + : Q3Action(parent, name) +{ + d = new Q3ActionGroupPrivate; + d->exclusive = true; + d->dropdown = false; + d->selected = 0; + d->separatorAction = 0; + + connect(this, SIGNAL(selected(Q3Action*)), SLOT(internalToggle(Q3Action*))); +} + +/*! + 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 +*/ +Q3ActionGroup::Q3ActionGroup(QObject* parent, const char* name, bool exclusive) + : Q3Action(parent, name) +{ + d = new Q3ActionGroupPrivate; + d->exclusive = exclusive; + d->dropdown = false; + d->selected = 0; + d->separatorAction = 0; + + connect(this, SIGNAL(selected(Q3Action*)), SLOT(internalToggle(Q3Action*))); +} + +/*! + Destroys the object and frees allocated resources. +*/ + +Q3ActionGroup::~Q3ActionGroup() +{ + QList<Q3ActionGroupPrivate::MenuItem*>::Iterator mit(d->menuitems.begin()); + while (mit != d->menuitems.end()) { + Q3ActionGroupPrivate::MenuItem *mi = *mit; + ++mit; + if (mi->popup) + mi->popup->disconnect(SIGNAL(destroyed()), this, SLOT(objectDestroyed())); + } + + QList<QComboBox*>::Iterator cbit(d->comboboxes.begin()); + while (cbit != d->comboboxes.end()) { + QComboBox *cb = *cbit; + ++cbit; + cb->disconnect(SIGNAL(destroyed()), this, SLOT(objectDestroyed())); + } + QList<QToolButton*>::Iterator mbit(d->menubuttons.begin()); + while (mbit != d->menubuttons.end()) { + QToolButton *mb = *mbit; + ++mbit; + mb->disconnect(SIGNAL(destroyed()), this, SLOT(objectDestroyed())); + } + QList<Q3PopupMenu*>::Iterator pmit(d->popupmenus.begin()); + while (pmit != d->popupmenus.end()) { + Q3PopupMenu *pm = *pmit; + ++pmit; + pm->disconnect(SIGNAL(destroyed()), this, SLOT(objectDestroyed())); + } + + QList<Q3ActionGroupPrivate::Action4Item*>::Iterator itmi4(d->action4items.begin()); + Q3ActionGroupPrivate::Action4Item* mi4; + while (itmi4 != d->action4items.end()) { + mi4 = *itmi4; + ++itmi4; + mi4->widget->removeAction(mi4->action); + } + delete Q3ActionPrivate::Action4Item::action; + Q3ActionPrivate::Action4Item::action = 0; + + delete d->separatorAction; + while (!d->menubuttons.isEmpty()) + delete d->menubuttons.takeFirst(); + while (!d->comboboxes.isEmpty()) + delete d->comboboxes.takeFirst(); + while (!d->menuitems.isEmpty()) + delete d->menuitems.takeFirst(); + while (!d->popupmenus.isEmpty()) + delete d->popupmenus.takeFirst(); + delete d; +} + +/*! + \property Q3ActionGroup::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 Q3Action::toggleAction +*/ +void Q3ActionGroup::setExclusive(bool enable) +{ + d->exclusive = enable; +} + +bool Q3ActionGroup::isExclusive() const +{ + return d->exclusive; +} + +/*! + \property Q3ActionGroup::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 Q3Action::text and \l + Q3Action::iconSet properties shown. Non-exclusive groups are + represented by a tool button showing their \l Q3Action::iconSet and + text() property. + + In a popup menu the member actions are displayed in a submenu. + + Changing usesDropDown only affects \e subsequent calls to addTo(). + + This property's default is false. + +*/ +void Q3ActionGroup::setUsesDropDown(bool enable) +{ + d->dropdown = enable; +} + +bool Q3ActionGroup::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 Q3ActionGroup::add(Q3Action* action) +{ + if (d->actions.contains(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))); + connect(action, SIGNAL(activated()), this, SLOT(childActivated())); + + for (QList<QComboBox*>::Iterator cb(d->comboboxes.begin()); cb != d->comboboxes.end(); ++cb) + action->addTo(*cb); + for (QList<QToolButton*>::Iterator mb(d->menubuttons.begin()); mb != d->menubuttons.end(); ++mb) { + QMenu* menu = (*mb)->popup(); + if (!menu) + continue; + action->addTo(menu); + } + for (QList<Q3ActionGroupPrivate::Action4Item*>::Iterator ac(d->action4items.begin()); + ac != d->action4items.end(); ++ac) + action->addTo((*ac)->action->menu()); + for (QList<Q3ActionGroupPrivate::MenuItem*>::Iterator mi(d->menuitems.begin()); + mi != d->menuitems.end(); ++mi) { + Q3PopupMenu* popup = (*mi)->popup; + if (!popup) + continue; + action->addTo(popup); + } +} + +/*! + Adds a separator to the group. +*/ +void Q3ActionGroup::addSeparator() +{ + if (!d->separatorAction) + d->separatorAction = new Q3Action(0, "qt_separator_action"); + d->actions.append(d->separatorAction); +} + + +/*! + 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 Q3ActionGroup::addTo(QWidget *w) +{ +#ifndef QT_NO_TOOLBAR + if (qobject_cast<Q3ToolBar*>(w)) { + if (d->dropdown) { + if (!d->exclusive) { + QList<Q3Action*>::Iterator it(d->actions.begin()); + if (it == d->actions.end() || !(*it)) + return true; + + Q3Action *defAction = *it; + + QToolButton* btn = new QToolButton((Q3ToolBar*) 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().size()) + btn->setTextLabel(text()); + else if (defAction->text().size()) + btn->setTextLabel(defAction->text()); +#ifndef QT_NO_TOOLTIP + if (toolTip().size()) + QToolTip::add(btn, toolTip()); + else if (defAction->toolTip().size()) + QToolTip::add(btn, defAction->toolTip()); +#endif +#ifndef QT_NO_WHATSTHIS + if (whatsThis().size()) + QWhatsThis::add(btn, whatsThis()); + else if (defAction->whatsThis().size()) + QWhatsThis::add(btn, defAction->whatsThis()); +#endif + + connect(btn, SIGNAL(clicked()), defAction, SIGNAL(activated())); + connect(btn, SIGNAL(toggled(bool)), defAction, SLOT(toolButtonToggled(bool))); + connect(btn, SIGNAL(destroyed()), defAction, SLOT(objectDestroyed())); + + Q3PopupMenu *menu = new Q3PopupMenu(btn, "qt_actiongroup_menu"); + btn->setPopupDelay(0); + btn->setPopup(menu); + btn->setPopupMode(QToolButton::MenuButtonPopup); + + while (it != d->actions.end()) { + (*it)->addTo(menu); + ++it; + } + d->update(this); + return true; + } else { + QComboBox *box = new QComboBox(false, w, "qt_actiongroup_combo"); + addedTo(box, w); + connect(box, SIGNAL(destroyed()), SLOT(objectDestroyed())); + d->comboboxes.append(box); +#ifndef QT_NO_TOOLTIP + if (toolTip().size()) + QToolTip::add(box, toolTip()); +#endif +#ifndef QT_NO_WHATSTHIS + if (whatsThis().size()) + QWhatsThis::add(box, whatsThis()); +#endif + int onIndex = 0; + bool foundOn = false; + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) { + Q3Action *action = *it; + if (!foundOn) + foundOn = action->isOn(); + if (action->objectName() != QLatin1String("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 (qobject_cast<Q3PopupMenu*>(w)) { + Q3PopupMenu *popup; + if (d->dropdown) { + Q3PopupMenu *menu = (Q3PopupMenu*)w; + popup = new Q3PopupMenu(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); + + Q3ActionGroupPrivate::MenuItem *item = new Q3ActionGroupPrivate::MenuItem; + item->id = id; + item->popup = popup; + d->menuitems.append(item); + } else { + popup = (Q3PopupMenu*)w; + } + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) { + // #### do an addedTo(index, popup, action), need to find out index + (*it)->addTo(popup); + } + return true; + } + if (qobject_cast<QMenu*>(w)) { + QMenu *menu = (QMenu*)w; + if (d->dropdown) { + Q3ActionGroupPrivate::Action4Item *ai = new Q3ActionGroupPrivate::Action4Item; + if(!ai->action) { //static + ai->action = menu->menuAction(); + if (!iconSet().isNull()) + ai->action->setIcon(iconSet()); + if (menuText().isEmpty()) + ai->action->setText(text()); + else + ai->action->setText(menuText()); + } + addedTo(w, w); + ai->widget = w; + ai->widget->addAction(Q3ActionGroupPrivate::Action4Item::action); + d->action4items.append(ai); + menu = ai->action->menu(); + } + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) + (*it)->addTo(menu); + return true; + } + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) { + // #### do an addedTo(index, popup, action), need to find out index + (*it)->addTo(w); + } + return true; +} + +/*! \reimp +*/ +bool Q3ActionGroup::removeFrom(QWidget* w) +{ + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) + (*it)->removeFrom(w); + +#ifndef QT_NO_TOOLBAR + if (qobject_cast<Q3ToolBar*>(w)) { + QList<QComboBox*>::Iterator cb(d->comboboxes.begin()); + while (cb != d->comboboxes.end()) { + QComboBox *box = *cb; + ++cb; + if (box->parentWidget() == w) + delete box; + } + QList<QToolButton*>::Iterator mb(d->menubuttons.begin()); + while (mb != d->menubuttons.end()) { + QToolButton *btn = *mb; + ++mb; + if (btn->parentWidget() == w) + delete btn; + } + } else +#endif + if (qobject_cast<Q3PopupMenu*>(w)) { + QList<Q3ActionGroupPrivate::MenuItem*>::Iterator pu(d->menuitems.begin()); + while (pu != d->menuitems.end()) { + Q3ActionGroupPrivate::MenuItem *mi = *pu; + ++pu; + if (d->dropdown && mi->popup) + ((Q3PopupMenu*)w)->removeItem(mi->id); + delete mi->popup; + } + } + if (qobject_cast<QMenu*>(w)) { + QList<Q3ActionGroupPrivate::Action4Item*>::Iterator it(d->action4items.begin()); + Q3ActionGroupPrivate::Action4Item *a4i; + while (it != d->action4items.end()) { + a4i = *it; + ++it; + if (a4i->widget == w) { + a4i->widget->removeAction(a4i->action); + d->action4items.removeAll(a4i); + delete a4i; + } + } + } + return true; +} + +/*! \internal +*/ +void Q3ActionGroup::childToggled(bool b) +{ + if (!isExclusive()) + return; + Q3Action* s = qobject_cast<Q3Action*>(sender()); + if (!s) + return; + if (b) { + if (s != d->selected) { + d->selected = s; + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) { + if ((*it)->isToggleAction() && (*it) != s) + (*it)->setOn(false); + } + emit selected(s); + } + } else { + if (s == d->selected) { + // at least one has to be selected + s->setOn(true); + } + } +} + +/*! \internal +*/ +void Q3ActionGroup::childActivated() +{ + Q3Action* s = qobject_cast<Q3Action*>(sender()); + if (s) { + emit activated(s); + emit Q3Action::activated(); + } +} + + +/*! \internal +*/ +void Q3ActionGroup::childDestroyed() +{ + d->actions.removeAll((Q3Action *)sender()); + if (d->selected == sender()) + d->selected = 0; +} + +/*! \reimp +*/ +void Q3ActionGroup::setEnabled(bool enable) +{ + if (enable == isEnabled()) + return; + Q3Action::setEnabled(enable); + d->update(this); +} + +/*! \reimp +*/ +void Q3ActionGroup::setToggleAction(bool toggle) +{ + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) + (*it)->setToggleAction(toggle); + Q3Action::setToggleAction(true); + d->update(this); +} + +/*! \reimp +*/ +void Q3ActionGroup::setOn(bool on) +{ + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) { + Q3Action *act = *it; + if (act->isToggleAction()) + act->setOn(on); + } + Q3Action::setOn(on); + d->update(this); +} + +/*! \reimp + */ +void Q3ActionGroup::setVisible(bool visible) +{ + Q3Action::setVisible(visible); + d->update(this); +} + +/*! \reimp +*/ +void Q3ActionGroup::setIconSet(const QIcon& icon) +{ + Q3Action::setIconSet(icon); + d->update(this); +} + +/*! \reimp +*/ +void Q3ActionGroup::setText(const QString& txt) +{ + if (txt == text()) + return; + + Q3Action::setText(txt); + d->update(this); +} + +/*! \reimp +*/ +void Q3ActionGroup::setMenuText(const QString& text) +{ + if (text == menuText()) + return; + + Q3Action::setMenuText(text); + d->update(this); +} + +/*! \reimp +*/ +void Q3ActionGroup::setToolTip(const QString& text) +{ + if (text == toolTip()) + return; + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) { + if ((*it)->toolTip().isNull()) + (*it)->setToolTip(text); + } + Q3Action::setToolTip(text); + d->update(this); +} + +/*! \reimp +*/ +void Q3ActionGroup::setWhatsThis(const QString& text) +{ + if (text == whatsThis()) + return; + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) { + if ((*it)->whatsThis().isNull()) + (*it)->setWhatsThis(text); + } + Q3Action::setWhatsThis(text); + d->update(this); +} + +/*! \reimp +*/ +void Q3ActionGroup::childEvent(QChildEvent *e) +{ + if (!e->removed()) + return; + + Q3Action *action = qobject_cast<Q3Action*>(e->child()); + if (!action) + return; + + for (QList<QComboBox*>::Iterator cb(d->comboboxes.begin()); + cb != d->comboboxes.end(); ++cb) { + for (int i = 0; i < (*cb)->count(); i++) { + if ((*cb)->text(i) == action->text()) { + (*cb)->removeItem(i); + break; + } + } + } + for (QList<QToolButton*>::Iterator mb(d->menubuttons.begin()); + mb != d->menubuttons.end(); ++mb) { + QMenu* popup = (*mb)->popup(); + if (!popup) + continue; + action->removeFrom(popup); + } + for (QList<Q3ActionGroupPrivate::MenuItem*>::Iterator mi(d->menuitems.begin()); + mi != d->menuitems.end(); ++mi) { + Q3PopupMenu* popup = (*mi)->popup; + if (!popup) + continue; + action->removeFrom(popup); + } + if(QAction *act = Q3ActionGroupPrivate::Action4Item::action) + action->removeFrom(act->menu()); +} + +/*! + \fn void Q3ActionGroup::selected(Q3Action* action) + + This signal is emitted from exclusive groups when toggle actions + change state. + + The argument is the \a action whose state changed to "on". + + \sa setExclusive(), isOn() Q3Action::toggled() +*/ + +/*! + \fn void Q3ActionGroup::activated(Q3Action* action) + + This signal is emitted from groups when one of its actions gets + activated. + + The argument is the \a action which was activated. + + \sa setExclusive(), isOn() Q3Action::toggled() +*/ + + +/*! \internal +*/ +void Q3ActionGroup::internalComboBoxActivated(int index) +{ + if (index == -1) + return; + + Q3Action *a = 0; + for (int i = 0; i <= index && i < (int)d->actions.count(); ++i) { + a = d->actions.at(i); + if (a && a->objectName() == QLatin1String("qt_separator_action")) + index++; + } + a = d->actions.at(index); + if (a) { + if (a != d->selected) { + d->selected = a; + for (QList<Q3Action*>::Iterator it(d->actions.begin()); it != d->actions.end(); ++it) { + if ((*it)->isToggleAction() && (*it) != a) + (*it)->setOn(false); + } + if (a->isToggleAction()) + a->setOn(true); + + emit activated(a); + emit Q3Action::activated(); + emit a->activated(); + if (a->isToggleAction()) + emit selected(d->selected); + } else if (!a->isToggleAction()) { + emit activated(a); + emit Q3Action::activated(); + emit a->activated(); + } + a->clearStatusText(); + } +} + +/*! \internal +*/ +void Q3ActionGroup::internalComboBoxHighlighted(int index) +{ + Q3Action *a = 0; + for (int i = 0; i <= index && i < (int)d->actions.count(); ++i) { + a = d->actions.at(i); + if (a && a->objectName() == QLatin1String("qt_separator_action")) + index++; + } + a = d->actions.at(index); + if (a) + a->showStatusText(a->statusTip()); + else + clearStatusText(); +} + +/*! \internal +*/ +void Q3ActionGroup::internalToggle(Q3Action *a) +{ + int index = d->actions.indexOf(a); + if (index == -1) + return; + + int lastItem = index; + for (int i=0; i<lastItem; ++i) { + Q3Action *action = d->actions.at(i); + if (action->objectName() == QLatin1String("qt_separator_action")) + --index; + } + + for (QList<QComboBox*>::Iterator it(d->comboboxes.begin()); + it != d->comboboxes.end(); ++it) + (*it)->setCurrentItem(index); +} + +/*! \internal +*/ +void Q3ActionGroup::objectDestroyed() +{ + const QObject* obj = sender(); + d->menubuttons.removeAll((QToolButton *)obj); + for (QList<Q3ActionGroupPrivate::MenuItem *>::Iterator mi(d->menuitems.begin()); + mi != d->menuitems.end(); ++mi) { + if ((*mi)->popup == obj) { + d->menuitems.removeAll(*mi); + delete *mi; + break; + } + } + d->popupmenus.removeAll((Q3PopupMenu*)obj); + d->comboboxes.removeAll((QComboBox*)obj); +} + +/*! + 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 Q3ActionGroup::addedTo(QWidget *actionWidget, QWidget *container, Q3Action *a) +{ + Q_UNUSED(actionWidget); + Q_UNUSED(container); + Q_UNUSED(a); +} + +/*! + \overload + + 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 Q3ActionGroup::addedTo(int index, Q3PopupMenu *menu, Q3Action *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 Q3ActionGroup::addedTo(QWidget *actionWidget, QWidget *container) +{ + Q_UNUSED(actionWidget); + Q_UNUSED(container); +} + +/*! + \reimp + \overload + + This function is called from the addTo() function when it has + created a menu item at the index position \a index in the popup + menu \a menu. +*/ + +void Q3ActionGroup::addedTo(int index, Q3PopupMenu *menu) +{ + Q_UNUSED(index); + Q_UNUSED(menu); +} + +/*! + \fn void Q3ActionGroup::insert(Q3Action *action) + + Use add(\a action) instead. +*/ + +QT_END_NAMESPACE + +#endif diff --git a/src/qt3support/widgets/q3action.h b/src/qt3support/widgets/q3action.h new file mode 100644 index 0000000..c079eac --- /dev/null +++ b/src/qt3support/widgets/q3action.h @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3ACTION_H +#define Q3ACTION_H + +#include <QtGui/qicon.h> +#include <QtGui/qkeysequence.h> +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_ACTION + +class Q3ActionPrivate; +class Q3ActionGroupPrivate; +class QStatusBar; +class Q3PopupMenu; +class QToolTipGroup; +class QWidget; + +class Q_COMPAT_EXPORT Q3Action : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool toggleAction READ isToggleAction WRITE setToggleAction) + Q_PROPERTY(bool on READ isOn WRITE setOn) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) + Q_PROPERTY(QIcon iconSet READ iconSet WRITE setIconSet) + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QString menuText READ menuText WRITE setMenuText) + Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip) + Q_PROPERTY(QString statusTip READ statusTip WRITE setStatusTip) + Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis) +#ifndef QT_NO_ACCEL + Q_PROPERTY(QKeySequence accel READ accel WRITE setAccel) +#endif + Q_PROPERTY(bool visible READ isVisible WRITE setVisible) + +public: + Q3Action(QObject* parent, const char* name = 0); +#ifndef QT_NO_ACCEL + Q3Action(const QString& menuText, QKeySequence accel, + QObject* parent, const char* name = 0); + Q3Action(const QIcon& icon, const QString& menuText, QKeySequence accel, + QObject* parent, const char* name = 0); + + Q3Action(const QString& text, const QIcon& icon, const QString& menuText, QKeySequence accel, + QObject* parent, const char* name = 0, bool toggle = false); // obsolete + Q3Action(const QString& text, const QString& menuText, QKeySequence accel, QObject* parent, + const char* name = 0, bool toggle = false); // obsolete +#endif + Q3Action(QObject* parent, const char* name , bool toggle); // obsolete + ~Q3Action(); + + virtual void setIconSet(const QIcon&); + QIcon iconSet() const; + virtual void setText(const QString&); + QString text() const; + virtual void setMenuText(const QString&); + QString menuText() const; + virtual void setToolTip(const QString&); + QString toolTip() const; + virtual void setStatusTip(const QString&); + QString statusTip() const; + virtual void setWhatsThis(const QString&); + QString whatsThis() const; +#ifndef QT_NO_ACCEL + virtual void setAccel(const QKeySequence& key); + QKeySequence accel() const; +#endif + virtual void setToggleAction(bool); + + bool isToggleAction() const; + bool isOn() const; + bool isEnabled() const; + bool isVisible() const; + virtual bool addTo(QWidget*); + virtual bool removeFrom(QWidget*); + +protected: + virtual void addedTo(QWidget *actionWidget, QWidget *container); + virtual void addedTo(int index, Q3PopupMenu *menu); + +public Q_SLOTS: + void activate(); + void toggle(); + virtual void setOn(bool); + virtual void setEnabled(bool); + void setDisabled(bool); + virtual void setVisible(bool); + +Q_SIGNALS: + void activated(); + void toggled(bool); + +private Q_SLOTS: + void internalActivation(); + void toolButtonToggled(bool); + void objectDestroyed(); + void menuStatusText(int id); + void showStatusText(const QString&); + void clearStatusText(); + +private: + Q_DISABLE_COPY(Q3Action) + + void init(); + + Q3ActionPrivate* d; + + friend class Q3ActionPrivate; + friend class Q3ActionGroup; + friend class Q3ActionGroupPrivate; +}; + +class Q_COMPAT_EXPORT Q3ActionGroup : public Q3Action +{ + Q_OBJECT + Q_PROPERTY(bool exclusive READ isExclusive WRITE setExclusive) + Q_PROPERTY(bool usesDropDown READ usesDropDown WRITE setUsesDropDown) + +public: + Q3ActionGroup(QObject* parent, const char* name = 0); + Q3ActionGroup(QObject* parent, const char* name , bool exclusive ); // obsolete + ~Q3ActionGroup(); + void setExclusive(bool); + bool isExclusive() const; + void add(Q3Action* a); + void addSeparator(); + bool addTo(QWidget*); + bool removeFrom(QWidget*); + void setEnabled(bool); + void setToggleAction(bool toggle); + void setOn(bool on); + void setVisible(bool); + + void setUsesDropDown(bool enable); + bool usesDropDown() const; + + void setIconSet(const QIcon &); + void setText(const QString&); + void setMenuText(const QString&); + void setToolTip(const QString&); + void setWhatsThis(const QString&); + +protected: + void childEvent(QChildEvent*); + virtual void addedTo(QWidget *actionWidget, QWidget *container, Q3Action *a); + virtual void addedTo(int index, Q3PopupMenu *menu, Q3Action *a); + virtual void addedTo(QWidget *actionWidget, QWidget *container); + virtual void addedTo(int index, Q3PopupMenu *menu); + +Q_SIGNALS: + void selected(Q3Action*); + void activated(Q3Action *); + +private Q_SLOTS: + void childToggled(bool); + void childActivated(); + void childDestroyed(); + void internalComboBoxActivated(int); + void internalComboBoxHighlighted(int); + void internalToggle(Q3Action*); + void objectDestroyed(); + +private: + Q3ActionGroupPrivate* d; + +public: + void insert(Q3Action *a) { add(a); } + +private: + Q_DISABLE_COPY(Q3ActionGroup) +}; + +#endif // QT_NO_ACTION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3ACTION_H diff --git a/src/qt3support/widgets/q3button.cpp b/src/qt3support/widgets/q3button.cpp new file mode 100644 index 0000000..3871f56 --- /dev/null +++ b/src/qt3support/widgets/q3button.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3button.h" +#include "qpainter.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Q3Button + \brief The Q3Button class is a compatibility base class of button + widgets + + \compat + + \bold{In new code, use QAbstractButton.} + + To subclass Q3Button, 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). +*/ + +/*! + Constructs a standard button called \a name with parent \a parent, + using the widget flags \a f. +*/ + +Q3Button::Q3Button( QWidget *parent, const char *name, Qt::WindowFlags f ) + : QAbstractButton( parent, name, f ) +{ +} + +/*! + Destroys the button. + */ +Q3Button::~Q3Button() +{ +} + +/*! + \fn void Q3Button::paintEvent( QPaintEvent *event) + + Handles paint events, received in \a event, 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 Q3Button::paintEvent( QPaintEvent *) +{ + QPainter p(this); + drawButton( &p ); +} + +/*! + \fn void Q3Button::drawButton( QPainter *painter) + + Draws the button on the given \a painter. 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() +*/ +void Q3Button::drawButton( QPainter * ) +{ +} + +/*! + \fn void Q3Button::drawButtonLabel( QPainter *painter ) + + Draws the button text or pixmap on the given \a painter. + + This virtual function is reimplemented by subclasses to draw real + buttons. It is invoked by drawButton(). + + \sa drawButton(), paintEvent() +*/ + +void Q3Button::drawButtonLabel( QPainter * ) +{ +} + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3button.h b/src/qt3support/widgets/q3button.h new file mode 100644 index 0000000..fd6c0a1 --- /dev/null +++ b/src/qt3support/widgets/q3button.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3BUTTON_H +#define Q3BUTTON_H + +#include <QtGui/qabstractbutton.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class Q_COMPAT_EXPORT Q3Button : public QAbstractButton +{ + Q_OBJECT +public: + Q3Button( QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0 ); + ~Q3Button(); + +protected: + virtual void drawButton( QPainter * ); + virtual void drawButtonLabel( QPainter * ); + void paintEvent( QPaintEvent * ); + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3BUTTON_H diff --git a/src/qt3support/widgets/q3buttongroup.cpp b/src/qt3support/widgets/q3buttongroup.cpp new file mode 100644 index 0000000..b6021a1 --- /dev/null +++ b/src/qt3support/widgets/q3buttongroup.cpp @@ -0,0 +1,565 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3buttongroup.h" +#include "qabstractbutton.h" +#include "qmap.h" +#include "qapplication.h" +#include "qradiobutton.h" +#include "qevent.h" +#include "qset.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Q3ButtonGroup + \brief The Q3ButtonGroup widget organizes QAbstractButton widgets in a group. + + \compat + + 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 Q3ButtonGroup can display a frame + and a title because it inherits Q3GroupBox. + \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(). + + \sa QPushButton, QCheckBox, QRadioButton +*/ + +/*! + \property Q3ButtonGroup::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 Q3ButtonGroup::radioButtonExclusive + \brief whether the radio buttons in the group are exclusive + + If this property is true (the default), the \link QRadioButton + radio buttons\endlink in the group are treated exclusively. +*/ + + +/*! + Constructs a button group with no title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +Q3ButtonGroup::Q3ButtonGroup(QWidget *parent, const char *name) + : Q3GroupBox(parent, name) +{ + init(); +} + +/*! + Constructs a button group with the title \a title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +Q3ButtonGroup::Q3ButtonGroup(const QString &title, QWidget *parent, + const char *name) + : Q3GroupBox(title, parent, name) +{ + init(); +} + +/*! + Constructs a button group with no title. Child widgets will be + arranged in \a strips rows or columns (depending on \a + orientation). + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +Q3ButtonGroup::Q3ButtonGroup(int strips, Qt::Orientation orientation, + QWidget *parent, const char *name) + : Q3GroupBox(strips, orientation, parent, name) +{ + init(); +} + +/*! + Constructs a button group with title \a title. Child widgets will + be arranged in \a strips rows or columns (depending on \a + orientation). + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +Q3ButtonGroup::Q3ButtonGroup(int strips, Qt::Orientation orientation, + const QString &title, QWidget *parent, + const char *name) + : Q3GroupBox(strips, orientation, title, parent, name) +{ + init(); +} + +/*! + Initializes the button group. +*/ + +void Q3ButtonGroup::init() +{ + group.setExclusive(false); + radio_excl = true; +} + +/*! Destructor. */ + +Q3ButtonGroup::~Q3ButtonGroup() +{ +} + +bool Q3ButtonGroup::isExclusive() const +{ + return group.exclusive(); +} + +void Q3ButtonGroup::setExclusive(bool enable) +{ + group.setExclusive(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 Q3ButtonGroup::insert(QAbstractButton *button, int id) +{ + remove_helper(button); + return insert_helper(button, id); +} + +int Q3ButtonGroup::insert_helper(QAbstractButton *button, int id) +{ + if (isExclusive() || !qobject_cast<QRadioButton*>(button)) + group.addButton(button); + + static int seq_no = -2; + if (id < -1) + id = seq_no--; + else if (id == -1) + id = buttonIds.count(); + buttonIds.insert(id, button); + connect(button, SIGNAL(pressed()) , SLOT(buttonPressed())); + connect(button, SIGNAL(released()), SLOT(buttonReleased())); + connect(button, SIGNAL(clicked()) , SLOT(buttonClicked())); + connect(button, SIGNAL(destroyed()) , SLOT(buttonDestroyed())); + return id; +} + +/*! + Returns the number of buttons in the group. +*/ +int Q3ButtonGroup::count() const +{ + fixChildren(); + return buttonIds.count(); +} + +/*! + Removes the \a button from the button group. + + \sa insert() +*/ + +void Q3ButtonGroup::remove(QAbstractButton *button) +{ + fixChildren(); + remove_helper(button); +} + +void Q3ButtonGroup::remove_helper(QAbstractButton *button) +{ + QMap<int, QAbstractButton*>::Iterator it = buttonIds.begin(); + while (it != buttonIds.end()) { + if (it.value() == button) { + buttonIds.erase(it); + button->disconnect(this); + group.removeButton(button); + break; + } + ++it; + } +} + + +/*! + Returns the button with the specified identifier \a id, or 0 if + the button was not found. +*/ + +QAbstractButton *Q3ButtonGroup::find(int id) const +{ + fixChildren(); + return buttonIds.value(id); +} + + +/*! + \fn void Q3ButtonGroup::pressed(int id) + + This signal is emitted when a button in the group is \link + QAbstractButton::pressed() pressed\endlink. The \a id argument is the + button's identifier. + + \sa insert() +*/ + +/*! + \fn void Q3ButtonGroup::released(int id) + + This signal is emitted when a button in the group is \link + QAbstractButton::released() released\endlink. The \a id argument is the + button's identifier. + + \sa insert() +*/ + +/*! + \fn void Q3ButtonGroup::clicked(int id) + + This signal is emitted when a button in the group is \link + QAbstractButton::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 + QAbstractButton::pressed() signal. +*/ + +void Q3ButtonGroup::buttonPressed() +{ + QAbstractButton *senderButton = qobject_cast<QAbstractButton *>(sender()); + Q_ASSERT(senderButton); + int senderId = id(senderButton); + if (senderId != -1) + emit pressed(senderId); +} + +/*! + \internal + This slot is activated when one of the buttons in the group emits the + QAbstractButton::released() signal. +*/ + +void Q3ButtonGroup::buttonReleased() +{ + QAbstractButton *senderButton = qobject_cast<QAbstractButton *>(sender()); + Q_ASSERT(senderButton); + int senderId = id(senderButton); + if (senderId != -1) + emit released(senderId); +} + +/*! + \internal + This slot is activated when one of the buttons in the group emits the + QAbstractButton::clicked() signal. +*/ + +void Q3ButtonGroup::buttonClicked() +{ + QAbstractButton *senderButton = qobject_cast<QAbstractButton *>(sender()); + Q_ASSERT(senderButton); + int senderId = id(senderButton); + if (senderId != -1) + emit clicked(senderId); +} + +/*! + \internal +*/ +void Q3ButtonGroup::buttonDestroyed() +{ + int id = buttonIds.key(static_cast<QAbstractButton *>(sender()), -1); + if (id != -1) + buttonIds.remove(id); +} + +void Q3ButtonGroup::setButton(int id) +{ + QAbstractButton *b = find(id); + if (b) + b->setOn(true); +} + +void Q3ButtonGroup::setRadioButtonExclusive(bool on) +{ + radio_excl = on; +} + + +/*! + Returns the selected toggle button if exactly one is selected; + otherwise returns 0. + + \sa selectedId() +*/ + +QAbstractButton *Q3ButtonGroup::selected() const +{ + fixChildren(); + QAbstractButton *candidate = 0; + QMap<int, QAbstractButton*>::ConstIterator it = buttonIds.constBegin(); + while (it != buttonIds.constEnd()) { + if (it.value()->isCheckable() && it.value()->isChecked()) { + if (candidate) + return 0; + candidate = it.value(); + } + ++it; + } + return candidate; +} + +/*! + \property Q3ButtonGroup::selectedId + \brief The id of the selected toggle button. + + If no toggle button is selected, id() returns -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 Q3ButtonGroup::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 Q3ButtonGroup::id(QAbstractButton *button) const +{ + fixChildren(); + QMap<int, QAbstractButton*>::ConstIterator it = buttonIds.constBegin(); + while (it != buttonIds.constEnd()) { + if (it.value() == button) + return it.key(); + ++it; + } + return -1; +} + + +/*! + \reimp +*/ +bool Q3ButtonGroup::event(QEvent * e) +{ + if (e->type() == QEvent::ChildInserted) { + QChildEvent * ce = (QChildEvent *) e; + if (QAbstractButton *button = qobject_cast<QAbstractButton*>(ce->child())) { + button->setAutoExclusive(false); + if (group.exclusive() || qobject_cast<QRadioButton*>(button)) { + button->setAutoExclusive(true); + QMap<int, QAbstractButton*>::ConstIterator it = buttonIds.constBegin(); + while (it != buttonIds.constEnd()) { + if (it.value() == button) + return Q3GroupBox::event(e); + ++it; + } + } + insert(button, id(button)); + } + } + return Q3GroupBox::event(e); +} + +void Q3ButtonGroup::fixChildren() const +{ + if (children().count() == buttonIds.count()) + return; // small optimization, all our children have ids. + + QSet<QAbstractButton*> set; + for (QMap<int, QAbstractButton*>::ConstIterator it = buttonIds.constBegin(); + it != buttonIds.constEnd(); ++it) + set.insert(*it); + // Use children() instead of qFindChildren<QAbstractButton*> because the search + // should not be recursive.We match with the behavior of Qt3 + const QObjectList childList = children(); + Q_FOREACH(QObject* obj, childList) { + QAbstractButton *button = qobject_cast<QAbstractButton*>(obj); + if ( button && !set.contains(button)) + const_cast<Q3ButtonGroup*>(this)->insert_helper(button); + } +} + + +/*! + \class Q3HButtonGroup + \brief The Q3HButtonGroup widget organizes button widgets in a + group with one horizontal row. + + \compat + + Q3HButtonGroup is a convenience class that offers a thin layer on + top of Q3ButtonGroup. From a layout point of view it is effectively + a Q3HBoxWidget that offers a frame with a title and is specifically + designed for buttons. From a functionality point of view it is a + Q3ButtonGroup. + + \sa Q3VButtonGroup +*/ + +/*! + \fn Q3HButtonGroup::Q3HButtonGroup(QWidget *parent, const char *name) + + Constructs a horizontal button group with no title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +/*! + \fn Q3HButtonGroup::Q3HButtonGroup(const QString &title, QWidget *parent, + const char *name) + + Constructs a horizontal button group with the title \a title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +/*! + \class Q3VButtonGroup + \brief The Q3VButtonGroup widget organizes button widgets in a + vertical column. + + \compat + + Q3VButtonGroup is a convenience class that offers a thin layer on top + of Q3ButtonGroup. Think of it as a QVBoxWidget that offers a frame with a + title and is specifically designed for buttons. + + \sa Q3HButtonGroup +*/ + +/*! + \fn Q3VButtonGroup::Q3VButtonGroup(QWidget *parent, const char *name) + + Constructs a vertical button group with no title. + + The \a parent and \a name arguments are passed on to the QWidget + constructor. +*/ + +/*! + \fn Q3VButtonGroup::Q3VButtonGroup(const QString &title, QWidget *parent, + const char *name) + + Constructs a vertical button group with the title \a title. + + The \a parent and \a name arguments are passed on to the QWidget + constructor. +*/ + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3buttongroup.h b/src/qt3support/widgets/q3buttongroup.h new file mode 100644 index 0000000..680d351 --- /dev/null +++ b/src/qt3support/widgets/q3buttongroup.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3BUTTONGROUP_H +#define Q3BUTTONGROUP_H + +#include <QtGui/qbuttongroup.h> +#include <Qt3Support/q3groupbox.h> +#include <QtCore/qmap.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class QAbstractButton; + +class Q_COMPAT_EXPORT Q3ButtonGroup : public Q3GroupBox +{ + 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: + Q3ButtonGroup(QWidget* parent=0, const char* name=0); + Q3ButtonGroup(const QString &title, + QWidget* parent=0, const char* name=0); + Q3ButtonGroup(int columns, Qt::Orientation o, + QWidget* parent=0, const char* name=0); + Q3ButtonGroup(int columns, Qt::Orientation o, const QString &title, + QWidget* parent=0, const char* name=0); + ~Q3ButtonGroup(); + + bool isExclusive() const; + bool isRadioButtonExclusive() const { return radio_excl; } + void setExclusive(bool); + void setRadioButtonExclusive(bool); + +public: + int insert(QAbstractButton *, int id=-1); + void remove(QAbstractButton *); + QAbstractButton *find(int id) const; + int id(QAbstractButton *) const; + int count() const; + + void setButton(int id); + + QAbstractButton *selected() const; + int selectedId() const; + +Q_SIGNALS: + void pressed(int id); + void released(int id); + void clicked(int id); + +protected Q_SLOTS: + void buttonPressed(); + void buttonReleased(); + void buttonClicked(); + +protected: + bool event(QEvent * e); + +private Q_SLOTS: + void buttonDestroyed(); + +private: + Q_DISABLE_COPY(Q3ButtonGroup) + + void init(); + void fixChildren() const; + int insert_helper(QAbstractButton* , int id=-1); + void remove_helper(QAbstractButton *); + + bool excl_grp; // Not used. + bool radio_excl; + QMap<int, QAbstractButton*> buttonIds; + QButtonGroup group; +}; + +class Q_COMPAT_EXPORT Q3VButtonGroup : public Q3ButtonGroup +{ + Q_OBJECT +public: + inline Q3VButtonGroup(QWidget* parent=0, const char* name=0) + : Q3ButtonGroup(1, Qt::Horizontal /* sic! */, parent, name) {} + inline Q3VButtonGroup(const QString &title, QWidget* parent=0, const char* name=0) + : Q3ButtonGroup(1, Qt::Horizontal /* sic! */, title, parent, name) {} + +private: + Q_DISABLE_COPY(Q3VButtonGroup) +}; + + +class Q_COMPAT_EXPORT Q3HButtonGroup : public Q3ButtonGroup +{ + Q_OBJECT +public: + inline Q3HButtonGroup(QWidget* parent=0, const char* name=0) + : Q3ButtonGroup(1, Qt::Vertical /* sic! */, parent, name) {} + inline Q3HButtonGroup(const QString &title, QWidget* parent=0, const char* name=0) + : Q3ButtonGroup(1, Qt::Vertical /* sic! */, title, parent, name) {} + +private: + Q_DISABLE_COPY(Q3HButtonGroup) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3BUTTONGROUP_H diff --git a/src/qt3support/widgets/q3combobox.cpp b/src/qt3support/widgets/q3combobox.cpp new file mode 100644 index 0000000..3bac984 --- /dev/null +++ b/src/qt3support/widgets/q3combobox.cpp @@ -0,0 +1,2356 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3combobox.h" +#ifndef QT_NO_COMBOBOX +#include "qpainter.h" +#include "qdrawutil.h" +#include "qpixmap.h" +#include "qtimer.h" +#include "qapplication.h" +#include "qlineedit.h" +#include "qbitmap.h" +#include "qstringlist.h" +#include "qstyle.h" +#include "qevent.h" +#include "qmenu.h" +#include "qmenudata.h" +#include "qstyleoption.h" +#include "qdesktopwidget.h" +#include "q3popupmenu.h" +#include "q3listbox.h" +#include "q3strlist.h" +#include "q3frame.h" +#include <limits.h> +#include <qdebug.h> +#ifndef QT_NO_EFFECTS +# include <private/qeffects_p.h> +#endif +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class Q3ComboBox + \brief The Q3ComboBox widget is a combined button and popup list. + \since 4.1 + \compat + + 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. + + Q3ComboBox 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. Q3ComboBox provides both. + + Q3ComboBox provides two different constructors. The simplest + constructor creates an "old-style" combobox in Motif (or Aqua) + style: + \snippet doc/src/snippets/code/src_qt3support_widgets_q3combobox.cpp 0 + + The other constructor creates a new-style combobox in Motif style, + and can create both read-only and editable comboboxes: + \snippet doc/src/snippets/code/src_qt3support_widgets_q3combobox.cpp 1 + + New-style comboboxes use a list box in both Motif and Windows + styles, and both the content size and the on-screen size of the + list box can be limited with sizeLimit() and setMaxCount() + respectively. Old-style comboboxes use a popup in Aqua and Motif + style, and that popup will happily grow larger than the desktop if + you put enough data into it. + + The two constructors create identical-looking comboboxes in + Windows style. + + Comboboxes can contain pixmaps as well as strings; the + insertItem() and changeItem() functions are suitably overloaded. + For editable comboboxes, the function clearEdit() is provided, + to clear the displayed string without changing the combobox's + contents. + + A combobox emits two signals, activated() and highlighted(), when + a new item has been activated (selected) or highlighted (made + current). Both signals exist in two versions, one with a \c + QString argument and one with an \c int argument. If the user + highlights or activates a pixmap, only the \c int signals are + emitted. Whenever the text of an editable combobox is changed the + textChanged() signal is emitted. + + When the user enters a new string in an editable combobox, the + widget may or may not insert it, and it can insert it in several + locations. The default policy is is \c AtBottom but you can change + this using setInsertionPolicy(). + + It is possible to constrain the input to an editable combobox + using QValidator; see setValidator(). By default, any input is + accepted. + + If the combobox is not editable then it has a default + focusPolicy() of \c TabFocus, i.e. it will not grab focus if + clicked. This differs from both Windows and Motif. If the combobox + is editable then it has a default focusPolicy() of \c StrongFocus, + i.e. it will grab focus if clicked. + + A combobox can be populated using the insert functions, + insertStringList() and insertItem() for example. Items can be + changed with changeItem(). An item can be removed with + removeItem() and all items can be removed with clear(). The text + of the current item is returned by currentText(), and the text of + a numbered item is returned with text(). The current item can be + set with setCurrentItem() or setCurrentText(). The number of items + in the combobox is returned by count(); the maximum number of + items can be set with setMaxCount(). You can allow editing using + setEditable(). For editable comboboxes you can set auto-completion + using setAutoCompletion() and whether or not the user can add + duplicates is set with setDuplicatesEnabled(). + + Depending on the style, Q3ComboBox will use a list box or a + popup menu to display the list of items. See setListBox() for + more information. + + \sa QComboBox, QLineEdit, QSpinBox + {GUI Design Handbook}{GUI Design Handbook: Combo Box, Drop-Down List Box} +*/ + + +/*! + \enum Q3ComboBox::Policy + + This enum specifies what the Q3ComboBox 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. + + \omitvalue NoInsert + \omitvalue InsertAtTop + \omitvalue InsertAtCurrent + \omitvalue InsertAtBottom + \omitvalue InsertAfterCurrent + \omitvalue InsertBeforeCurrent +*/ + + +/*! + \fn void Q3ComboBox::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 + \fn void Q3ComboBox::activated( const QString &string ) + + This signal is emitted when a new item has been activated + (selected). \a string is the selected string. + + You can also use the activated(int) signal, but be aware that its + argument is meaningful only for selected strings, not for user + entered strings. +*/ + +/*! + \fn void Q3ComboBox::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 + \fn void Q3ComboBox::highlighted( const QString &string ) + + This signal is emitted when a new item has been set to be the + current item. \a string is the item's text. + + You can also use the highlighted(int) signal. +*/ + +/*! + \fn void Q3ComboBox::textChanged( const QString &string ) + + This signal is used for editable comboboxes. It is emitted + whenever the contents of the text entry field changes. \a string + contains the new text. +*/ + +/*! + \property Q3ComboBox::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 Q3ComboBox::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 Q3ComboBox::count + \brief the number of items in the combobox +*/ + +/*! + \property Q3ComboBox::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 Q3ComboBox::currentText + \brief the text of the combobox's current item +*/ + +/*! + \property Q3ComboBox::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 Q3ComboBox::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 its appearance + to a 2.0 style Motif combobox is it is set to be editable. +*/ + +/*! + \property Q3ComboBox::insertionPolicy + \brief the position of the items inserted by the user + + The default insertion policy is \c AtBottom. See \l Policy. +*/ + +/*! + \property Q3ComboBox::maxCount + \brief the maximum number of items allowed in the combobox +*/ + +/*! + \property Q3ComboBox::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 scroll bar is added. +*/ + +class Q3ComboBoxPopup : public Q3PopupMenu +{ +public: + Q3ComboBoxPopup( QWidget *parent=0, const char *name=0 ) + : Q3PopupMenu( parent, name ) + { + } + + int itemHeight( int index ) + { + return Q3PopupMenu::itemHeight( index ); + } +}; + +static inline QString escapedComboString(const QString &str) +{ + QString stringToReturn = str; + return stringToReturn.replace(QLatin1Char('&'), QLatin1String("&&")); +} + +class Q3ComboBoxPopupItem : public QMenuItem +{ + Q3ListBoxItem *li; + QSize sc; // Size cache optimization +public: + Q3ComboBoxPopupItem(Q3ListBoxItem *i) : QMenuItem(), li(i), sc(0, 0) { } + virtual bool fullSpan() const { return true; } + virtual void paint( QPainter*, const QColorGroup&, bool, bool, int, int, int, int); + virtual QSize sizeHint() { if (sc.isNull()) sc = QSize(li->width(li->listBox()), QMAX(25, li->height(li->listBox()))); return sc; } +}; +void Q3ComboBoxPopupItem::paint( QPainter* p, const QColorGroup&, bool, + bool, int x, int y, int, int) +{ + p->save(); + p->translate(x, y + ((sizeHint().height() / 2) - (li->height(li->listBox()) / 2))); + li->paint(p); + p->restore(); +} + + +class Q3ComboBoxData +{ +public: + Q3ComboBoxData( Q3ComboBox *cb ): current( 0 ), arrowDown(false), ed( 0 ), usingLBox( false ), pop( 0 ), lBox( 0 ), combo( cb ) + { + duplicatesEnabled = true; + cb->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) ); + } + + inline bool usingListBox() { return usingLBox; } + inline Q3ListBox * listBox() { return lBox; } + inline Q3ComboBoxPopup * popup() { return pop; } + void updateLinedGeometry(); + + void setListBox( Q3ListBox *l ) { lBox = l ; usingLBox = true; + l->setMouseTracking( true );} + + void setPopupMenu( Q3ComboBoxPopup * pm, bool isPopup=true ) + { pop = pm; if(isPopup) usingLBox = false; } + + QStyleOptionComboBox getStyleOption() const + { + QStyleOptionComboBox opt; + opt.init(combo); + if (!combo->editable() && combo->hasFocus()) + opt.state |= QStyle::State_Selected; + opt.subControls = QStyle::SC_All; + if (arrowDown) { + opt.activeSubControls = QStyle::SC_ComboBoxArrow; + opt.state |= QStyle::State_Sunken; + } + opt.editable = combo->editable(); + opt.frame = 1; // ### get from style? + if (current > -1 && current < combo->count()) { + opt.currentText = combo->text(current); + if (combo->pixmap(current)) + opt.currentIcon = QIcon(*combo->pixmap(current)); + } + opt.iconSize = QSize(22, 22); // ### need a sane value here +// if (container && container->isVisible()) +// opt.state |= QStyle::State_On; + return opt; + } + + int current; + int maxCount; + int sizeLimit; + Q3ComboBox::Policy p; + bool autoresize; + bool poppedUp; + bool mouseWasInsidePopup; + bool arrowPressed; + bool arrowDown; + bool discardNextMousePress; + bool shortClick; + bool useCompletion; + bool completeNow; + int completeAt; + bool duplicatesEnabled; + int fullHeight, currHeight; + + QLineEdit * ed; // /bin/ed rules! + QTimer *completionTimer; + + QSize sizeHint; + QHash<int, QPixmap> popupPixmaps; + +private: + bool usingLBox; + Q3ComboBoxPopup *pop; + Q3ListBox *lBox; + Q3ComboBox *combo; +}; + +void Q3ComboBoxData::updateLinedGeometry() +{ + if ( !ed || !combo ) + return; + + QStyleOptionComboBox opt = getStyleOption(); + QRect r = combo->style()->subControlRect( + QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, combo); + + const QPixmap *pix = current < combo->count() ? combo->pixmap( current ) : 0; + if ( pix && pix->width() < r.width() ) + r.setLeft( r.left() + pix->width() + 4 ); + if ( r != ed->geometry() ) + ed->setGeometry( r ); +} + +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( "Q3ComboBox::%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( "Q3ComboBox::%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 Q3ComboBox, then + the pixmap() function will always return 0. To workaround this, + use the other constructor. + +*/ + + + +Q3ComboBox::Q3ComboBox( QWidget *parent, const char *name ) + : QWidget( parent, name, Qt::WNoAutoErase ) +{ + d = new Q3ComboBoxData( this ); + QStyleOptionComboBox opt; + opt.init(this); + if ( style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) || + style()->styleHint(QStyle::SH_GUIStyle, &opt, this) == Qt::MotifStyle ) { + d->setPopupMenu( new Q3ComboBoxPopup( this, "in-combo" ) ); + d->popup()->setFont( font() ); + connect( d->popup(), SIGNAL(activated(int)), + SLOT(internalActivate(int)) ); + connect( d->popup(), SIGNAL(highlighted(int)), + SLOT(internalHighlight(int)) ); + } else { + setUpListBox(); + } + d->ed = 0; + d->current = 0; + d->maxCount = INT_MAX; + d->sizeLimit = 10; + d->p = AtBottom; + d->autoresize = false; + d->poppedUp = false; + d->arrowDown = false; + d->arrowPressed = false; + d->discardNextMousePress = false; + d->shortClick = false; + d->useCompletion = false; + d->completeAt = 0; + d->completeNow = false; + d->completionTimer = new QTimer( this ); + + setFocusPolicy( Qt::TabFocus ); + setBackgroundMode( Qt::PaletteButton ); +} + + +/*! + Constructs a combobox with a maximum size and either Motif 2.0 or + Windows look and feel. + + The input field can be edited if \a rw is true, otherwise the user + may only choose one of the items in the combobox. + + The \a parent and \a name arguments are passed on to the QWidget + constructor. +*/ + + +Q3ComboBox::Q3ComboBox( bool rw, QWidget *parent, const char *name ) + : QWidget( parent, name, Qt::WNoAutoErase ) +{ + d = new Q3ComboBoxData( this ); + setUpListBox(); + + QStyleOptionComboBox opt = d->getStyleOption(); + if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) + d->popup()->setItemChecked(d->current, false); + d->maxCount = INT_MAX; + setSizeLimit(10); + d->p = AtBottom; + d->autoresize = false; + d->poppedUp = false; + d->arrowDown = false; + d->discardNextMousePress = false; + d->shortClick = false; + d->useCompletion = false; + d->completeAt = 0; + d->completeNow = false; + d->completionTimer = new QTimer( this ); + + setFocusPolicy( Qt::StrongFocus ); + + d->ed = 0; + if ( rw ) + setUpLineEdit(); + setBackgroundMode( Qt::PaletteButton, Qt::PaletteBase ); +} + + + +/*! + Destroys the combobox. +*/ + +Q3ComboBox::~Q3ComboBox() +{ + delete d; +} + +void Q3ComboBox::setDuplicatesEnabled( bool enable ) +{ + d->duplicatesEnabled = enable; +} + +bool Q3ComboBox::duplicatesEnabled() const +{ + return d->duplicatesEnabled; +} + +int Q3ComboBox::count() const +{ + if ( d->usingListBox() ) + return d->listBox()->count(); + else if (d->popup()) + return d->popup()->count(); + else + return 0; +} + + +/*! + \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 Q3ComboBox::insertStrList( const Q3StrList &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 Q3ComboBox::insertStrList( const Q3StrList *list, int index ) +{ + if ( !list ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( list != 0 ); +#endif + return; + } + Q3StrListIterator it( *list ); + const char* tmp; + if ( index < 0 ) + index = count(); + while ( (tmp=it.current()) ) { + ++it; + if ( d->usingListBox() ) + d->listBox()->insertItem( QString::fromLatin1(tmp), index ); + else + d->popup()->insertItem( escapedComboString(QString::fromLatin1(tmp)), index, index ); + if ( index++ == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + currentChanged(); + } + } + if ( index != count() ) + reIndex(); +} + +/*! + Inserts the \a list of strings at position \a index in the + combobox. +*/ + +void Q3ComboBox::insertStringList( const QStringList &list, int index ) +{ + QStringList::ConstIterator it = list.begin(); + if ( index < 0 ) + index = count(); + while ( it != list.end() ) { + if ( d->usingListBox() ) + d->listBox()->insertItem( *it, index ); + else + d->popup()->insertItem( escapedComboString(*it), index, index ); + if ( index++ == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + currentChanged(); + } + ++it; + } + if ( index != count() ) + reIndex(); +} + +/*! + Inserts the array of char * \a strings at position \a index in the + combobox. + + The \a numStrings argument is the number of strings. If \a + numStrings is -1 (default), the \a strings array must be + terminated with 0. + + Example: + \snippet doc/src/snippets/code/src_qt3support_widgets_q3combobox.cpp 2 + + \sa insertStringList() +*/ + +void Q3ComboBox::insertStrList( const char **strings, int numStrings, int index) +{ + if ( !strings ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( strings != 0 ); +#endif + return; + } + if ( index < 0 ) + index = count(); + int i = 0; + while ( (numStrings<0 && strings[i]!=0) || i<numStrings ) { + if ( d->usingListBox() ) + d->listBox()->insertItem( QString::fromLatin1(strings[i]), index ); + else + d->popup()->insertItem( escapedComboString(QString::fromLatin1(strings[i])), index, index ); + i++; + if ( index++ == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + currentChanged(); + } + } + if ( index != count() ) + reIndex(); +} + + +/*! + Inserts a text item with text \a t, at position \a index. The item + will be appended if \a index is negative. +*/ + +void Q3ComboBox::insertItem( const QString &t, int index ) +{ + int cnt = count(); + if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->insertItem( t, index ); + else + d->popup()->insertItem( escapedComboString(t), index, index ); + if ( index != cnt ) + reIndex(); + if ( index == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } + if ( index == d->current ) + currentChanged(); +} + +/*! + \overload + + Inserts a \a pixmap item at position \a index. The item will be + appended if \a index is negative. +*/ + +void Q3ComboBox::insertItem( const QPixmap &pixmap, int index ) +{ + int cnt = count(); + if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->insertItem( pixmap, index ); + else + d->popup()->insertItem( pixmap, index, index ); + if ( index != cnt ) + reIndex(); + if ( index == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } + if ( index == d->current ) + currentChanged(); +} + +/*! + \overload + + Inserts a \a pixmap item with additional text \a text at position + \a index. The item will be appended if \a index is negative. +*/ + +void Q3ComboBox::insertItem( const QPixmap &pixmap, const QString& text, int index ) +{ + int cnt = count(); + if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->insertItem( pixmap, text, index ); + else + d->popup()->insertItem( pixmap, escapedComboString(text), index, index ); + if ( index != cnt ) + reIndex(); + if ( index == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( this->text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } + if ( index == d->current ) + currentChanged(); +} + + +/*! + Removes the item at position \a index. +*/ + +void Q3ComboBox::removeItem( int index ) +{ + int cnt = count(); + if ( !checkIndex( "removeItem", name(), cnt, index ) ) + return; + if ( d->usingListBox() ) { + QStyleOptionComboBox opt = d->getStyleOption(); + if ( style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) && d->popup() ) + d->popup()->removeItemAt( index ); + d->listBox()->removeItem( index ); + } else { + d->popup()->removeItemAt( index ); + } + if ( index != cnt-1 ) + reIndex(); + if ( index == d->current ) { + if ( d->ed ) { + QString s = QString::fromLatin1(""); + if (d->current < cnt - 1) + s = text( d->current ); + d->ed->setText( s ); + d->updateLinedGeometry(); + } + else { + if ( d->usingListBox() ) { + d->current = d->listBox()->currentItem(); + } else { + if (d->current > count()-1 && d->current > 0) + d->current--; + } + update(); + } + currentChanged(); + } + else { + if ( !d->ed ) { + if (d->current < cnt - 1) + setCurrentItem( d->current ); + else + setCurrentItem( d->current - 1 ); + } + } + +} + + +/*! + Removes all combobox items. +*/ + +void Q3ComboBox::clear() +{ + QStyleOptionComboBox opt = d->getStyleOption(); + if ( d->usingListBox() ) { + if ( style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) && d->popup() ) + d->popup()->clear(); + d->listBox()->resize( 0, 0 ); + d->listBox()->clear(); + } else { + d->popup()->clear(); + } + + if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) + d->popup()->setItemChecked(d->current, false); + d->current = 0; + if ( d->ed ) { + d->ed->setText( QString::fromLatin1("") ); + d->updateLinedGeometry(); + } + currentChanged(); +} + + +QString Q3ComboBox::currentText() const +{ + if ( d->ed ) + return d->ed->text(); + else if ( d->current < count() ) + return text( currentItem() ); + else + return QString::null; +} + +void Q3ComboBox::setCurrentText( const QString& txt ) +{ + int i; + for ( i = 0; i < count(); i++) + if ( text( i ) == txt ) + break; + if ( i < count() ) + setCurrentItem( i ); + else if ( d->ed ) + d->ed->setText( txt ); + else + changeItem( txt, currentItem() ); +} + + +/*! + Returns the text item at position \a index, or QString::null if + the item is not a string. + + \sa currentText() +*/ + +QString Q3ComboBox::text( int index ) const +{ + if ( !checkIndex( "text", name(), count(), index ) ) + return QString::null; + if ( d->usingListBox() ) { + return d->listBox()->text( index ); + } else { + QString retText = d->popup()->text(index); + retText.replace(QLatin1String("&&"), QString(QLatin1Char('&'))); + return retText; + } +} + +/*! + Returns the pixmap item at position \a index, or 0 if the item is + not a pixmap. +*/ + +const QPixmap *Q3ComboBox::pixmap( int index ) const +{ + if ( !checkIndex( "pixmap", name(), count(), index ) ) + return 0; + + if (d->usingListBox()) { + return d->listBox()->pixmap( index ); + } else { + d->popupPixmaps[index] = d->popup()->pixmap(index); + return d->popupPixmaps[index].isNull() ? 0 : &d->popupPixmaps[index]; + } +} + +/*! + Replaces the item at position \a index with the text \a t. +*/ + +void Q3ComboBox::changeItem( const QString &t, int index ) +{ + if ( !checkIndex( "changeItem", name(), count(), index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->changeItem( t, index ); + else + d->popup()->changeItem(index, t); + 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 Q3ComboBox::changeItem( const QPixmap &im, int index ) +{ + if ( !checkIndex( "changeItem", name(), count(), index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->changeItem( im, index ); + else + d->popup()->changeItem(index, im); + 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 Q3ComboBox::changeItem( const QPixmap &im, const QString &t, int index ) +{ + if ( !checkIndex( "changeItem", name(), count(), index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->changeItem( im, t, index ); + else + d->popup()->changeItem(index, im, t); + if ( index == d->current ) + update(); +} + + +int Q3ComboBox::currentItem() const +{ + return d->current; +} + +void Q3ComboBox::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; + + QStyleOptionComboBox opt = d->getStyleOption(); + if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, 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(); +} + +/*! + Returns true if auto-resize is enabled; otherwise returns false. + + \sa autoResize +*/ + +bool Q3ComboBox::autoResize() const +{ + return d->autoresize; +} + +/*! + If \a enable is true, enable auto-resize; disable it otherwise. + + \sa autoResize +*/ + +void Q3ComboBox::setAutoResize( bool enable ) +{ + if ( (bool)d->autoresize != enable ) { + d->autoresize = enable; + if ( enable ) + adjustSize(); + } +} + + +/*! + \reimp + + This implementation caches the size hint to avoid resizing when + the contents change dynamically. To invalidate the cached value + call setFont(). +*/ +QSize Q3ComboBox::sizeHint() const +{ + if ( isVisible() && d->sizeHint.isValid() ) + return d->sizeHint; + + constPolish(); + int i, w; + QFontMetrics fm = fontMetrics(); + + int maxW = count() ? 18 : 7 * fm.width(QLatin1Char('x')) + 18; + int maxH = QMAX( fm.lineSpacing(), 14 ) + 2; + + if ( !d->usingListBox() ) { + w = d->popup()->sizeHint().width() - 2* d->popup()->frameWidth(); + if ( w > maxW ) + maxW = w; + } else { + for( i = 0; i < count(); i++ ) { + w = d->listBox()->item( i )->width( d->listBox() ); + if ( w > maxW ) + maxW = w; + } + } + + QStyleOptionComboBox opt = d->getStyleOption(); + d->sizeHint = (style()->sizeFromContents(QStyle::CT_ComboBox, &opt, QSize(maxW, maxH), this). + expandedTo(QApplication::globalStrut())); + + return d->sizeHint; +} + + +/*! + \internal + Receives activated signals from an internal popup list and emits + the activated() signal. +*/ + +void Q3ComboBox::internalActivate( int index ) +{ + QStyleOptionComboBox opt = d->getStyleOption(); + if ( d->current != index ) { + if ( !d->usingListBox() || listBox()->item( index )->isSelectable() ) { + if (d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) + d->popup()->setItemChecked(d->current, false); + d->current = index; + currentChanged(); + } + } + if ( d->usingListBox() ) + popDownListBox(); + else + d->popup()->removeEventFilter( this ); + d->poppedUp = false; + + QString t( text( index ) ); + if ( d->ed ) { + d->ed->setText( t ); + d->updateLinedGeometry(); + } + emit activated( index ); + emit activated( t ); +} + +/*! + \internal + Receives highlighted signals from an internal popup list and emits + the highlighted() signal. +*/ + +void Q3ComboBox::internalHighlight( int index ) +{ + emit highlighted( index ); + QString t = text( index ); + if ( !t.isNull() ) + emit highlighted( t ); +} + +/*! + \internal + Receives timeouts after a click. Used to decide if a Motif style + popup should stay up or not after a click. +*/ +void Q3ComboBox::internalClickTimeout() +{ + d->shortClick = false; +} + +/*! + Sets the palette for both the combobox button and the combobox + popup list to \a palette. +*/ + +void Q3ComboBox::setPalette( const QPalette &palette ) +{ + QWidget::setPalette( palette ); + if ( d->listBox() ) + d->listBox()->setPalette( palette ); + if ( d->popup() ) + d->popup()->setPalette( palette ); +} + +/*! + Sets the font for both the combobox button and the combobox popup + list to \a font. +*/ + +void Q3ComboBox::setFont( const QFont &font ) +{ + d->sizeHint = QSize(); // invalidate size hint + QWidget::setFont( font ); + if ( d->usingListBox() ) + d->listBox()->setFont( font ); + else + d->popup()->setFont( font ); + if (d->ed) + d->ed->setFont( font ); + if ( d->autoresize ) + adjustSize(); +} + + +/*!\reimp +*/ + +void Q3ComboBox::resizeEvent( QResizeEvent * e ) +{ + if ( d->ed ) + d->updateLinedGeometry(); + if ( d->listBox() ) + d->listBox()->resize( width(), d->listBox()->height() ); + QWidget::resizeEvent( e ); +} + +/*!\reimp +*/ + +void Q3ComboBox::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + const QColorGroup & g = colorGroup(); + p.setPen(g.text()); + + if ( width() < 5 || height() < 5 ) { + qDrawShadePanel( &p, rect(), g, false, 2, + &g.brush( QColorGroup::Button ) ); + return; + } + + QStyleOptionComboBox opt = d->getStyleOption(); + bool reverse = QApplication::reverseLayout(); + if ( !d->usingListBox() && + style()->styleHint(QStyle::SH_GUIStyle) == Qt::MotifStyle) { // motif 1.x style + int dist, buttonH, buttonW; + dist = 8; + buttonH = 7; + buttonW = 11; + int xPos; + int x0; + int w = width() - dist - buttonW - 1; + if ( reverse ) { + xPos = dist + 1; + x0 = xPos + 4; + } else { + xPos = w; + x0 = 4; + } + qDrawShadePanel( &p, rect(), g, false, + style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this), + &g.brush( QColorGroup::Button ) ); + qDrawShadePanel( &p, xPos, (height() - buttonH)/2, + buttonW, buttonH, g, false, + style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this) ); + QRect clip( x0, 2, w - 2 - 4 - 5, height() - 4 ); + QString str = d->popup()->text( this->d->current ); + if ( !str.isNull() ) { + p.drawText( clip, Qt::AlignCenter | Qt::TextSingleLine, str ); + } + + QPixmap pix = d->popup()->pixmap( this->d->current ); + QIcon iconSet = d->popup()->iconSet( this->d->current ); + if (!pix.isNull() || !iconSet.isNull()) { + QPixmap pm = ( !pix.isNull() ? pix : iconSet.pixmap() ); + p.setClipRect( clip ); + p.drawPixmap( 4, (height()-pm.height())/2, pm ); + p.setClipping( false ); + } + + if ( hasFocus() ) + p.drawRect( xPos - 5, 4, width() - xPos + 1 , height() - 8 ); + } else if(!d->usingListBox()) { + style()->drawComplexControl(QStyle::CC_ComboBox, &opt, &p, this); + QRect re = style()->subControlRect(QStyle::CC_ComboBox, &opt, + QStyle::SC_ComboBoxEditField, this); + p.setClipRect( re ); + + QString str = d->popup()->text( this->d->current ); + QPixmap pix = d->popup()->pixmap( this->d->current ); + if ( !str.isNull() ) { + p.save(); + p.setFont(font()); + QFontMetrics fm(font()); + int x = re.x(), y = re.y() + fm.ascent(); + x += pix.width() + 5; + p.drawText( x, y, str ); + p.restore(); + } + if (!pix.isNull()) { + p.fillRect(re.x(), re.y(), pix.width() + 4, re.height(), + colorGroup().brush(QColorGroup::Base)); + p.drawPixmap(re.x() + 2, re.y() + (re.height() - pix.height()) / 2, pix); + } + } else { + style()->drawComplexControl(QStyle::CC_ComboBox, &opt, &p, this); + QRect re = style()->subControlRect(QStyle::CC_ComboBox, &opt, + QStyle::SC_ComboBoxEditField, this); + p.setClipRect(re); + + if ( !d->ed ) { + Q3ListBoxItem * 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 ) ) { + Q3ListBoxItem * item = d->listBox()->item( d->current ); + const QPixmap *pix = item->pixmap(); + if ( pix ) { + p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(), + colorGroup().brush( QColorGroup::Base ) ); + p.drawPixmap( re.x() + 2, re.y() + + ( re.height() - pix->height() ) / 2, *pix ); + } + } + p.setClipping( false ); + } +} + + +/*!\reimp +*/ + +void Q3ComboBox::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() != Qt::LeftButton ) + return; + if ( d->discardNextMousePress ) { + d->discardNextMousePress = false; + return; + } + + QStyleOptionComboBox opt = d->getStyleOption(); + QRect arrowRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow + , this); + + // Correction for motif style, where arrow is smaller + // and thus has a rect that doesn't fit the button. + arrowRect.setHeight( QMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) ); + + if ( count() && ( !editable() || arrowRect.contains( e->pos() ) ) ) { + d->arrowPressed = false; + + if ( d->usingListBox() ) { + listBox()->blockSignals( true ); + qApp->sendEvent( listBox(), e ); // trigger the listbox's autoscroll + listBox()->setCurrentItem(d->current); + listBox()->blockSignals( false ); + popup(); + if ( arrowRect.contains( e->pos() ) ) { + d->arrowPressed = true; + d->arrowDown = true; + repaint( false ); + } + } else { + popup(); + } + QTimer::singleShot( 200, this, SLOT(internalClickTimeout())); + d->shortClick = true; + } +} + +/*!\reimp +*/ + +void Q3ComboBox::mouseMoveEvent( QMouseEvent * ) +{ +} + +/*!\reimp +*/ + +void Q3ComboBox::mouseReleaseEvent( QMouseEvent * ) +{ +} + +/*!\reimp +*/ + +void Q3ComboBox::mouseDoubleClickEvent( QMouseEvent *e ) +{ + mousePressEvent( e ); +} + + +/*!\reimp +*/ + +void Q3ComboBox::keyPressEvent( QKeyEvent *e ) +{ + bool handleEventHere = d->usingListBox() || !d->poppedUp; + + int c = currentItem(); + if ( ( e->key() == Qt::Key_F4 && e->state() == 0 ) || + ( e->key() == Qt::Key_Down && (e->state() & Qt::AltModifier) ) || + ( !d->ed && e->key() == Qt::Key_Space ) ) { + if ( count() ) { + if ( !d->usingListBox() ) + d->popup()->setActiveItem( this->d->current ); + popup(); + } + return; + } else if ( handleEventHere && e->key() == Qt::Key_Up ) { + if ( c > 0 ) + setCurrentItem( c-1 ); + } else if ( handleEventHere && e->key() == Qt::Key_Down ) { + if ( ++c < count() ) + setCurrentItem( c ); + } else if ( handleEventHere && e->key() == Qt::Key_Home && ( !d->ed || !d->ed->hasFocus() ) ) { + setCurrentItem( 0 ); + } else if ( handleEventHere && e->key() == Qt::Key_End && ( !d->ed || !d->ed->hasFocus() ) ) { + setCurrentItem( count()-1 ); + } else if ( !d->ed && e->ascii() >= 32 && !e->text().isEmpty() ) { + if ( !d->completionTimer->isActive() ) { + d->completeAt = 0; + c = completionIndex( e->text(), ++c ); + if ( c >= 0 ) { + setCurrentItem( c ); + d->completeAt = e->text().length(); + } + } else { + d->completionTimer->stop(); + QString ct = currentText().left( d->completeAt ) + e->text(); + c = completionIndex( ct, c ); + if ( c < 0 && d->completeAt > 0 ) { + c = completionIndex( e->text(), 0 ); + ct = e->text(); + } + d->completeAt = 0; + if ( c >= 0 ) { + setCurrentItem( c ); + d->completeAt = ct.length(); + } + } + d->completionTimer->start( 400, true ); + } else { + e->ignore(); + return; + } + + c = currentItem(); + if ( count() && !text( c ).isNull() ) + emit activated( text( c ) ); + emit activated( c ); +} + + +/*!\reimp +*/ + +void Q3ComboBox::focusInEvent( QFocusEvent * e ) +{ + QWidget::focusInEvent( e ); + d->completeNow = false; + d->completeAt = 0; +} + +/*!\reimp +*/ + +void Q3ComboBox::focusOutEvent( QFocusEvent * e ) +{ + QWidget::focusOutEvent( e ); + d->completeNow = false; + d->completeAt = 0; +} + +/*!\reimp +*/ +#ifndef QT_NO_WHEELEVENT +void Q3ComboBox::wheelEvent( QWheelEvent *e ) +{ + if ( d->poppedUp ) { + if ( d->usingListBox() ) { + QApplication::sendEvent( d->listBox(), e ); + } + } else { + if ( e->delta() > 0 ) { + int c = currentItem(); + if ( c > 0 ) { + setCurrentItem( c-1 ); + emit activated( currentItem() ); + emit activated( currentText() ); + } + } else { + int c = currentItem(); + if ( ++c < count() ) { + setCurrentItem( c ); + emit activated( currentItem() ); + emit activated( currentText() ); + } + } + e->accept(); + } +} +#endif + +/*! + \internal + Calculates the listbox height needed to contain all items, or as + many as the list box is supposed to contain. +*/ +static int listHeight( Q3ListBox *l, int sl ) +{ + if ( l->count() > 0 ) + return QMIN( l->count(), (uint)sl) * l->item( 0 )->height(l); + else + return l->sizeHint().height(); +} + + +/*! + Pops up the combobox popup list. + + If the list is empty, no items appear. +*/ + +void Q3ComboBox::popup() +{ + if ( !count() || d->poppedUp ) + return; + + QStyleOptionComboBox opt = d->getStyleOption(); + if( !d->usingListBox() || style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this) ) { + if(d->usingListBox()) { + if(!d->popup()) { + Q3ComboBoxPopup *p = new Q3ComboBoxPopup( 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++) { + Q3ListBoxItem *item = d->listBox()->item(i); + if(item->rtti() == Q3ListBoxText::RTTI) { + d->popup()->insertItem(escapedComboString(item->text()), i, i); + } else if(item->rtti() == Q3ListBoxPixmap::RTTI) { + if(item->pixmap()) + d->popup()->insertItem(QIcon(*item->pixmap()), escapedComboString(item->text()), i, i); + else + d->popup()->insertItem(escapedComboString(item->text()), i, i); + } else { + d->popup()->insertItem(new Q3ComboBoxPopupItem(item), i, i); + } + } + } + d->popup()->installEventFilter( this ); + if(d->popup() && style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) + d->popup()->setItemChecked(this->d->current, true); + d->popup()->popup( mapToGlobal( QPoint(0,0) ), this->d->current ); + update(); + } else { + // Send all listbox events to eventFilter(): + Q3ListBox* lb = d->listBox(); + lb->triggerUpdate( true ); + lb->installEventFilter( this ); + d->mouseWasInsidePopup = false; + int w = lb->variableWidth() ? lb->sizeHint().width() : width(); + int h = listHeight( lb, d->sizeLimit ) + 2; + QRect screen = QApplication::desktop()->availableGeometry( this ); + + int sx = screen.x(); // screen pos + int sy = screen.y(); + int sw = screen.width(); // screen width + int sh = screen.height(); // screen height + QPoint pos = mapToGlobal( QPoint(0,height()) ); + // ## Similar code is in QPopupMenu + int x = pos.x(); + int y = pos.y(); + + // the complete widget must be visible + if ( x + w > sx + sw ) + x = sx+sw - w; + if ( x < sx ) + x = sx; + if (y + h > sy+sh && y - h - height() >= 0 ) + y = y - h - height(); + + opt.rect = QRect(x, y, w, h); + QRect rect = style()->subControlRect(QStyle::CC_ComboBox, &opt, + QStyle::SC_ComboBoxListBoxPopup, this); + + // 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 ); + Q3ListBoxItem* 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(Q3ScrollView::Auto); + +#ifndef QT_NO_EFFECTS + if ( QApplication::isEffectEnabled( Qt::UI_AnimateCombo ) ) { + if ( lb->y() < mapToGlobal(QPoint(0,0)).y() ) + qScrollEffect( lb, QEffects::UpScroll ); + else + qScrollEffect( lb ); + } else +#endif + lb->show(); + } + d->poppedUp = true; +} + + +/*! + Updates the widget mask. + + \sa QWidget::setMask() +*/ +void Q3ComboBox::updateMask() +{ + QBitmap bm( size() ); + bm.fill( Qt::color0 ); + + QStyleOptionComboBox opt = d->getStyleOption(); + { + QPainter p(&bm); + p.initFrom(this); + p.fillRect(opt.rect, Qt::color1); // qcommonstyle old drawComplexControl implementation + } + + setMask( bm ); +} + +/*! + \internal + Pops down (removes) the combobox popup list box. +*/ +void Q3ComboBox::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 Q3ComboBox::reIndex() +{ + if ( !d->usingListBox() ) { + int cnt = count(); + while ( cnt-- ) + d->popup()->setId( cnt, cnt ); + } +} + +/*! + \internal + Repaints the combobox. +*/ + +void Q3ComboBox::currentChanged() +{ + if ( d->autoresize ) + adjustSize(); + update(); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif +} + +/*! \reimp + + \internal + + The event filter steals events from the popup or listbox when they + are popped up. It makes the popup stay up after a short click in + motif style. In windows style it toggles the arrow button of the + combobox field, and activates an item and takes down the listbox + when the mouse button is released. +*/ + +bool Q3ComboBox::eventFilter( QObject *object, QEvent *event ) +{ + QStyleOptionComboBox opt = d->getStyleOption(); + if ( !event ) + return true; + else if ( object == d->ed ) { + if ( event->type() == QEvent::KeyPress ) { + bool isAccepted = ( (QKeyEvent*)event )->isAccepted(); + keyPressEvent( (QKeyEvent *)event ); + if ( ((QKeyEvent *)event)->isAccepted() ) { + d->completeNow = false; + return true; + } else if ( ((QKeyEvent *)event)->key() != Qt::Key_End ) { + d->completeNow = true; + d->completeAt = d->ed->cursorPosition(); + } + if ( isAccepted ) + ( (QKeyEvent*)event )->accept(); + else + ( (QKeyEvent*)event )->ignore(); + } else if ( event->type() == QEvent::KeyRelease ) { + keyReleaseEvent( (QKeyEvent *)event ); + return ((QKeyEvent *)event)->isAccepted(); + } else if ( event->type() == QEvent::FocusIn ) { + focusInEvent( (QFocusEvent *)event ); + } else if ( event->type() == QEvent::FocusOut ) { + focusOutEvent( (QFocusEvent *)event ); + } else if ( d->useCompletion && d->completeNow ) { + d->completeNow = false; + if ( !d->ed->text().isNull() && + d->ed->cursorPosition() > d->completeAt && + d->ed->cursorPosition() == (int)d->ed->text().length() ) { + QString ct( d->ed->text() ); + int i = completionIndex( ct, currentItem() ); + if ( i > -1 ) { + QString it = text( i ); + d->ed->validateAndSet( it, ct.length(), + ct.length(), it.length() ); + d->current = i; + // ### sets current item without emitting signals. This is to + // make sure the right item is current if you change current with + // wheel/up/down. While typing current is not valid anyway. Fix properly + // in 4.0. + } + } + } + } else if ( d->usingListBox() && ( object == d->listBox() || + object == d->listBox()->viewport() )) { + QMouseEvent *e = (QMouseEvent*)event; + switch( event->type() ) { + case QEvent::MouseMove: + if ( !d->mouseWasInsidePopup ) { + QPoint pos = e->pos(); + if ( d->listBox()->rect().contains( pos ) ) + d->mouseWasInsidePopup = true; + // Check if arrow button should toggle + if ( d->arrowPressed ) { + QPoint comboPos; + comboPos = mapFromGlobal( d->listBox()->mapToGlobal(pos) ); + QRect arrowRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, + QStyle::SC_ComboBoxArrow, 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() & ( Qt::RightButton | Qt::LeftButton | Qt::MidButton ) ) == 0 && + style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking, &opt, this)) { + QWidget *mouseW = QApplication::widgetAt( e->globalPos(), true ); + if ( mouseW == d->listBox()->viewport() ) { //### + QMouseEvent m( QEvent::MouseMove, e->pos(), e->globalPos(), + Qt::NoButton, Qt::LeftButton ); + QApplication::sendEvent( object, &m ); //### Evil + return true; + } + } + + break; + case QEvent::MouseButtonRelease: + if ( d->listBox()->rect().contains( e->pos() ) ) { + QMouseEvent tmp( QEvent::MouseButtonDblClick, + e->pos(), e->button(), e->state() ) ; + // will hide popup + QApplication::sendEvent( object, &tmp ); + return true; + } else { + if ( d->mouseWasInsidePopup ) { + popDownListBox(); + } else { + d->arrowPressed = false; + if ( d->arrowDown ) { + d->arrowDown = false; + repaint( false ); + } + } + } + break; + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + if ( !d->listBox()->rect().contains( e->pos() ) ) { + QPoint globalPos = d->listBox()->mapToGlobal(e->pos()); + if ( QApplication::widgetAt( globalPos, true ) == this ) { + d->discardNextMousePress = true; + // avoid popping up again + } + popDownListBox(); + return true; + } + break; + case QEvent::KeyPress: + switch( ((QKeyEvent *)event)->key() ) { + case Qt::Key_Up: + case Qt::Key_Down: + if ( !(((QKeyEvent *)event)->state() & Qt::AltModifier) ) + break; + case Qt::Key_F4: + case Qt::Key_Escape: + if ( d->poppedUp ) { + popDownListBox(); + return true; + } + break; + case Qt::Key_Enter: + case Qt::Key_Return: + // work around QDialog's enter handling + return false; + default: + break; + } + break; + case QEvent::Hide: + popDownListBox(); + break; + default: + break; + } + } else if ( (!d->usingListBox() || style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) && + object == d->popup() ) { + QMouseEvent *e = (QMouseEvent*)event; + switch ( event->type() ) { + case QEvent::MouseButtonRelease: + if ( d->shortClick ) { + QMouseEvent tmp( QEvent::MouseMove, + e->pos(), e->button(), e->state() ) ; + // highlight item, but don't pop down: + QApplication::sendEvent( object, &tmp ); + return true; + } + break; + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + if ( !d->popup()->rect().contains( e->pos() ) ) { + d->poppedUp = false; + d->arrowDown = false; + // remove filter, event will take down popup: + d->popup()->removeEventFilter( this ); + // ### uglehack! + // call internalHighlight so the highlighed signal + // will be emitted at least as often as necessary. + // it may be called more often than necessary + internalHighlight( d->current ); + } + break; + case QEvent::Hide: + d->poppedUp = false; + break; + default: + break; + } + } + return QWidget::eventFilter( object, event ); +} + + +/*! + Returns the index of the first item \e after \a startingAt of + which \a prefix is a case-insensitive prefix. Returns -1 if no + items start with \a prefix. +*/ + +int Q3ComboBox::completionIndex( const QString & prefix, + int startingAt = 0 ) const +{ + int start = startingAt; + if ( start < 0 || start >= count() ) + start = 0; + if ( start >= count() ) + return -1; + QString match = prefix.lower(); + if ( match.length() < 1 ) + return start; + + QString current; + int i = start; + do { + current = text( i ).lower(); + if ( current.startsWith( match ) ) + return i; + i++; + if ( i == count() ) + i = 0; + } while ( i != start ); + return -1; +} + +int Q3ComboBox::sizeLimit() const +{ + return d ? d->sizeLimit : INT_MAX; +} + +void Q3ComboBox::setSizeLimit( int lines ) +{ + d->sizeLimit = lines; +} + + +int Q3ComboBox::maxCount() const +{ + return d ? d->maxCount : INT_MAX; +} + +void Q3ComboBox::setMaxCount( int count ) +{ + int l = this->count(); + while( --l > count ) + removeItem( l ); + d->maxCount = count; +} + +Q3ComboBox::Policy Q3ComboBox::insertionPolicy() const +{ + return d->p; +} + +void Q3ComboBox::setInsertionPolicy( Policy policy ) +{ + d->p = policy; +} + + + +/*! + Internal slot to keep the line editor up to date. +*/ + +void Q3ComboBox::returnPressed() +{ + QString s( d->ed->text() ); + + if ( s.isEmpty() ) + return; + + int c = 0; + bool doInsert = true; + if ( !d->duplicatesEnabled ) { + for ( int i = 0; i < count(); ++i ) { + if ( s == text( i ) ) { + doInsert = false; + c = i; + break; + } + } + } + + if ( doInsert ) { + if ( insertionPolicy() != NoInsert ) { + int cnt = count(); + while ( cnt >= d->maxCount ) { + removeItem( --cnt ); + } + } + + switch ( insertionPolicy() ) { + case InsertAtCurrent: + if (count() == 0) + insertItem(s); + else if ( s != text( currentItem() ) ) + changeItem( s, currentItem() ); + emit activated( currentItem() ); + emit activated( s ); + return; + case NoInsert: + emit activated( s ); + return; + case InsertAtTop: + c = 0; + break; + case InsertAtBottom: + c = count(); + break; + case InsertBeforeCurrent: + c = currentItem(); + break; + case InsertAfterCurrent: + c = count() == 0 ? 0 : currentItem() + 1; + break; + } + insertItem( s, c ); + } + + setCurrentItem( c ); + emit activated( c ); + emit activated( s ); +} + + +/*! + Enables the combobox if \a enable is true; otherwise disables it. + + \sa QWidget::enabled +*/ + +void Q3ComboBox::setEnabled( bool enable ) +{ + if ( !enable ) { + if ( d->usingListBox() ) { + popDownListBox(); + } else { + d->popup()->removeEventFilter( this ); + d->popup()->close(); + d->poppedUp = false; + } + } + QWidget::setEnabled( enable ); +} + + + +/*! + Applies the validator \a v to the combobox so that only text which + is valid according to \a v is accepted. + + This function does nothing if the combobox is not editable. + + \sa validator() clearValidator() QValidator +*/ + +void Q3ComboBox::setValidator( const QValidator * v ) +{ + if ( d && d->ed ) + d->ed->setValidator( v ); +} + + +/*! + Returns the validator which constrains editing for this combobox + if there is one; otherwise returns 0. + + \sa setValidator() clearValidator() QValidator +*/ + +const QValidator * Q3ComboBox::validator() const +{ + return d && d->ed ? d->ed->validator() : 0; +} + + +/*! + This slot is equivalent to setValidator( 0 ). +*/ + +void Q3ComboBox::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 Q3ComboBox assumes that newListBox->text(n) returns + non-null for 0 \<= n \< newListbox->count(). This assumption is + necessary because of the line edit in Q3ComboBox. +*/ + +void Q3ComboBox::setListBox( Q3ListBox * newListBox ) +{ + clear(); + + if ( d->usingListBox() ) { + delete d->listBox(); + } else { + delete d->popup(); + d->setPopupMenu(0, false); + } + + newListBox->reparent( this, Qt::WType_Popup, QPoint(0,0), false ); + d->setListBox( newListBox ); + d->listBox()->setFont( font() ); + d->listBox()->setPalette( palette() ); + d->listBox()->setVScrollBarMode(Q3ScrollView::AlwaysOff); + d->listBox()->setHScrollBarMode(Q3ScrollView::AlwaysOff); + d->listBox()->setFrameStyle( Q3Frame::Box | Q3Frame::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. + (Q3ComboBox can use QPopupMenu instead of QListBox.) Provided to + match setListBox(). + + \sa setListBox() +*/ + +Q3ListBox * Q3ComboBox::listBox() const +{ + return d && d->usingListBox() ? d->listBox() : 0; +} + +/*! + Returns the line edit, or 0 if there is no line edit. + + Only editable listboxes have a line editor. +*/ +QLineEdit* Q3ComboBox::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 Q3ComboBox::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 Q3ComboBox::setEditText( const QString &newText ) +{ + if ( d && d->ed ) { + d->updateLinedGeometry(); + d->ed->setText( newText ); + } +} + +void Q3ComboBox::setAutoCompletion( bool enable ) +{ + d->useCompletion = enable; + d->completeNow = false; +} + + +bool Q3ComboBox::autoCompletion() const +{ + return d->useCompletion; +} + +/*!\reimp + */ +void Q3ComboBox::styleChange( QStyle& s ) +{ + d->sizeHint = QSize(); // invalidate size hint... + if ( d->ed ) + d->updateLinedGeometry(); + QWidget::styleChange( s ); +} + +bool Q3ComboBox::editable() const +{ + return d->ed != 0; +} + +void Q3ComboBox::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(Qt::StrongFocus); + updateGeometry(); + update(); +} + + +void Q3ComboBox::setUpListBox() +{ + d->setListBox( new Q3ListBox( this, "in-combo", Qt::WType_Popup ) ); + d->listBox()->setFont( font() ); + d->listBox()->setPalette( palette() ); + d->listBox()->setVScrollBarMode( Q3ListBox::AlwaysOff ); + d->listBox()->setHScrollBarMode( Q3ListBox::AlwaysOff ); + d->listBox()->setFrameStyle( Q3Frame::Box | Q3Frame::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 Q3ComboBox::setUpLineEdit() +{ + if ( !d->ed ) + setLineEdit( new QLineEdit( this, "combo edit" ) ); +} + +/*! + Sets the line edit to use \a edit instead of the current line edit. +*/ + +void Q3ComboBox::setLineEdit( QLineEdit *edit ) +{ + if ( !edit ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( edit != 0 ); +#endif + return; + } + + edit->setText( currentText() ); + delete d->ed; + d->ed = edit; + + if ( edit->parent() != this ) + edit->reparent( this, QPoint(0,0), false ); + + connect (edit, SIGNAL(textChanged(QString)), + this, SIGNAL(textChanged(QString)) ); + connect( edit, SIGNAL(returnPressed()), SLOT(returnPressed()) ); + + edit->setFrame( false ); + d->updateLinedGeometry(); + edit->installEventFilter( this ); + setFocusProxy( edit ); + setFocusPolicy(Qt::StrongFocus); + setInputMethodEnabled( true ); + + if ( !d->usingListBox() ) + setUpListBox(); + + if ( isVisible() ) + edit->show(); + + updateGeometry(); + update(); +} + +/*! + Hides the combobox. + + \sa QWidget::hide() +*/ +void Q3ComboBox::hide() +{ + QWidget::hide(); + + if (listBox()) + listBox()->hide(); + else if (d->popup()) + d->popup()->hide(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_COMBOBOX diff --git a/src/qt3support/widgets/q3combobox.h b/src/qt3support/widgets/q3combobox.h new file mode 100644 index 0000000..f9a0093 --- /dev/null +++ b/src/qt3support/widgets/q3combobox.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3COMBOBOX_H +#define Q3COMBOBOX_H + +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_COMBOBOX + +class Q3StrList; +class QStringList; +class QLineEdit; +class QValidator; +class Q3ListBox; +class Q3ComboBoxData; +class QWheelEvent; + +class Q_COMPAT_EXPORT Q3ComboBox : public QWidget +{ + Q_OBJECT + Q_ENUMS( Policy ) + Q_PROPERTY( bool editable READ editable WRITE setEditable ) + Q_PROPERTY( int count READ count ) + Q_PROPERTY( QString currentText READ currentText WRITE setCurrentText DESIGNABLE false ) + Q_PROPERTY( int currentItem READ currentItem WRITE setCurrentItem ) + Q_PROPERTY( bool autoResize READ autoResize WRITE setAutoResize DESIGNABLE false ) + Q_PROPERTY( int sizeLimit READ sizeLimit WRITE setSizeLimit ) + Q_PROPERTY( int maxCount READ maxCount WRITE setMaxCount ) + Q_PROPERTY( Policy insertionPolicy READ insertionPolicy WRITE setInsertionPolicy ) + Q_PROPERTY( bool autoCompletion READ autoCompletion WRITE setAutoCompletion ) + Q_PROPERTY( bool duplicatesEnabled READ duplicatesEnabled WRITE setDuplicatesEnabled ) + +public: + Q3ComboBox( QWidget* parent=0, const char* name=0 ); + Q3ComboBox( bool rw, QWidget* parent=0, const char* name=0 ); + ~Q3ComboBox(); + + int count() const; + + void insertStringList( const QStringList &, int index=-1 ); + void insertStrList( const Q3StrList &, int index=-1 ); + void insertStrList( const Q3StrList *, int index=-1 ); + void insertStrList( const char **, int numStrings=-1, int index=-1); + + void insertItem( const QString &text, int index=-1 ); + void insertItem( const QPixmap &pixmap, int index=-1 ); + void insertItem( const QPixmap &pixmap, const QString &text, int index=-1 ); + + void removeItem( int index ); + + int currentItem() const; + virtual void setCurrentItem( int index ); + + QString currentText() const; + virtual void setCurrentText( const QString& ); + + QString text( int index ) const; + const QPixmap *pixmap( int index ) const; + + void changeItem( const QString &text, int index ); + void changeItem( const QPixmap &pixmap, int index ); + void changeItem( const QPixmap &pixmap, const QString &text, int index ); + + bool autoResize() const; + virtual void setAutoResize( bool ); + QSize sizeHint() const; + + void setPalette( const QPalette & ); + void setFont( const QFont & ); + void setEnabled( bool ); + + virtual void setSizeLimit( int ); + int sizeLimit() const; + + virtual void setMaxCount( int ); + int maxCount() const; + + enum Policy { NoInsertion, + AtTop, + AtCurrent, + AtBottom, + AfterCurrent, + BeforeCurrent, + NoInsert = NoInsertion, + InsertAtTop = AtTop, + InsertAtCurrent = AtCurrent, + InsertAtBottom = AtBottom, + InsertAfterCurrent = AfterCurrent, + InsertBeforeCurrent = BeforeCurrent + }; + + virtual void setInsertionPolicy( Policy policy ); + Policy insertionPolicy() const; + + virtual void setValidator( const QValidator * ); + const QValidator * validator() const; + + virtual void setListBox( Q3ListBox * ); + Q3ListBox * listBox() const; + + virtual void setLineEdit( QLineEdit *edit ); + QLineEdit* lineEdit() const; + + virtual void setAutoCompletion( bool ); + bool autoCompletion() const; + + bool eventFilter( QObject *object, QEvent *event ); + + void setDuplicatesEnabled( bool enable ); + bool duplicatesEnabled() const; + + bool editable() const; + void setEditable( bool ); + + virtual void popup(); + + void hide(); + +public Q_SLOTS: + void clear(); + void clearValidator(); + void clearEdit(); + virtual void setEditText( const QString &); + +Q_SIGNALS: + void activated( int index ); + void highlighted( int index ); + void activated( const QString &); + void highlighted( const QString &); + void textChanged( const QString &); + +private Q_SLOTS: + void internalActivate( int ); + void internalHighlight( int ); + void internalClickTimeout(); + void returnPressed(); + +protected: + void paintEvent( QPaintEvent * ); + void resizeEvent( QResizeEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + void keyPressEvent( QKeyEvent *e ); + void focusInEvent( QFocusEvent *e ); + void focusOutEvent( QFocusEvent *e ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( QWheelEvent *e ); +#endif + void styleChange( QStyle& ); + + void updateMask(); + +private: + void setUpListBox(); + void setUpLineEdit(); + void popDownListBox(); + void reIndex(); + void currentChanged(); + int completionIndex( const QString &, int ) const; + + Q3ComboBoxData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + Q3ComboBox( const Q3ComboBox & ); + Q3ComboBox &operator=( const Q3ComboBox & ); +#endif +}; + + +#endif // QT_NO_COMBOBOX + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3COMBOBOX_H diff --git a/src/qt3support/widgets/q3datetimeedit.cpp b/src/qt3support/widgets/q3datetimeedit.cpp new file mode 100644 index 0000000..3d02687 --- /dev/null +++ b/src/qt3support/widgets/q3datetimeedit.cpp @@ -0,0 +1,2826 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3datetimeedit.h" + +#ifndef QT_NO_DATETIMEEDIT + +#include <private/q3richtext_p.h> +#include "qevent.h" +#include "q3rangecontrol.h" +#include "qapplication.h" +#include "qpixmap.h" +#include "qlist.h" +#include "qstring.h" +#include "qstyle.h" + +#if defined(Q_WS_WIN) +#include "qt_windows.h" +#endif + +QT_BEGIN_NAMESPACE + +#define QDATETIMEEDIT_HIDDEN_CHAR QLatin1Char('0') + +class Q_COMPAT_EXPORT QNumberSection +{ +public: + QNumberSection(int selStart = 0, int selEnd = 0, bool separat = true, int actual = -1) + : selstart(selStart), selend(selEnd), act(actual), sep(separat) + {} + int selectionStart() const { return selstart; } + void setSelectionStart(int s) { selstart = s; } + int selectionEnd() const { return selend; } + void setSelectionEnd(int s) { selend = s; } + int width() const { return selend - selstart; } + int index() const { return act; } + bool separator() const { return sep; } + Q_DUMMY_COMPARISON_OPERATOR(QNumberSection) +private: + signed int selstart :12; + signed int selend :12; + signed int act :7; + bool sep :1; +}; + +static QString *lDateSep = 0; +static QString *lTimeSep = 0; +static bool lAMPM = false; +static QString *lAM = 0; +static QString *lPM = 0; +static Q3DateEdit::Order lOrder = Q3DateEdit::YMD; +static int refcount = 0; + +static void cleanup() +{ + delete lDateSep; + lDateSep = 0; + delete lTimeSep; + lTimeSep = 0; + delete lAM; + lAM = 0; + delete lPM; + lPM = 0; +} + +/*! +\internal +try to get the order of DMY and the date/time separator from the locale settings +*/ +static void readLocaleSettings() +{ + int dpos, mpos, ypos; + cleanup(); + + lDateSep = new QString(); + lTimeSep = new QString(); + +#if defined(Q_WS_WIN) + QT_WA({ + TCHAR data[10]; + GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, data, 10); + *lDateSep = QString::fromUtf16((ushort*)data); + GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIME, data, 10); + *lTimeSep = QString::fromUtf16((ushort*)data); + GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, data, 10); + lAMPM = QString::fromUtf16((ushort*)data).toInt()==0; + GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_S1159, data, 10); + QString am = QString::fromUtf16((ushort*)data); + if (!am.isEmpty()) + lAM = new QString(am); + GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_S2359, data, 10); + QString pm = QString::fromUtf16((ushort*)data); + if (!pm.isEmpty() ) + lPM = new QString(pm); + } , { + char data[10]; + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDATE, (char*)&data, 10); + *lDateSep = QString::fromLocal8Bit(data); + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STIME, (char*)&data, 10); + *lTimeSep = QString::fromLocal8Bit(data); + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ITIME, (char*)&data, 10); + lAMPM = QString::fromLocal8Bit(data).toInt()==0; + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_S1159, (char*)&data, 10); + QString am = QString::fromLocal8Bit(data); + if (!am.isEmpty()) + lAM = new QString(am); + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_S2359, (char*)&data, 10); + QString pm = QString::fromLocal8Bit(data); + if (!pm.isEmpty()) + lPM = new QString(pm); + }); +#else + *lDateSep = QLatin1Char('-'); + *lTimeSep = QLatin1Char(':'); +#endif + QString d = QDate(1999, 11, 22).toString(Qt::LocalDate); + dpos = d.indexOf(QLatin1String("22")); + mpos = d.indexOf(QLatin1String("11")); + ypos = d.indexOf(QLatin1String("99")); + if (dpos > -1 && mpos > -1 && ypos > -1) { + // test for DMY, MDY, YMD, YDM + if (dpos < mpos && mpos < ypos) { + lOrder = Q3DateEdit::DMY; + } else if (mpos < dpos && dpos < ypos) { + lOrder = Q3DateEdit::MDY; + } else if (ypos < mpos && mpos < dpos) { + lOrder = Q3DateEdit::YMD; + } else if (ypos < dpos && dpos < mpos) { + lOrder = Q3DateEdit::YDM; + } else { + // cannot determine the dateformat - use the default + return; + } + + // this code needs to change if new formats are added + +#ifndef Q_WS_WIN + QString sep = d.mid(qMin(dpos, mpos) + 2, QABS(dpos - mpos) - 2); + if (d.count(sep) == 2) { + *lDateSep = sep; + } +#endif + } + +#ifndef Q_WS_WIN + QString t = QTime(11, 22, 33).toString(Qt::LocalDate); + dpos = t.indexOf(QLatin1String("11")); + mpos = t.indexOf(QLatin1String("22")); + ypos = t.indexOf(QLatin1String("33")); + // We only allow hhmmss + if (dpos > -1 && dpos < mpos && mpos < ypos) { + QString sep = t.mid(dpos + 2, mpos - dpos - 2); + if (sep == t.mid(mpos + 2, ypos - mpos - 2)) { + *lTimeSep = sep; + } + } +#endif +} + +static Q3DateEdit::Order localOrder() { + if (!lDateSep) { + readLocaleSettings(); + } + return lOrder; +} + +static QString localDateSep() { + if (!lDateSep) { + readLocaleSettings(); + } + return *lDateSep; +} + +static QString localTimeSep() { + if (!lTimeSep) { + readLocaleSettings(); + } + return *lTimeSep; +} + +class Q3DateTimeEditorPrivate +{ +public: + Q3DateTimeEditorPrivate() + : frm(true), + parag(new Q3TextParagraph(0, 0, 0, false)), + focusSec(0) + { + parag->formatter()->setWrapEnabled(false); + cursor = new Q3TextCursor(0); + cursor->setParagraph(parag); + offset = 0; + sep = localDateSep(); + refcount++; + } + ~Q3DateTimeEditorPrivate() + { + delete parag; + delete cursor; + if (!--refcount) + cleanup(); + } + + void appendSection(const QNumberSection& sec) + { + sections.append(sec); + + } + void clearSections() + { + sections.clear(); + } + void setSectionSelection(int sec, int selstart, int selend) + { + if (sec < 0 || sec >= sections.count()) + return; + sections[sec].setSelectionStart(selstart); + sections[sec].setSelectionEnd(selend); + } + uint sectionCount() const { return (uint)sections.count(); } + void setSeparator(const QString& s) { sep = s; } + QString separator() const { return sep; } + + void setFrame(bool f) { frm = f; } + bool frame() const { return frm; } + + int focusSection() const { return focusSec; } + int section(const QPoint& p) + { + cursor->place(p + QPoint(offset, 0), parag); + int idx = cursor->index(); + for (int i = 0; i < sections.count(); ++i) { + if (idx >= sections[i].selectionStart() && + idx <= sections[i].selectionEnd()) + return i; + } + return -1; + } + QNumberSection section(int idx) const + { + return sections[idx]; + } + bool setFocusSection(int idx) + { + if (idx > (int)sections.count()-1 || idx < 0) + return false; + if (idx != focusSec) { + focusSec = idx; + applyFocusSelection(); + return true; + } + return false; + } + + bool inSectionSelection(int idx) + { + for (int i = 0; i < sections.count(); ++i) { + if (idx >= sections[i].selectionStart() && + idx <= sections[i].selectionEnd()) + return true; + } + return false; + } + + void paint(const QString& txt, bool focus, QPainter& p, + const QPalette&pal, const QRect& rect, QStyle *style) + { + int fw = 0; + if (frm) + fw = style->pixelMetric(QStyle::PM_DefaultFrameWidth); + + parag->truncate(0); + parag->append(txt); + if (!focus) + parag->removeSelection(Q3TextDocument::Standard); + else { + applyFocusSelection(); + } + + /* color all QDATETIMEEDIT_HIDDEN_CHAR chars to background color */ + Q3TextFormat *fb = parag->formatCollection()->format(p.font(), + pal.base().color()); + Q3TextFormat *nf = parag->formatCollection()->format(p.font(), + pal.text().color()); + for (int i = 0; i < txt.length(); ++i) { + parag->setFormat(i, 1, nf); + if (inSectionSelection(i)) + continue; + if (txt.at(i) == QDATETIMEEDIT_HIDDEN_CHAR) + parag->setFormat(i, 1, fb); + else + parag->setFormat(i, 1, nf); + } + fb->removeRef(); + nf->removeRef(); + + QRect r(rect.x(), rect.y(), rect.width() - 2 * (2 + fw), rect.height()); + parag->pseudoDocument()->docRect = r; + parag->invalidate(0); + parag->format(); + + int xoff = 2 + fw - offset; + int yoff = (rect.height() - parag->rect().height() + 1) / 2; + if (yoff < 0) + yoff = 0; + + p.translate(xoff, yoff); + parag->paint(p, pal, 0, true); + if (frm) + p.translate(-xoff, -yoff); + } + + void resize(const QSize& size) { sz = size; } + + int mapSection(int sec) + { + return (sec >= 0 && sec < sections.count() ? sections[sec].index() : -1); + } + +protected: + void applyFocusSelection() + { + if (focusSec > -1 && focusSec < sections.count()) { + int selstart = sections[focusSec].selectionStart(); + int selend = sections[focusSec].selectionEnd(); + parag->setSelection(Q3TextDocument::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; + Q3TextParagraph *parag; + Q3TextCursor *cursor; + QSize sz; + int focusSec; + QList< QNumberSection > sections; + QString sep; + int offset; +}; + +class Q3DateTimeEditor : public QWidget +{ + Q_OBJECT +public: + Q3DateTimeEditor(Q3DateTimeEditBase *widget, QWidget *parent, const char* name=0); + ~Q3DateTimeEditor(); + + void setControlWidget(Q3DateTimeEditBase * widget); + Q3DateTimeEditBase * controlWidget() const; + + void setSeparator(const QString& s); + QString separator() const; + + int focusSection() const; + bool setFocusSection(int s); + void appendSection(const QNumberSection& sec); + void clearSections(); + void setSectionSelection(int sec, int selstart, int selend); + bool eventFilter(QObject *o, QEvent *e); + int sectionAt(const QPoint &p); + int mapSection(int sec); + +protected: + void init(); + bool event(QEvent *e); + void resizeEvent(QResizeEvent *); + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *e); + +private: + Q3DateTimeEditBase* cw; + Q3DateTimeEditorPrivate* d; +}; + +class QDateTimeSpinWidget : public Q3SpinWidget +{ + Q_OBJECT +public: + QDateTimeSpinWidget(QWidget *parent, const char *name) + : Q3SpinWidget(parent, name) + { + } + + void changeEvent(QEvent *e) + { + if (e->type() == QEvent::EnabledChange && isEnabled()) { + Q3DateEdit *de = qobject_cast<Q3DateEdit*>(parentWidget()); + if (de) { + setUpEnabled(de->date() < de->maxValue()); + setDownEnabled(de->date() > de->minValue()); + } else { + setUpEnabled(true); + setDownEnabled(true); + } + } + } + void enabledChange(bool notenabled) + { + Q3DateEdit *de = qobject_cast<Q3DateEdit*>(parentWidget()); + if (de && !notenabled) { + setUpEnabled(de->date() < de->maxValue()); + setDownEnabled(de->date() > de->minValue()); + } else { + setUpEnabled(!notenabled); + setDownEnabled(!notenabled); + } + } + + +protected: +#ifndef QT_NO_WHEELEVENT + void wheelEvent(QWheelEvent *e) + { + Q3DateTimeEditor *editor = qobject_cast<Q3DateTimeEditor*>(editWidget()); + Q_ASSERT(editor); + if (!editor) + return; + + int section = editor->sectionAt(e->pos()); + editor->setFocusSection(section); + + if (section == -1) + return; + Q3SpinWidget::wheelEvent(e); + } +#endif +}; + +/*! + Constructs an empty datetime editor with parent \a parent and + called \a name. +*/ +Q3DateTimeEditor::Q3DateTimeEditor(Q3DateTimeEditBase *widget, QWidget *parent, const char * name) + : QWidget(parent, name) +{ + d = new Q3DateTimeEditorPrivate(); + cw = widget; + init(); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3DateTimeEditor::~Q3DateTimeEditor() +{ + delete d; +} + +/*! \internal + +*/ + +void Q3DateTimeEditor::init() +{ + setBackgroundRole(QPalette::Base); + setFocusSection(-1); + installEventFilter(this); + setFocusPolicy(Qt::WheelFocus); +} + + +/*! \reimp + +*/ + +bool Q3DateTimeEditor::event(QEvent *e) +{ + if (e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut) { + if (e->type() == QEvent::FocusOut) + qApp->sendEvent(cw, e); + update(rect()); + } else if (e->type() == QEvent::ShortcutOverride) { + QKeyEvent* ke = (QKeyEvent*) e; + switch (ke->key()) { + case Qt::Key_Delete: + case Qt::Key_Backspace: + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Left: + case Qt::Key_Right: + ke->accept(); + default: + break; + } + } + return QWidget::event(e); +} + +/*! \reimp + +*/ + +void Q3DateTimeEditor::resizeEvent(QResizeEvent *e) +{ + d->resize(e->size()); + QWidget::resizeEvent(e); +} + + +/*! \reimp + +*/ + +void Q3DateTimeEditor::paintEvent(QPaintEvent *) +{ + QString txt; + for (uint i = 0; i < d->sectionCount(); ++i) { + txt += cw->sectionFormattedText(i); + if (i < d->sectionCount()-1) { + if (d->section(i+1).separator()) + txt += d->separator(); + else + txt += QLatin1Char(' '); + } + } + + QPainter p(this); + const QBrush &bg = palette().brush(isEnabled() ? QPalette::Base : QPalette::Window); + p.fillRect(0, 0, width(), height(), bg); + d->paint(txt, hasFocus(), p, palette(), rect(), style()); +} + + +/*! + Returns the section index at point \a p. +*/ +int Q3DateTimeEditor::sectionAt(const QPoint &p) +{ + return d->section(p); +} + +int Q3DateTimeEditor::mapSection(int sec) +{ + return d->mapSection(sec); +} + + +/*! \reimp + +*/ + +void Q3DateTimeEditor::mousePressEvent(QMouseEvent *e) +{ + QPoint p(e->pos().x(), 0); + int sec = sectionAt(p); + if (sec != -1) { + cw->setFocusSection(sec); + repaint(rect()); + } +} + +/*! \reimp + +*/ +bool Q3DateTimeEditor::eventFilter(QObject *o, QEvent *e) +{ + if (o == this) { + if (e->type() == QEvent::KeyPress) { + QKeyEvent *ke = (QKeyEvent*)e; + switch (ke->key()) { + case Qt::Key_Right: + if (d->focusSection() < (int)d->sectionCount()-1) { + if (cw->setFocusSection(focusSection()+1)) + repaint(rect()); + } + return true; + case Qt::Key_Left: + if (d->focusSection() > 0) { + if (cw->setFocusSection(focusSection()-1)) + repaint(rect()); + } + return true; + case Qt::Key_Up: + cw->stepUp(); + return true; + case Qt::Key_Down: + cw->stepDown(); + return true; + case Qt::Key_Backspace: + if (qobject_cast<Q3DateEdit*>(cw)) + ((Q3DateEdit*)cw)->removeFirstNumber(d->focusSection()); + else if (qobject_cast<Q3TimeEdit*>(cw)) + ((Q3TimeEdit*)cw)->removeFirstNumber(d->focusSection()); + return true; + case Qt::Key_Delete: + cw->removeLastNumber(d->focusSection()); + return true; + case Qt::Key_Tab: + case Qt::Key_BackTab: { + if (ke->state() == Qt::ControlButton) + return false; + QWidget *w = this; + bool hadDateEdit = false; + while (w) { + if (qobject_cast<QDateTimeSpinWidget*>(w) || qobject_cast<Q3DateTimeEdit*>(w)) + break; + hadDateEdit = hadDateEdit || qobject_cast<Q3DateEdit*>(w); + w = w->parentWidget(); + } + if (w) { + if (!qobject_cast<Q3DateTimeEdit*>(w)) { + w = w->parentWidget(); + } else { + Q3DateTimeEdit *ed = (Q3DateTimeEdit*)w; + if (hadDateEdit && ke->key() == Qt::Key_Tab) { + ed->timeEdit()->setFocus(); + return true; + } else if (!hadDateEdit && ke->key() == Qt::Key_BackTab) { + ed->dateEdit()->setFocus(); + return true; + } else { + while (w && !qobject_cast<Q3DateTimeEdit*>(w)) + w = w->parentWidget(); + } + } + qApp->sendEvent(w, e); + return true; + } + } break; + default: + QString txt = ke->text().toLower(); + 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()); + } + return true; + } else if (!txt.isEmpty() && qobject_cast<Q3TimeEdit*>(cw) && focusSection() == (int) d->sectionCount()-1) { + // the first character of the AM/PM indicator toggles if the section has focus + Q3TimeEdit *te = (Q3TimeEdit*)cw; + QTime time = te->time(); + if (lAMPM && lAM && lPM && (te->display()&Q3TimeEdit::AMPM)) { + if (txt[0] == (*lAM).toLower()[0] && time.hour() >= 12) { + time.setHMS(time.hour()-12, time.minute(), time.second(), time.msec()); + te->setTime(time); + } else if (txt[0] == (*lPM).toLower()[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 Q3DateTimeEditor::appendSection(const QNumberSection& sec) +{ + d->appendSection(sec); +} + +/*! + Removes all sections from the editor. +*/ + +void Q3DateTimeEditor::clearSections() +{ + d->clearSections(); +} + +/*! + Sets the selection of \a sec to start at \a selstart and end at \a + selend. +*/ + +void Q3DateTimeEditor::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 Q3DateTimeEditor::setSeparator(const QString& s) +{ + d->setSeparator(s); + update(); +} + + +/*! + Returns the editor's separator. +*/ + +QString Q3DateTimeEditor::separator() const +{ + return d->separator(); +} + +/*! + Returns the number of the section that has focus. +*/ + +int Q3DateTimeEditor::focusSection() const +{ + return d->focusSection(); +} + + +/*! + Sets the focus to section \a sec. If \a sec does not exist, + nothing happens. +*/ + +bool Q3DateTimeEditor::setFocusSection(int sec) +{ + return d->setFocusSection(sec); +} + +/*! + \class Q3DateTimeEditBase + \brief The Q3DateTimeEditBase class provides an abstraction for date and edit editors. + + \compat + + Small abstract class that provides some functions that are common + for both Q3DateEdit and Q3TimeEdit. It is used internally by + Q3DateTimeEditor. +*/ + +/*! + \fn Q3DateTimeEditBase::Q3DateTimeEditBase(QWidget *, const char*) + \internal +*/ + +/*! + \fn Q3DateTimeEditBase::setFocusSection(int) + \internal +*/ + +/*! \fn QString Q3DateTimeEditBase::sectionFormattedText(int sec) + \internal + + Pure virtual function which returns the formatted text of section \a + sec. + +*/ + +/*! \fn void Q3DateTimeEditBase::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 Q3DateTimeEditBase::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 Q3DateTimeEditBase::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 Q3DateTimeEditBase::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 Q3DateEditPrivate +{ +public: + int y; + int m; + int d; + // remembers 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; + Q3DateEdit::Order ord; + bool overwrite; + bool adv; + int timerId; + bool typing; + QDate min; + QDate max; + bool changed; + Q3DateTimeEditor *ed; + Q3SpinWidget *controls; +}; + + +/*! + \class Q3DateEdit + \brief The Q3DateEdit class provides a date editor. + + \compat + + Q3DateEdit 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 Q3DateEdit + 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 Q3DateEdit be initialised + with a date, e.g. + + \snippet doc/src/snippets/code/src_qt3support_widgets_q3datetimeedit.cpp 0 + + Here we've created a new Q3DateEdit object initialised with today's + date and restricted the valid date range to today plus or minus + 365 days. We've set the order to month, day, year. If the auto + advance property is true (as we've set it here) when the user + completes a section of the date, e.g. enters two digits for the + month, they are automatically taken to the next section. + + The maximum and minimum values for a date value in the date editor + default to the maximum and minimum values for a QDate. You can + change this by calling setMinValue(), setMaxValue() or setRange(). + + Terminology: A Q3DateEdit widget comprises three 'sections', one + each for the year, month and day. You can change the separator + character using Q3DateTimeEditor::setSeparator(), by default the + separator will be taken from the systems settings. If that is + not possible, it defaults to "-". + + \img datetimewidgets.png Date Time Widgets + + \sa QDate Q3TimeEdit Q3DateTimeEdit +*/ + +/*! + \enum Q3DateEdit::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) + \omitvalue YDM +*/ + +/*! + \enum Q3TimeEdit::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. +*/ + +Q3DateEdit::Q3DateEdit(QWidget * parent, const char * name) + : Q3DateTimeEditBase(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. +*/ + +Q3DateEdit::Q3DateEdit(const QDate& date, QWidget * parent, const char * name) + : Q3DateTimeEditBase(parent, name) +{ + init(); + setDate(date); +} + +/*! \internal +*/ +void Q3DateEdit::init() +{ + d = new Q3DateEditPrivate(); + d->controls = new QDateTimeSpinWidget(this, 0); + d->ed = new Q3DateTimeEditor(this, d->controls); + 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(QDate)), SLOT(updateButtons())); + d->ed->appendSection(QNumberSection(0,4)); + d->ed->appendSection(QNumberSection(5,7)); + d->ed->appendSection(QNumberSection(8,10)); + + d->yearSection = -1; + d->monthSection = -1; + d->daySection = -1; + + d->y = 0; + d->m = 0; + d->d = 0; + d->dayCache = 0; + setOrder(localOrder()); + setFocusSection(0); + d->overwrite = true; + d->adv = false; + d->timerId = 0; + d->typing = false; + d->min = QDate(1752, 9, 14); + d->max = QDate(8000, 12, 31); + d->changed = false; + + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + + refcount++; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3DateEdit::~Q3DateEdit() +{ + delete d; + if (!--refcount) + cleanup(); +} + +/*! + \property Q3DateEdit::minValue + + \brief the editor's minimum value + + Setting the minimum date value is equivalent to calling + Q3DateEdit::setRange(\e d, maxValue()), where \e d is the minimum + date. The default minimum date is 1752-09-14. + + \sa maxValue setRange() +*/ + +QDate Q3DateEdit::minValue() const +{ + return d->min; +} + +/*! + \property Q3DateEdit::maxValue + + \brief the editor's maximum value + + Setting the maximum date value for the editor is equivalent to + calling Q3DateEdit::setRange(minValue(), \e d), where \e d is the + maximum date. The default maximum date is 8000-12-31. + + \sa minValue setRange() +*/ + +QDate Q3DateEdit::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 Q3DateEdit::setRange(const QDate& min, const QDate& max) +{ + if (min.isValid()) + d->min = min; + if (max.isValid()) + d->max = max; +} + +/*! + Sets the separator to \a s. Note that currently only the first + character of \a s is used. +*/ + +void Q3DateEdit::setSeparator(const QString& s) +{ + d->ed->setSeparator(s); +} + +/*! + Returns the editor's separator. +*/ + +QString Q3DateEdit::separator() const +{ + return d->ed->separator(); +} + + +/*! + Enables/disables the push buttons according to the min/max date + for this widget. +*/ + +void Q3DateEdit::updateButtons() +{ + if (!isEnabled()) + return; + + bool upEnabled = date() < maxValue(); + bool downEnabled = date() > minValue(); + + d->controls->setUpEnabled(upEnabled); + d->controls->setDownEnabled(downEnabled); +} + +/*! \reimp + */ +void Q3DateEdit::resizeEvent(QResizeEvent *) +{ + d->controls->resize(width(), height()); +} + +/*! \reimp + +*/ +QSize Q3DateEdit::sizeHint() const +{ + ensurePolished(); + QFontMetrics fm(font()); + int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this); + int h = qMax(fm.lineSpacing(), 14) + 2; + int w = 2 + fm.width(QLatin1Char('9')) * 8 + fm.width(d->ed->separator()) * 2 + d->controls->upRect().width() + fw * 4; + + return QSize(w, qMax(h + fw * 2,20)).expandedTo(QApplication::globalStrut()); +} + +/*! \reimp + +*/ +QSize Q3DateEdit::minimumSizeHint() const +{ + return sizeHint(); +} + + +/*! + Returns the formatted number for section \a sec. This will + correspond to either the year, month or day section, depending on + the current display order. + + \sa setOrder() +*/ + +QString Q3DateEdit::sectionFormattedText(int sec) +{ + QString txt; + txt = sectionText(sec); + if (d->typing && sec == d->ed->focusSection()) + d->ed->setSectionSelection(sec, sectionOffsetEnd(sec) - txt.length(), + sectionOffsetEnd(sec)); + else + d->ed->setSectionSelection(sec, sectionOffsetEnd(sec) - sectionLength(sec), + sectionOffsetEnd(sec)); + txt = txt.rightJustified(sectionLength(sec), QDATETIMEEDIT_HIDDEN_CHAR); + return txt; +} + + +/*! + Returns the desired length (number of digits) of section \a sec. + This will correspond to either the year, month or day section, + depending on the current display order. + + \sa setOrder() +*/ + +int Q3DateEdit::sectionLength(int sec) const +{ + int val = 0; + if (sec == d->yearSection) { + val = 4; + } else if (sec == d->monthSection) { + val = 2; + } else if (sec == d->daySection) { + val = 2; + } + return val; +} + +/*! + Returns the text of section \a sec. This will correspond to either + the year, month or day section, depending on the current display + order. + + \sa setOrder() +*/ + +QString Q3DateEdit::sectionText(int sec) const +{ + int val = 0; + if (sec == d->yearSection) { + val = d->y; + } else if (sec == d->monthSection) { + val = d->m; + } else if (sec == d->daySection) { + val = d->d; + } + return QString::number(val); +} + +/*! \internal + + Returns the end of the section offset \a sec. + +*/ + +int Q3DateEdit::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 Q3DateEdit::order + \brief the order in which the year, month and day appear + + The default order is locale dependent. + + \sa Order +*/ + +void Q3DateEdit::setOrder(Q3DateEdit::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()); +} + + +Q3DateEdit::Order Q3DateEdit::order() const +{ + return d->ord; +} + + +/*! \reimp + +*/ +void Q3DateEdit::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()); +} + + + +/*! \reimp + +*/ + +void Q3DateEdit::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()); +} + +/*! + Sets the year to \a year, which must be a valid year. The range + currently supported is from 1752 to 8000. + + \sa QDate +*/ + +void Q3DateEdit::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 Q3DateEdit::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 Q3DateEdit::setDay(int day) +{ + if (day < 1) + day = 1; + if (day > 31) + day = 31; + if (d->m > 0 && d->y > 1752) { + while (!QDate::isValid(d->y, d->m, day)) + --day; + if (!outOfRange(d->y, d->m, day)) + d->d = day; + } else if (d->m > 0) { + if (day > 0 && day < 32) { + if (!outOfRange(d->y, d->m, day)) + d->d = day; + } + } + d->dayCache = d->d; +} + + +/*! + \property Q3DateEdit::date + \brief the editor's date value. + + If the date property is not valid, the editor displays all zeroes + and Q3DateEdit::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 Q3DateEdit::setDate(const QDate& date) +{ + if (!date.isValid()) { + d->y = 0; + d->m = 0; + d->d = 0; + d->dayCache = 0; + } else { + if (date > maxValue() || date < minValue()) + return; + d->y = date.year(); + d->m = date.month(); + d->d = date.day(); + d->dayCache = d->d; + emit valueChanged(date); + } + d->changed = false; + d->ed->repaint(d->ed->rect()); +} + +QDate Q3DateEdit::date() const +{ + if (QDate::isValid(d->y, d->m, d->d)) + return QDate(d->y, d->m, d->d); + return QDate(); +} + +/*! \internal + + Returns true if \a y, \a m, \a d is out of range, otherwise returns + false. + + \sa setRange() + +*/ + +bool Q3DateEdit::outOfRange(int y, int m, int d) const +{ + if (QDate::isValid(y, m, d)) { + QDate currentDate(y, m, d); + if (currentDate > maxValue() || + currentDate < minValue()) { + //## outOfRange should set overwrite? + return true; + } + return false; + } + return false; /* assume ok */ +} + +/*! \reimp + +*/ + +void Q3DateEdit::addNumber(int sec, int num) +{ + if (sec == -1) + return; + if (d->timerId) + killTimer(d->timerId); + d->timerId = 0; + bool overwrite = false; + bool accepted = false; + d->typing = true; + QString txt; + if (sec == d->yearSection) { + txt = QString::number(d->y); + if (d->overwrite || txt.length() == 4) { + accepted = true; + d->y = num; + } else { + txt += QString::number(num); + if (txt.length() == 4 ) { + const int val = qBound(1792, txt.toInt(), 8000); + if (outOfRange(val, d->m, d->d)) { + txt = QString::number(d->y); + } else { + accepted = true; + d->y = val; + } + } else { + accepted = true; + d->y = txt.toInt(); + } + if (d->adv && txt.length() == 4) { + d->ed->setFocusSection(d->ed->focusSection()+1); + overwrite = true; + } + } + } else if (sec == d->monthSection) { + txt = QString::number(d->m); + if (d->overwrite || txt.length() == 2) { + accepted = true; + d->m = num; + } else { + txt += QString::number(num); + int temp = txt.toInt(); + if (temp > 12) + temp = num; + if (outOfRange(d->y, temp, d->d)) + txt = QString::number(d->m); + else { + accepted = true; + d->m = temp; + } + if (d->adv && txt.length() == 2) { + d->ed->setFocusSection(d->ed->focusSection()+1); + overwrite = true; + } + } + } else if (sec == d->daySection) { + txt = QString::number(d->d); + if (d->overwrite || txt.length() == 2) { + accepted = true; + d->d = num; + d->dayCache = d->d; + } else { + txt += QString::number(num); + int temp = txt.toInt(); + if (temp > 31) + temp = num; + if (outOfRange(d->y, d->m, temp)) + txt = QString::number(d->d); + else { + accepted = true; + d->d = temp; + d->dayCache = d->d; + } + if (d->adv && txt.length() == 2) { + d->ed->setFocusSection(d->ed->focusSection()+1); + overwrite = true; + } + } + } + if (accepted) { + d->changed = false; + emit valueChanged(date()); + } + d->overwrite = overwrite; + d->timerId = startTimer(qApp->doubleClickInterval()*4); + d->ed->repaint(d->ed->rect()); +} + + +/*! \reimp + +*/ + +bool Q3DateEdit::setFocusSection(int s) +{ + if (s != d->ed->focusSection()) { + if (d->timerId) + killTimer(d->timerId); + d->timerId = 0; + 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 Q3DateEdit::fix() +{ + bool changed = false; + int currentYear = QDate::currentDate().year(); + int year = d->y; + if (year < 100) { + int currentCentury = currentYear / 100; + year += currentCentury * 100; + if (currentYear > year) { + if (currentYear > year + 70) + year += 100; + } else { + if (year >= currentYear + 30) + year -= 100; + } + changed = true; + } else if (year < 1000) { + int currentMillennium = currentYear / 10; + year += currentMillennium * 10; + changed = true; + } else if (d->d == 0) { + d->d = 1; + changed = true; + } else if (d->m == 0) { + d->m = 1; + changed = true; + } + if (outOfRange(year, d->m, d->d)) { + if (minValue().isValid() && date() < minValue()) { + d->d = minValue().day(); + d->dayCache = d->d; + d->m = minValue().month(); + d->y = minValue().year(); + } + if (date() > maxValue()) { + d->d = maxValue().day(); + d->dayCache = d->d; + d->m = maxValue().month(); + d->y = maxValue().year(); + } + changed = true; + } else if (changed) + setYear(year); + if (changed) { + emit valueChanged(date()); + d->changed = false; + } +} + + +/*! \reimp + +*/ + +bool Q3DateEdit::event(QEvent *e) +{ + if(e->type() == QEvent::FocusOut) { + d->typing = false; + fix(); + // the following can't be done in fix() because fix() called + // from all over the place and it will break the old behaviour + if (!QDate::isValid(d->y, d->m, d->d)) { + d->dayCache = d->d; + int i = d->d; + for (; i > 0; i--) { + d->d = i; + if (QDate::isValid(d->y, d->m, d->d)) + break; + } + d->changed = true; + } + if (d->changed) { + emit valueChanged(date()); + d->changed = false; + } + } else if (e->type() == QEvent::LocaleChange) { + readLocaleSettings(); + d->ed->setSeparator(localDateSep()); + setOrder(localOrder()); + } + return Q3DateTimeEditBase::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 Q3DateEdit::removeFirstNumber(int sec) +{ + if (sec == -1) + return; + QString txt; + if (sec == d->yearSection) { + txt = QString::number(d->y); + txt = txt.mid(1, txt.length()) + QLatin1Char('0'); + d->y = txt.toInt(); + } else if (sec == d->monthSection) { + txt = QString::number(d->m); + txt = txt.mid(1, txt.length()) + QLatin1Char('0'); + d->m = txt.toInt(); + } else if (sec == d->daySection) { + txt = QString::number(d->d); + txt = txt.mid(1, txt.length()) + QLatin1Char('0'); + d->d = txt.toInt(); + d->dayCache = d->d; + } + d->ed->repaint(d->ed->rect()); +} + +/*! \reimp + +*/ + +void Q3DateEdit::removeLastNumber(int sec) +{ + if (sec == -1) + return; + QString txt; + if (sec == d->yearSection) { + txt = QString::number(d->y); + txt = txt.mid(0, txt.length()-1); + d->y = txt.toInt(); + } else if (sec == d->monthSection) { + txt = QString::number(d->m); + txt = txt.mid(0, txt.length()-1); + d->m = txt.toInt(); + } else if (sec == d->daySection) { + txt = QString::number(d->d); + txt = txt.mid(0, txt.length()-1); + d->d = txt.toInt(); + d->dayCache = d->d; + } + d->ed->repaint(d->ed->rect()); +} + +/*! + \property Q3DateEdit::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 Q3DateEdit::setAutoAdvance(bool advance) +{ + d->adv = advance; +} + + +bool Q3DateEdit::autoAdvance() const +{ + return d->adv; +} + +/*! \reimp +*/ + +void Q3DateEdit::timerEvent(QTimerEvent *) +{ + d->overwrite = true; +} + +/*! + \fn void Q3DateEdit::valueChanged(const QDate& date) + + This signal is emitted whenever the editor's value changes. The \a + date parameter is the new value. +*/ + +/////////// + +class Q3TimeEditPrivate +{ +public: + int h; + int m; + int s; + uint display; + bool adv; + bool overwrite; + int timerId; + bool typing; + QTime min; + QTime max; + bool changed; + Q3DateTimeEditor *ed; + Q3SpinWidget *controls; +}; + +/*! + \class Q3TimeEdit + \brief The Q3TimeEdit class provides a time editor. + + \compat + + Q3TimeEdit 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 Q3TimeEdit + 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 Q3TimeEdit + is initialised with a time, e.g. + \snippet doc/src/snippets/code/src_qt3support_widgets_q3datetimeedit.cpp 1 + Here we've created a Q3TimeEdit widget set to the current time. + We've also set the minimum value to the current time and the + maximum time to one hour from now. + + The maximum and minimum values for a time value in the time editor + default to the maximum and minimum values for a QTime. You can + change this by calling setMinValue(), setMaxValue() or setRange(). + + Terminology: A QTimeWidget consists of three sections, one each + for the hour, minute and second. You can change the separator + character using setSeparator(), by default the separator is read + from the system's settings. + + \img datetimewidgets.png Date Time Widgets + + \sa QTime Q3DateEdit Q3DateTimeEdit +*/ + + +/*! + Constructs an empty time edit with parent \a parent and called \a + name. +*/ + +Q3TimeEdit::Q3TimeEdit(QWidget * parent, const char * name) + : Q3DateTimeEditBase(parent, name) +{ + init(); +} + +/*! + \overload + + Constructs a time edit with the initial time value, \a time, + parent \a parent and called \a name. +*/ + +Q3TimeEdit::Q3TimeEdit(const QTime& time, QWidget * parent, const char * name) + : Q3DateTimeEditBase(parent, name) +{ + init(); + setTime(time); +} + +/*! \internal + */ + +void Q3TimeEdit::init() +{ + d = new Q3TimeEditPrivate(); + d->controls = new QDateTimeSpinWidget(this, 0); + d->ed = new Q3DateTimeEditor(this, d->controls, "time edit base"); + d->controls->setEditWidget(d->ed); + setFocusProxy(d->ed); + connect(d->controls, SIGNAL(stepUpPressed()), SLOT(stepUp())); + connect(d->controls, SIGNAL(stepDownPressed()), SLOT(stepDown())); + + d->ed->appendSection(QNumberSection(0,0, true, 0)); + d->ed->appendSection(QNumberSection(0,0, true, 1)); + d->ed->appendSection(QNumberSection(0,0, true, 2)); + d->ed->setSeparator(localTimeSep()); + + d->h = 0; + d->m = 0; + d->s = 0; + d->display = Hours | Minutes | Seconds; + if (lAMPM) { + d->display |= AMPM; + d->ed->appendSection(QNumberSection(0,0, false, 3)); + } + d->adv = false; + d->overwrite = true; + d->timerId = 0; + d->typing = false; + d->min = QTime(0, 0, 0); + d->max = QTime(23, 59, 59); + d->changed = false; + + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + + refcount++; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3TimeEdit::~Q3TimeEdit() +{ + delete d; + if (!--refcount) + cleanup(); +} + +/*! + \property Q3TimeEdit::minValue + \brief the minimum time value + + Setting the minimum time value is equivalent to calling + Q3TimeEdit::setRange(\e t, maxValue()), where \e t is the minimum + time. The default minimum time is 00:00:00. + + \sa maxValue setRange() +*/ + +QTime Q3TimeEdit::minValue() const +{ + return d->min; +} + +/*! + \property Q3TimeEdit::maxValue + \brief the maximum time value + + Setting the maximum time value is equivalent to calling + Q3TimeEdit::setRange(minValue(), \e t), where \e t is the maximum + time. The default maximum time is 23:59:59. + + \sa minValue setRange() +*/ + +QTime Q3TimeEdit::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 Q3TimeEdit::setRange(const QTime& min, const QTime& max) +{ + if (min.isValid()) + d->min = min; + if (max.isValid()) + d->max = max; +} + +/*! + \property Q3TimeEdit::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 Q3TimeEdit::setDisplay(uint display) +{ + if (d->display == display) + return; + + d->ed->clearSections(); + d->display = display; + if (d->display & Hours) + d->ed->appendSection(QNumberSection(0,0, true, 0)); + if (d->display & Minutes) + d->ed->appendSection(QNumberSection(0,0, true, 1)); + if (d->display & Seconds) + d->ed->appendSection(QNumberSection(0,0, true, 2)); + if (d->display & AMPM) + d->ed->appendSection(QNumberSection(0,0, false, 3)); + + d->ed->setFocusSection(0); + d->ed->update(); +} + +uint Q3TimeEdit::display() const +{ + return d->display; +} + +/*! + \property Q3TimeEdit::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 Q3TimeEdit::setTime(const QTime& time) +{ + if (!time.isValid()) { + d->h = 0; + d->m = 0; + d->s = 0; + } else { + if (time > maxValue() || time < minValue()) + return; + d->h = time.hour(); + d->m = time.minute(); + d->s = time.second(); + emit valueChanged(time); + } + d->changed = false; + d->ed->repaint(d->ed->rect()); +} + +QTime Q3TimeEdit::time() const +{ + if (QTime::isValid(d->h, d->m, d->s)) + return QTime(d->h, d->m, d->s); + return QTime(); +} + +/*! + \property Q3TimeEdit::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 Q3TimeEdit::setAutoAdvance(bool advance) +{ + d->adv = advance; +} + +bool Q3TimeEdit::autoAdvance() const +{ + return d->adv; +} + +/*! + Sets the separator to \a s. Note that currently only the first + character of \a s is used. +*/ + +void Q3TimeEdit::setSeparator(const QString& s) +{ + d->ed->setSeparator(s); +} + +/*! + Returns the editor's separator. +*/ + +QString Q3TimeEdit::separator() const +{ + return d->ed->separator(); +} + + +/*! + \fn void Q3TimeEdit::valueChanged(const QTime& time) + + This signal is emitted whenever the editor's value changes. The \a + time parameter is the new value. +*/ + +/*! \reimp + +*/ + +bool Q3TimeEdit::event(QEvent *e) +{ + if (e->type() == QEvent::FocusOut) { + d->typing = false; + if (d->changed) { + emit valueChanged(time()); + d->changed = false; + } + } else if (e->type() == QEvent::LocaleChange) { + readLocaleSettings(); + d->ed->setSeparator(localTimeSep()); + } + return Q3DateTimeEditBase::event(e); +} + +/*! \reimp + +*/ + +void Q3TimeEdit::timerEvent(QTimerEvent *) +{ + d->overwrite = true; +} + + +/*! \reimp + +*/ + +void Q3TimeEdit::stepUp() +{ + int sec = d->ed->mapSection(d->ed->focusSection()); + bool accepted = true; + switch(sec) { + case 0: + if (!outOfRange(d->h+1, d->m, d->s)) + setHour(d->h+1); + else + setHour(d->min.hour()); + break; + case 1: + if (!outOfRange(d->h, d->m+1, d->s)) + setMinute(d->m+1); + else + setMinute(d->min.minute()); + break; + case 2: + if (!outOfRange(d->h, d->m, d->s+1)) + setSecond(d->s+1); + else + setSecond(d->min.second()); + break; + case 3: + if (d->h < 12) + setHour(d->h+12); + else + setHour(d->h-12); + break; + default: + accepted = false; + qWarning("Q3TimeEdit::stepUp: Focus section out of range!"); + break; + } + if (accepted) { + d->changed = false; + emit valueChanged(time()); + } + d->ed->repaint(d->ed->rect()); +} + + +/*! \reimp + +*/ + +void Q3TimeEdit::stepDown() +{ + int sec = d->ed->mapSection(d->ed->focusSection()); + + bool accepted = true; + switch(sec) { + case 0: + if (!outOfRange(d->h-1, d->m, d->s)) + setHour(d->h-1); + else + setHour(d->max.hour()); + break; + case 1: + if (!outOfRange(d->h, d->m-1, d->s)) + setMinute(d->m-1); + else + setMinute(d->max.minute()); + break; + case 2: + if (!outOfRange(d->h, d->m, d->s-1)) + setSecond(d->s-1); + else + setSecond(d->max.second()); + break; + case 3: + if (d->h > 11) + setHour(d->h-12); + else + setHour(d->h+12); + break; + default: + accepted = false; + qWarning("Q3TimeEdit::stepDown: Focus section out of range!"); + break; + } + if (accepted) { + d->changed = false; + emit valueChanged(time()); + } + d->ed->repaint(d->ed->rect()); +} + + +/*! + Returns the formatted number for section \a sec. This will + correspond to either the hour, minute or second section, depending + on \a sec. +*/ + +QString Q3TimeEdit::sectionFormattedText(int sec) +{ + QString txt; + txt = sectionText(sec); + txt = txt.rightJustified(2, QDATETIMEEDIT_HIDDEN_CHAR); + int offset = sec*2+sec*separator().length() + txt.length(); + if (d->typing && sec == d->ed->focusSection()) + d->ed->setSectionSelection(sec, offset - txt.length(), offset); + else + d->ed->setSectionSelection(sec, offset - txt.length(), offset); + + return txt; +} + + +/*! \reimp + +*/ + +bool Q3TimeEdit::setFocusSection(int sec) +{ + if (sec != d->ed->focusSection()) { + if (d->timerId) + killTimer(d->timerId); + d->timerId = 0; + d->overwrite = true; + d->typing = false; + QString txt = sectionText(sec); + txt = txt.rightJustified(2, QDATETIMEEDIT_HIDDEN_CHAR); + int offset = sec*2+sec*separator().length() + txt.length(); + d->ed->setSectionSelection(sec, offset - txt.length(), offset); + if (d->changed) { + emit valueChanged(time()); + d->changed = false; + } + } + return d->ed->setFocusSection(sec); +} + + +/*! + Sets the hour to \a h, which must be a valid hour, i.e. in the + range 0..24. +*/ + +void Q3TimeEdit::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 Q3TimeEdit::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 Q3TimeEdit::setSecond(int s) +{ + if (s < 0) + s = 0; + if (s > 59) + s = 59; + d->s = s; +} + + +/*! \internal + + Returns the text of section \a sec. + +*/ + +QString Q3TimeEdit::sectionText(int sec) +{ + sec = d->ed->mapSection(sec); + + QString txt; + switch(sec) { + case 0: + if (!(d->display & AMPM) || (d->h < 13 && d->h)) { // I wished the day stared at 0:00 for everybody + txt = QString::number(d->h); + } else { + if (d->h) + txt = QString::number(d->h - 12); + else + txt = QLatin1String("12"); + } + break; + case 1: + txt = QString::number(d->m); + break; + case 2: + txt = QString::number(d->s); + break; + case 3: + if (d->h < 12) { + if (lAM) + txt = *lAM; + else + txt = QString::fromLatin1("AM"); + } else { + if (lPM) + txt = *lPM; + else + txt = QString::fromLatin1("PM"); + } + break; + default: + break; + } + return txt; +} + + +/*! \internal + Returns true if \a h, \a m, and \a s are out of range. + */ + +bool Q3TimeEdit::outOfRange(int h, int m, int s) const +{ + if (QTime::isValid(h, m, s)) { + QTime currentTime(h, m, s); + if (currentTime > maxValue() || + currentTime < minValue()) + return true; + else + return false; + } + return true; +} + +/*! \reimp + +*/ + +void Q3TimeEdit::addNumber(int sec, int num) +{ + if (sec == -1) + return; + sec = d->ed->mapSection(sec); + if (d->timerId) + killTimer(d->timerId); + d->timerId = 0; + bool overwrite = false; + bool accepted = false; + d->typing = true; + QString txt; + + switch(sec) { + case 0: + txt = (d->display & AMPM && d->h > 12) ? + QString::number(d->h - 12) : QString::number(d->h); + + if (d->overwrite || txt.length() == 2) { + if (d->display & AMPM && num == 0) + break; // Don't process 0 in 12 hour clock mode + if (d->display & AMPM && d->h > 11) + num += 12; + if (!outOfRange(num, d->m, d->s)) { + accepted = true; + d->h = num; + } + } else { + txt += QString::number(num); + int temp = txt.toInt(); + + if (d->display & AMPM) { + if (temp == 12) { + if (d->h < 12) { + temp = 0; + } + accepted = true; + } else if (outOfRange(temp + 12, d->m, d->s)) { + txt = QString::number(d->h); + } else { + if (d->h > 11) { + temp += 12; + } + accepted = true; + } + } else if (!(d->display & AMPM) && outOfRange(temp, d->m, d->s)) { + txt = QString::number(d->h); + } else { + accepted = true; + } + + if (accepted) + d->h = temp; + + if (d->adv && txt.length() == 2) { + setFocusSection(d->ed->focusSection()+1); + overwrite = true; + } + } + break; + + case 1: + txt = QString::number(d->m); + if (d->overwrite || txt.length() == 2) { + if (!outOfRange(d->h, num, d->s)) { + accepted = true; + d->m = num; + } + } else { + txt += QString::number(num); + int temp = txt.toInt(); + if (temp > 59) + temp = num; + if (outOfRange(d->h, temp, d->s)) + txt = QString::number(d->m); + else { + accepted = true; + d->m = temp; + } + if (d->adv && txt.length() == 2) { + setFocusSection(d->ed->focusSection()+1); + overwrite = true; + } + } + break; + + case 2: + txt = QString::number(d->s); + if (d->overwrite || txt.length() == 2) { + if (!outOfRange(d->h, d->m, num)) { + accepted = true; + d->s = num; + } + } else { + txt += QString::number(num); + int temp = txt.toInt(); + if (temp > 59) + temp = num; + if (outOfRange(d->h, d->m, temp)) + txt = QString::number(d->s); + else { + accepted = true; + d->s = temp; + } + if (d->adv && txt.length() == 2) { + setFocusSection(d->ed->focusSection()+1); + overwrite = true; + } + } + break; + + case 3: + break; + + default: + break; + } + d->changed = !accepted; + if (accepted) + emit valueChanged(time()); + d->overwrite = overwrite; + d->timerId = startTimer(qApp->doubleClickInterval()*4); + d->ed->repaint(d->ed->rect()); +} + + +/*! + \internal + + Function which is called whenever the user tries to + remove the first number from \a sec by pressing the backspace key. +*/ + +void Q3TimeEdit::removeFirstNumber(int sec) +{ + if (sec == -1) + return; + sec = d->ed->mapSection(sec); + QString txt; + switch(sec) { + case 0: + txt = QString::number(d->h); + break; + case 1: + txt = QString::number(d->m); + break; + case 2: + txt = QString::number(d->s); + break; + } + txt = txt.mid(1, txt.length()) + QLatin1Char('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()); +} + +/*! \reimp + +*/ +void Q3TimeEdit::removeLastNumber(int sec) +{ + if (sec == -1) + return; + sec = d->ed->mapSection(sec); + QString txt; + switch(sec) { + case 0: + txt = QString::number(d->h); + break; + case 1: + txt = QString::number(d->m); + break; + case 2: + txt = QString::number(d->s); + break; + } + txt = txt.mid(0, txt.length()-1); + switch(sec) { + case 0: + d->h = txt.toInt(); + break; + case 1: + d->m = txt.toInt(); + break; + case 2: + d->s = txt.toInt(); + break; + } + d->ed->repaint(d->ed->rect()); +} + +/*! \reimp + */ +void Q3TimeEdit::resizeEvent(QResizeEvent *) +{ + d->controls->resize(width(), height()); +} + +/*! \reimp +*/ +QSize Q3TimeEdit::sizeHint() const +{ + ensurePolished(); + QFontMetrics fm(font()); + int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this); + int h = fm.lineSpacing() + 2; + int w = 2 + fm.width(QLatin1Char('9')) * 6 + fm.width(d->ed->separator()) * 2 + + d->controls->upRect().width() + fw * 4; + if (d->display & AMPM) { + if (lAM) + w += fm.width(*lAM) + 4; + else + w += fm.width(QString::fromLatin1("AM")) + 4; + } + + return QSize(w, qMax(h + fw * 2,20)).expandedTo(QApplication::globalStrut()); +} + +/*! \reimp +*/ +QSize Q3TimeEdit::minimumSizeHint() const +{ + return sizeHint(); +} + +/*! + \internal + Enables/disables the push buttons according to the min/max time + for this widget. +*/ + +void Q3TimeEdit::updateButtons() +{ + if (!isEnabled()) + return; + + bool upEnabled = time() < maxValue(); + bool downEnabled = time() > minValue(); + + d->controls->setUpEnabled(upEnabled); + d->controls->setDownEnabled(downEnabled); +} + + +class Q3DateTimeEditPrivate +{ +public: + bool adv; +}; + +/*! + \class Q3DateTimeEdit + \brief The Q3DateTimeEdit class combines a Q3DateEdit and Q3TimeEdit + widget into a single widget for editing datetimes. + + \compat + + Q3DateTimeEdit consists of a Q3DateEdit and Q3TimeEdit 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 Q3DateTimeEdit 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 + Q3DateEdit::setOrder() to change this. Times appear in the order + hours, minutes, seconds using the 24 hour clock. + + It is recommended that the Q3DateTimeEdit is initialised with a + datetime, e.g. + \snippet doc/src/snippets/code/src_qt3support_widgets_q3datetimeedit.cpp 2 + Here we've created a new Q3DateTimeEdit 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 Q3DateEdit widget consists of three 'sections', one + each for the year, month and day. Similarly a Q3TimeEdit 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 Q3DateEdit Q3TimeEdit +*/ + +/*! + Constructs an empty datetime edit with parent \a parent and called + \a name. +*/ +Q3DateTimeEdit::Q3DateTimeEdit(QWidget * parent, const char * name) + : QWidget(parent, name) +{ + init(); +} + + +/*! + \overload + + Constructs a datetime edit with the initial value \a datetime, + parent \a parent and called \a name. +*/ +Q3DateTimeEdit::Q3DateTimeEdit(const QDateTime& datetime, + QWidget * parent, const char * name) + : QWidget(parent, name) +{ + init(); + setDateTime(datetime); +} + + + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3DateTimeEdit::~Q3DateTimeEdit() +{ + delete d; +} + + +/*! + \reimp + + Intercepts and handles resize events which have special meaning + for the Q3DateTimeEdit. +*/ + +void Q3DateTimeEdit::resizeEvent(QResizeEvent *) +{ + int dw = de->sizeHint().width(); + int tw = te->sizeHint().width(); + int w = width(); + int h = height(); + int extra = w - (dw + tw); + + if (tw + extra < 0) { + dw = w; + } else { + dw += 9 * extra / 16; + } + tw = w - dw; + + de->setGeometry(0, 0, dw, h); + te->setGeometry(dw, 0, tw, h); +} + +/*! \reimp +*/ + +QSize Q3DateTimeEdit::minimumSizeHint() const +{ + QSize dsh = de->minimumSizeHint(); + QSize tsh = te->minimumSizeHint(); + return QSize(dsh.width() + tsh.width(), + qMax(dsh.height(), tsh.height())); +} + +/*! \internal + */ + +void Q3DateTimeEdit::init() +{ + d = new Q3DateTimeEditPrivate(); + de = new Q3DateEdit(this, "qt_datetime_dateedit"); + te = new Q3TimeEdit(this, "qt_datetime_timeedit"); + d->adv = false; + connect(de, SIGNAL(valueChanged(QDate)), this, SLOT(newValue(QDate))); + connect(te, SIGNAL(valueChanged(QTime)), this, SLOT(newValue(QTime))); + setFocusProxy(de); + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); +} + +/*! \reimp + */ + +QSize Q3DateTimeEdit::sizeHint() const +{ + ensurePolished(); + QSize dsh = de->sizeHint(); + QSize tsh = te->sizeHint(); + return QSize(dsh.width() + tsh.width(), + qMax(dsh.height(), tsh.height())); +} + +/*! + \property Q3DateTimeEdit::dateTime + \brief the editor's datetime value + + The datetime edit's datetime which may be an invalid datetime. +*/ + +void Q3DateTimeEdit::setDateTime(const QDateTime & dt) +{ + if (dt.isValid()) { + de->setDate(dt.date()); + te->setTime(dt.time()); + emit valueChanged(dt); + } +} + +QDateTime Q3DateTimeEdit::dateTime() const +{ + return QDateTime(de->date(), te->time()); +} + +/*! + \fn void Q3DateTimeEdit::valueChanged(const QDateTime& datetime) + + This signal is emitted every time the date or time changes. The \a + datetime argument is the new datetime. +*/ + + +/*! \internal + + Re-emits the value \a d. + */ + +void Q3DateTimeEdit::newValue(const QDate&) +{ + QDateTime dt = dateTime(); + emit valueChanged(dt); +} + +/*! \internal + \overload + Re-emits the value \a t. + */ + +void Q3DateTimeEdit::newValue(const QTime&) +{ + QDateTime dt = dateTime(); + emit valueChanged(dt); +} + + +/*! + Sets the auto advance property of the editor to \a advance. If set + to true, the editor will automatically advance focus to the next + date or time section if the user has completed a section. +*/ + +void Q3DateTimeEdit::setAutoAdvance(bool advance) +{ + de->setAutoAdvance(advance); + te->setAutoAdvance(advance); +} + +/*! + Returns true if auto-advance is enabled, otherwise returns false. + + \sa setAutoAdvance() +*/ + +bool Q3DateTimeEdit::autoAdvance() const +{ + return de->autoAdvance(); +} + +/*! + \fn Q3DateEdit* Q3DateTimeEdit::dateEdit() + + Returns the internal widget used for editing the date part of the + datetime. +*/ + +/*! + \fn Q3TimeEdit* Q3DateTimeEdit::timeEdit() + + Returns the internal widget used for editing the time part of the + datetime. +*/ + +QT_END_NAMESPACE + +#include "q3datetimeedit.moc" + +#endif diff --git a/src/qt3support/widgets/q3datetimeedit.h b/src/qt3support/widgets/q3datetimeedit.h new file mode 100644 index 0000000..71edbdd --- /dev/null +++ b/src/qt3support/widgets/q3datetimeedit.h @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DATETIMEEDIT_H +#define Q3DATETIMEEDIT_H + +#include <QtGui/qwidget.h> +#include <QtCore/qstring.h> +#include <QtCore/qdatetime.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_DATETIMEEDIT + +class Q_COMPAT_EXPORT Q3DateTimeEditBase : public QWidget +{ + Q_OBJECT +public: + Q3DateTimeEditBase(QWidget* parent=0, const char* name=0) + : QWidget(parent) { setObjectName(QString::fromAscii(name)); } + + virtual bool setFocusSection(int sec) = 0; + virtual QString sectionFormattedText(int sec) = 0; + virtual void addNumber(int sec, int num) = 0; + virtual void removeLastNumber(int sec) = 0; + +public Q_SLOTS: + virtual void stepUp() = 0; + virtual void stepDown() = 0; + +private: + Q_DISABLE_COPY(Q3DateTimeEditBase) +}; + +class Q3DateEditPrivate; + +class Q_COMPAT_EXPORT Q3DateEdit : public Q3DateTimeEditBase +{ + Q_OBJECT + Q_ENUMS(Order) + Q_PROPERTY(Order order READ order WRITE setOrder) + Q_PROPERTY(QDate date READ date WRITE setDate USER true) + Q_PROPERTY(bool autoAdvance READ autoAdvance WRITE setAutoAdvance) + Q_PROPERTY(QDate maxValue READ maxValue WRITE setMaxValue) + Q_PROPERTY(QDate minValue READ minValue WRITE setMinValue) + +public: + Q3DateEdit(QWidget* parent=0, const char* name=0); + Q3DateEdit(const QDate& date, QWidget* parent=0, const char* name=0); + ~Q3DateEdit(); + + enum Order { DMY, MDY, YMD, YDM }; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +public Q_SLOTS: + virtual void setDate(const QDate& date); + +public: + QDate date() const; + virtual void setOrder(Order order); + Order order() const; + virtual void setAutoAdvance(bool advance); + bool autoAdvance() const; + + virtual void setMinValue(const QDate& d) { setRange(d, maxValue()); } + QDate minValue() const; + virtual void setMaxValue(const QDate& d) { setRange(minValue(), d); } + QDate maxValue() const; + virtual void setRange(const QDate& min, const QDate& max); + QString separator() const; + virtual void setSeparator(const QString& s); + + // Make removeFirstNumber() virtual in Q3DateTimeEditBase in 4.0 + void removeFirstNumber(int sec); + +Q_SIGNALS: + void valueChanged(const QDate& date); + +protected: + bool event(QEvent *e); + void timerEvent(QTimerEvent *); + void resizeEvent(QResizeEvent *); + void stepUp(); + void stepDown(); + QString sectionFormattedText(int sec); + void addNumber(int sec, int num); + + void removeLastNumber(int sec); + bool setFocusSection(int s); + + virtual void setYear(int year); + virtual void setMonth(int month); + virtual void setDay(int day); + virtual void fix(); + virtual bool outOfRange(int y, int m, int d) const; + +protected Q_SLOTS: + void updateButtons(); + +private: + Q_DISABLE_COPY(Q3DateEdit) + + void init(); + int sectionOffsetEnd(int sec) const; + int sectionLength(int sec) const; + QString sectionText(int sec) const; + Q3DateEditPrivate* d; +}; + +class Q3TimeEditPrivate; + +class Q_COMPAT_EXPORT Q3TimeEdit : public Q3DateTimeEditBase +{ + Q_OBJECT + Q_FLAGS(Display) + Q_PROPERTY(QTime time READ time WRITE setTime USER true) + Q_PROPERTY(bool autoAdvance READ autoAdvance WRITE setAutoAdvance) + Q_PROPERTY(QTime maxValue READ maxValue WRITE setMaxValue) + Q_PROPERTY(QTime minValue READ minValue WRITE setMinValue) + Q_PROPERTY(Display display READ display WRITE setDisplay) + +public: + enum Display { + Hours = 0x01, + Minutes = 0x02, + Seconds = 0x04, + /*Reserved = 0x08,*/ + AMPM = 0x10 + }; + + Q3TimeEdit(QWidget* parent=0, const char* name=0); + Q3TimeEdit(const QTime& time, QWidget* parent=0, const char* name=0); + ~Q3TimeEdit(); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +public Q_SLOTS: + virtual void setTime(const QTime& time); + +public: + QTime time() const; + virtual void setAutoAdvance(bool advance); + bool autoAdvance() const; + + virtual void setMinValue(const QTime& d) { setRange(d, maxValue()); } + QTime minValue() const; + virtual void setMaxValue(const QTime& d) { setRange(minValue(), d); } + QTime maxValue() const; + virtual void setRange(const QTime& min, const QTime& max); + QString separator() const; + virtual void setSeparator(const QString& s); + + uint display() const; + void setDisplay(uint disp); + + // Make removeFirstNumber() virtual in Q3DateTimeEditBase in 4.0 + void removeFirstNumber(int sec); + +Q_SIGNALS: + void valueChanged(const QTime& time); + +protected: + bool event(QEvent *e); + void timerEvent(QTimerEvent *e); + void resizeEvent(QResizeEvent *); + void stepUp(); + void stepDown(); + QString sectionFormattedText(int sec); + void addNumber(int sec, int num); + void removeLastNumber(int sec); + bool setFocusSection(int s); + + virtual bool outOfRange(int h, int m, int s) const; + virtual void setHour(int h); + virtual void setMinute(int m); + virtual void setSecond(int s); + +protected Q_SLOTS: + void updateButtons(); + +private: + Q_DISABLE_COPY(Q3TimeEdit) + + void init(); + QString sectionText(int sec); + Q3TimeEditPrivate* d; +}; + + +class Q3DateTimeEditPrivate; + +class Q_COMPAT_EXPORT Q3DateTimeEdit : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime USER true) + +public: + Q3DateTimeEdit(QWidget* parent=0, const char* name=0); + Q3DateTimeEdit(const QDateTime& datetime, QWidget* parent=0, + const char* name=0); + ~Q3DateTimeEdit(); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +public Q_SLOTS: + virtual void setDateTime(const QDateTime & dt); + +public: + QDateTime dateTime() const; + + Q3DateEdit* dateEdit() { return de; } + Q3TimeEdit* timeEdit() { return te; } + + virtual void setAutoAdvance(bool advance); + bool autoAdvance() const; + +Q_SIGNALS: + void valueChanged(const QDateTime& datetime); + +protected: + void init(); + void resizeEvent(QResizeEvent *); + +protected Q_SLOTS: + void newValue(const QDate& d); + void newValue(const QTime& t); + +private: + Q_DISABLE_COPY(Q3DateTimeEdit) + + Q3DateEdit* de; + Q3TimeEdit* te; + Q3DateTimeEditPrivate* d; +}; + +#endif // QT_NO_DATETIMEEDIT + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3DATETIMEEDIT_H diff --git a/src/qt3support/widgets/q3dockarea.cpp b/src/qt3support/widgets/q3dockarea.cpp new file mode 100644 index 0000000..1609aa4 --- /dev/null +++ b/src/qt3support/widgets/q3dockarea.cpp @@ -0,0 +1,1349 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3dockarea.h" + +#ifndef QT_NO_MAINWINDOW +#include "qsplitter.h" +#include "qevent.h" +#include "qlayout.h" +#include "qapplication.h" +#include "qpainter.h" +#include "qmap.h" +#include "q3mainwindow.h" +#include "q3toolbar.h" + +QT_BEGIN_NAMESPACE + +//#define QDOCKAREA_DEBUG + +struct Q3DockData +{ + Q3DockData() : w(0), rect() {} + Q3DockData(Q3DockWindow *dw, const QRect &r) : w(dw), rect(r) {} + Q3DockWindow *w; + QRect rect; + + Q_DUMMY_COMPARISON_OPERATOR(Q3DockData) +}; + +static int fix_x(Q3DockWindow* w, int width = -1) { + if (QApplication::reverseLayout()) { + if (width < 0) + width = w->width(); + return w->parentWidget()->width() - w->x() - width; + } + return w->x(); +} +static int fix_x(Q3DockWindow* w, int x, int width = -1) { + if (QApplication::reverseLayout()) { + if (width < 0) + width = w->width(); + return w->parentWidget()->width() - x - width; + } + return x; +} + +static QPoint fix_pos(Q3DockWindow* w) { + if (QApplication::reverseLayout()) { + QPoint p = w->pos(); + p.rx() = w->parentWidget()->width() - p.x() - w->width(); + return p; + } + return w->pos(); +} + + +void Q3DockAreaLayout::setGeometry(const QRect &r) +{ + QLayout::setGeometry(r); + layoutItems(r); +} + +QLayoutItem *Q3DockAreaLayout::itemAt(int) const +{ + return 0; //### +} + +QLayoutItem *Q3DockAreaLayout::takeAt(int) +{ + return 0; //### +} + +int Q3DockAreaLayout::count() const +{ + return 0; //### +} + + +QSize Q3DockAreaLayout::sizeHint() const +{ + if (dockWindows->isEmpty()) + return QSize(0, 0); + + if (dirty) { + Q3DockAreaLayout *that = (Q3DockAreaLayout *) this; + that->layoutItems(geometry()); + } + + int w = 0; + int h = 0; + int y = -1; + int x = -1; + int ph = 0; + int pw = 0; + for (int i = 0; i < dockWindows->size(); ++i) { + Q3DockWindow *dw = dockWindows->at(i); + int plush = 0, plusw = 0; + if (dw->isHidden()) + continue; + if (hasHeightForWidth()) { + if (y != dw->y()) + plush = ph; + y = dw->y(); + ph = dw->height(); + } else { + if (x != dw->x()) + plusw = pw; + x = dw->x(); + pw = dw->width(); + } + h = qMax(h, dw->height() + plush); + w = qMax(w, dw->width() + plusw); + } + + if (hasHeightForWidth()) + return QSize(0, h); + return QSize(w, 0); +} + +bool Q3DockAreaLayout::hasHeightForWidth() const +{ + return orient == Qt::Horizontal; +} + +void Q3DockAreaLayout::init() +{ + dirty = true; + cached_width = 0; + cached_height = 0; + cached_hfw = -1; + cached_wfh = -1; +} + +void Q3DockAreaLayout::invalidate() +{ + dirty = true; + cached_width = 0; + cached_height = 0; + QLayout::invalidate(); +} + +static int start_pos(const QRect &r, Qt::Orientation o) +{ + if (o == Qt::Horizontal) { + return qMax(0, r.x()); + } else { + return qMax(0, r.y()); + } +} + +static void add_size(int s, int &pos, Qt::Orientation o) +{ + if (o == Qt::Horizontal) { + pos += s; + } else { + pos += s; + } +} + +static int space_left(const QRect &r, int pos, Qt::Orientation o) +{ + if (o == Qt::Horizontal) { + return (r.x() + r.width()) - pos; + } else { + return (r.y() + r.height()) - pos; + } +} + +static int dock_extent(Q3DockWindow *w, Qt::Orientation o, int maxsize) +{ + if (o == Qt::Horizontal) + return qMin(maxsize, qMax(w->sizeHint().width(), w->fixedExtent().width())); + else + return qMin(maxsize, qMax(w->sizeHint().height(), w->fixedExtent().height())); +} + +static int dock_strut(Q3DockWindow *w, Qt::Orientation o) +{ + if (o != Qt::Horizontal) { + int wid; + if ((wid = w->fixedExtent().width()) != -1) + return qMax(wid, qMax(w->minimumSize().width(), w->minimumSizeHint().width())); + return qMax(w->sizeHint().width(), qMax(w->minimumSize().width(), w->minimumSizeHint().width())); + } else { + int hei; + if ((hei = w->fixedExtent().height()) != -1) + return qMax(hei, qMax(w->minimumSizeHint().height(), w->minimumSize().height())); + return qMax(w->sizeHint().height(), qMax(w->minimumSizeHint().height(), w->minimumSize().height())); + } +} + +static void set_geometry(Q3DockWindow *w, int pos, int sectionpos, int extent, int strut, Qt::Orientation o) +{ + if (o == Qt::Horizontal) + w->setGeometry(fix_x(w, pos, extent), sectionpos, extent, strut); + else + w->setGeometry(sectionpos, pos, strut, extent); +} + +static int size_extent(const QSize &s, Qt::Orientation o, bool swap = false) +{ + return o == Qt::Horizontal ? (swap ? s.height() : s.width()) : (swap ? s.width() : s.height()); +} + +static int point_pos(const QPoint &p, Qt::Orientation o, bool swap = false) +{ + return o == Qt::Horizontal ? (swap ? p.y() : p.x()) : (swap ? p.x() : p.y()); +} + +static void shrink_extend(Q3DockWindow *dw, int &dockExtend, int /*spaceLeft*/, Qt::Orientation o) +{ + Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(dw); + if (o == Qt::Horizontal) { + int mw = 0; + if (!tb) + mw = dw->minimumWidth(); + else + mw = dw->sizeHint().width(); + dockExtend = mw; + } else { + int mh = 0; + if (!tb) + mh = dw->minimumHeight(); + else + mh = dw->sizeHint().height(); + dockExtend = mh; + } +} + +static void place_line(QList<Q3DockData> &lastLine, Qt::Orientation o, int linestrut, int fullextent, int tbstrut, int maxsize, Q3DockAreaLayout *) +{ + Q3DockWindow *last = 0; + QRect lastRect; + for (QList<Q3DockData>::Iterator it = lastLine.begin(); it != lastLine.end(); ++it) { + if (tbstrut != -1 && qobject_cast<Q3ToolBar*>((*it).w)) + (*it).rect.setHeight(tbstrut); + if (!last) { + last = (*it).w; + lastRect = (*it).rect; + continue; + } + if (!last->isStretchable()) { + int w = qMin(lastRect.width(), maxsize); + set_geometry(last, lastRect.x(), lastRect.y(), w, lastRect.height(), o); + } else { + int w = qMin((*it).rect.x() - lastRect.x(), maxsize); + set_geometry(last, lastRect.x(), lastRect.y(), w, + last->isResizeEnabled() ? linestrut : lastRect.height(), o); + } + last = (*it).w; + lastRect = (*it).rect; + } + if (!last) + return; + if (!last->isStretchable()) { + int w = qMin(lastRect.width(), maxsize); + set_geometry(last, lastRect.x(), lastRect.y(), w, lastRect.height(), o); + } else { + int w = qMin(fullextent - lastRect.x() - (o == Qt::Vertical ? 1 : 0), maxsize); + set_geometry(last, lastRect.x(), lastRect.y(), w, + last->isResizeEnabled() ? linestrut : lastRect.height(), o); + } +} + +QSize Q3DockAreaLayout::minimumSize() const +{ + if (dockWindows->isEmpty()) + return QSize(0, 0); + + if (dirty) { + Q3DockAreaLayout *that = (Q3DockAreaLayout *) this; + that->layoutItems(geometry()); + } + + int s = 0; + + for (int i = 0; i < dockWindows->size(); ++i) { + Q3DockWindow *dw = dockWindows->at(i); + if (dw->isHidden()) + continue; + s = qMax(s, dock_strut(dw, orientation())); + } + + return orientation() == Qt::Horizontal ? QSize(0, s ? s+2 : 0) : QSize(s, 0); +} + + + +int Q3DockAreaLayout::layoutItems(const QRect &rect, bool testonly) +{ + if (dockWindows->isEmpty()) + return 0; + + dirty = false; + + // some corrections + QRect r = rect; + if (orientation() == Qt::Vertical) + r.setHeight(r.height() - 3); + + // init + lines.clear(); + ls.clear(); + int start = start_pos(r, orientation()); + int pos = start; + int sectionpos = 0; + int linestrut = 0; + QList<Q3DockData> lastLine; + int tbstrut = -1; + int maxsize = size_extent(rect.size(), orientation()); + int visibleWindows = 0; + + // go through all widgets in the dock + for (int i = 0; i < dockWindows->size(); ++i) { + Q3DockWindow *dw = dockWindows->at(i); + if (dw->isHidden()) + continue; + ++visibleWindows; + // find position for the widget: This is the maximum of the + // end of the previous widget and the offset of the widget. If + // the position + the width of the widget dosn't fit into the + // dock, try moving it a bit back, if possible. + int op = pos; + int dockExtend = dock_extent(dw, orientation(), maxsize); + if (!dw->isStretchable()) { + pos = qMax(pos, dw->offset()); + if (pos + dockExtend > size_extent(r.size(), orientation()) - 1) + pos = qMax(op, size_extent(r.size(), orientation()) - 1 - dockExtend); + } + if (!lastLine.isEmpty() && !dw->newLine() && space_left(rect, pos, orientation()) < dockExtend) + shrink_extend(dw, dockExtend, space_left(rect, pos, orientation()), orientation()); + // if the current widget doesn't fit into the line anymore and it is not the first widget of the line + if (!lastLine.isEmpty() && + (space_left(rect, pos, orientation()) < dockExtend || dw->newLine())) { + if (!testonly) // place the last line, if not in test mode + place_line(lastLine, orientation(), linestrut, size_extent(r.size(), orientation()), tbstrut, maxsize, this); + // remember the line coordinats of the last line + if (orientation() == Qt::Horizontal) + lines.append(QRect(0, sectionpos, r.width(), linestrut)); + else + lines.append(QRect(sectionpos, 0, linestrut, r.height())); + // do some clearing for the next line + lastLine.clear(); + sectionpos += linestrut; + linestrut = 0; + pos = start; + tbstrut = -1; + } + + // remember first widget of a line + if (lastLine.isEmpty()) { + ls.append(dw); + // try to make the best position + int op = pos; + if (!dw->isStretchable()) + pos = qMax(pos, dw->offset()); + if (pos + dockExtend > size_extent(r.size(), orientation()) - 1) + pos = qMax(op, size_extent(r.size(), orientation()) - 1 - dockExtend); + } + // do some calculations and add the remember the rect which the docking widget requires for the placing + QRect dwRect(pos, sectionpos, dockExtend, dock_strut(dw, orientation() )); + lastLine.append(Q3DockData(dw, dwRect)); + if (qobject_cast<Q3ToolBar*>(dw)) + tbstrut = qMax(tbstrut, dock_strut(dw, orientation())); + linestrut = qMax(dock_strut(dw, orientation()), linestrut); + add_size(dockExtend, pos, orientation()); + } + + // if some stuff was not placed/stored yet, do it now + if (!testonly) + place_line(lastLine, orientation(), linestrut, size_extent(r.size(), orientation()), tbstrut, maxsize, this); + if (orientation() == Qt::Horizontal) + lines.append(QRect(0, sectionpos, r.width(), linestrut)); + else + lines.append(QRect(sectionpos, 0, linestrut, r.height())); + if (lines.size() >= 2 && *(--lines.end()) == *(--(--lines.end()))) + lines.removeLast(); + + bool hadResizable = false; + for (int i = 0; i < dockWindows->size(); ++i) { + Q3DockWindow *dw = dockWindows->at(i); + if (!dw->isVisibleTo(parentWidget)) + continue; + hadResizable = hadResizable || dw->isResizeEnabled(); + dw->updateSplitterVisibility(visibleWindows > 1); //!dw->area()->isLastDockWindow(dw)); + if (Q3ToolBar *tb = qobject_cast<Q3ToolBar *>(dw)) + tb->checkForExtension(dw->size()); + } + return sectionpos + linestrut; +} + +int Q3DockAreaLayout::heightForWidth(int w) const +{ + if (dockWindows->isEmpty() && parentWidget) + return parentWidget->minimumHeight(); + + if (cached_width != w) { + Q3DockAreaLayout * mthis = (Q3DockAreaLayout*)this; + mthis->cached_width = w; + int h = mthis->layoutItems(QRect(0, 0, w, 0), true); + mthis->cached_hfw = h; + return h; + } + + return cached_hfw; +} + +int Q3DockAreaLayout::widthForHeight(int h) const +{ + if (cached_height != h) { + Q3DockAreaLayout * mthis = (Q3DockAreaLayout*)this; + mthis->cached_height = h; + int w = mthis->layoutItems(QRect(0, 0, 0, h), true); + mthis->cached_wfh = w; + return w; + } + return cached_wfh; +} + + + + +/*! + \class Q3DockArea + \brief The Q3DockArea class manages and lays out Q3DockWindows. + + \compat + + A Q3DockArea is a container which manages a list of + \l{Q3DockWindow}s which it lays out within its area. In cooperation + with the \l{Q3DockWindow}s it is responsible for the docking and + undocking of \l{Q3DockWindow}s and moving them inside the dock + area. Q3DockAreas also handle the wrapping of \l{Q3DockWindow}s to + fill the available space as compactly as possible. Q3DockAreas can + contain Q3ToolBars since Q3ToolBar is a Q3DockWindow subclass. + + QMainWindow contains four Q3DockAreas which you can use for your + Q3ToolBars and Q3DockWindows, so in most situations you do not need + to use the Q3DockArea class directly. Although QMainWindow contains + support for its own dock areas it isn't convenient for adding new + Q3DockAreas. If you need to create your own dock areas we suggest + that you create a subclass of QWidget and add your Q3DockAreas to + your subclass. + + \img qmainwindow-qdockareas.png QMainWindow's Q3DockAreas + + \target lines + \e Lines. Q3DockArea 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. Qt::Dock windows can be lined + up to minimize wasted space using the lineUp() function. + + The Q3DockArea class maintains a position list of all its child + dock windows. Qt::Dock windows are added to a dock area from position + 0 onwards. Qt::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 Q3DockWindow::dock() and + Q3DockWindow::undock() respectively. If you want to control which + dock windows can dock in a dock area use setAcceptDockWindow(). To + see if a dock area contains a particular dock window use + \l{hasDockWindow()}; to see how many dock windows a dock area + contains use count(). + + The streaming operators can write the positions of the dock + windows in the dock area to a QTextStream. The positions can be + read back later to restore the saved positions. + + Save the positions to a QTextStream: + \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockarea.cpp 0 + + Restore the positions from a QTextStream: + \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockarea.cpp 1 +*/ + +/*! + \property Q3DockArea::handlePosition + \brief where the dock window splitter handle is placed in the dock + area + + The default position is \c Normal. +*/ + +/*! + \property Q3DockArea::orientation + \brief the dock area's orientation + + There is no default value; the orientation is specified in the + constructor. +*/ + +/*! + \enum Q3DockArea::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 Q3DockArea with orientation \a o, HandlePosition \a h, + parent \a parent and called \a name. +*/ + +Q3DockArea::Q3DockArea(Qt::Orientation o, HandlePosition h, QWidget *parent, const char *name) + : QWidget(parent, name), orient(o), layout(0), hPos(h) +{ + layout = new Q3DockAreaLayout(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. +*/ + +Q3DockArea::~Q3DockArea() +{ + while (!dockWindows.isEmpty()) + delete dockWindows.takeFirst(); +} + +/*! + Moves the Q3DockWindow \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 Q3DockArea::moveDockWindow(Q3DockWindow *w, int index) +{ + invalidateFixedSizes(); + Q3DockWindow *dockWindow = 0; + int dockWindowIndex = findDockWindow(w); + if (dockWindowIndex == -1) { + dockWindow = w; + bool vis = dockWindow->isVisible(); + dockWindow->setParent(this); + dockWindow->move(0, 0); + if(vis) + dockWindow->show(); + w->installEventFilter(this); + updateLayout(); + setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum, + orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum)); + dockWindows.append(w); + } else { + if (w->parent() != this) { + bool vis = w->isVisible(); + w->setParent(this); + w->move(0, 0); + if(vis) + w->show(); + } + if (index == -1) { + dockWindows.removeAll(w); + dockWindows.append(w); + } + } + + w->dockArea = this; + w->curPlace = Q3DockWindow::InDock; + w->updateGui(); + + if (index != -1 && index < (int)dockWindows.count()) { + dockWindows.removeAll(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 Q3DockArea::hasDockWindow(Q3DockWindow *w, int *index) +{ + int i = dockWindows.indexOf(w); + if (index) + *index = i; + return i != -1; +} + +int Q3DockArea::lineOf(int index) +{ + QList<Q3DockWindow *> lineStarts = layout->lineStarts(); + int i = 0; + for (; i < lineStarts.size(); ++i) { + Q3DockWindow *w = lineStarts.at(i); + if (dockWindows.indexOf(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 Q3DockWindow. You shouldn't + need to call it yourself. +*/ + +void Q3DockArea::moveDockWindow(Q3DockWindow *w, const QPoint &p, const QRect &r, bool swap) +{ + invalidateFixedSizes(); + int mse = -10; + bool hasResizable = false; + for (int i = 0; i < dockWindows.size(); ++i) { + Q3DockWindow *dw = dockWindows.at(i); + if (dw->isHidden()) + continue; + if (dw->isResizeEnabled()) + hasResizable = true; + if (orientation() != Qt::Horizontal) + mse = qMax(qMax(dw->fixedExtent().width(), dw->width()), mse); + else + mse = qMax(qMax(dw->fixedExtent().height(), dw->height()), mse); + } + if (!hasResizable && w->isResizeEnabled()) { + if (orientation() != Qt::Horizontal) + mse = qMax(w->fixedExtent().width(), mse); + else + mse = qMax(w->fixedExtent().height(), mse); + } + + Q3DockWindow *dockWindow = 0; + int dockWindowIndex = findDockWindow(w); + QList<Q3DockWindow *> lineStarts = layout->lineStarts(); + QList<QRect> lines = layout->lineList(); + bool wasAloneInLine = false; + QPoint pos = mapFromGlobal(p); + int line = lineOf(dockWindowIndex); + QRect lr; + if (line < lines.size()) + lr = lines.at(line); + if (dockWindowIndex != -1) { + if (lineStarts.contains(w) + && ((dockWindowIndex < dockWindows.count() - 1 + && lineStarts.contains(dockWindows.at(dockWindowIndex + 1))) + || dockWindowIndex == dockWindows.count() - 1)) + wasAloneInLine = true; + dockWindow = dockWindows.takeAt(dockWindowIndex); + if (!wasAloneInLine) { // only do the pre-layout if the widget isn't the only one in its line + if (lineStarts.contains(dockWindow) && dockWindowIndex < dockWindows.count()) + dockWindows.at(dockWindowIndex)->setNewLine(true); + layout->layoutItems(QRect(0, 0, width(), height()), true); + } + } else { + dockWindow = w; + bool vis = dockWindow->isVisible(); + dockWindow->setParent(this); + dockWindow->move(0, 0); + if(vis) + dockWindow->show(); + if (swap) + dockWindow->resize(dockWindow->height(), dockWindow->width()); + w->installEventFilter(this); + } + + lineStarts = layout->lineStarts(); + lines = layout->lineList(); + + QRect rect = QRect(mapFromGlobal(r.topLeft()), r.size()); + if (orientation() == Qt::Horizontal && QApplication::reverseLayout()) { + rect = QRect(width() - rect.x() - rect.width(), rect.y(), rect.width(), rect.height()); + pos.rx() = width() - pos.x(); + } + dockWindow->setOffset(point_pos(rect.topLeft(), orientation())); + if (orientation() == Qt::Horizontal) { + int offs = dockWindow->offset(); + if (width() - offs < dockWindow->minimumWidth()) + dockWindow->setOffset(width() - dockWindow->minimumWidth()); + } else { + int offs = dockWindow->offset(); + if (height() - offs < dockWindow->minimumHeight()) + dockWindow->setOffset(height() - dockWindow->minimumHeight()); + } + + if (dockWindows.isEmpty()) { + dockWindows.append(dockWindow); + } else { + int dockLine = -1; + bool insertLine = false; + int i = 0; + QRect lineRect; + // find the line which we touched with the mouse + for (QList<QRect>::Iterator it = lines.begin(); it != lines.end(); ++it, ++i) { + if (point_pos(pos, orientation(), true) >= point_pos((*it).topLeft(), orientation(), true) && + point_pos(pos, orientation(), true) <= point_pos((*it).topLeft(), orientation(), true) + + size_extent((*it).size(), orientation(), true)) { + dockLine = i; + lineRect = *it; + break; + } + } + if (dockLine == -1) { // outside the dock... + insertLine = true; + if (point_pos(pos, orientation(), true) < 0) // insert as first line + dockLine = 0; + else + dockLine = (int)lines.count(); // insert after the last line ### size_t/int cast + } else { // inside the dock (we have found a dockLine) + if (point_pos(pos, orientation(), true) < + point_pos(lineRect.topLeft(), orientation(), true) + 4) { // mouse was at the very beginning of the line + insertLine = true; // insert a new line before that with the docking widget + } else if (point_pos(pos, orientation(), true) > + point_pos(lineRect.topLeft(), orientation(), true) + + size_extent(lineRect.size(), orientation(), true) - 4) { // mouse was at the very and of the line + insertLine = true; // insert a line after that with the docking widget + dockLine++; + } + } + + if (!insertLine && wasAloneInLine && lr.contains(pos)) // if we are alone in a line and just moved in there, re-insert it + insertLine = true; + +#if defined(QDOCKAREA_DEBUG) + qDebug("insert in line %d, and insert that line: %d", dockLine, insertLine); + qDebug(" (btw, we have %d lines)", lines.count()); +#endif + Q3DockWindow *dw = 0; + if (dockLine >= (int)lines.count()) { // insert after last line + dockWindows.append(dockWindow); + dockWindow->setNewLine(true); +#if defined(QDOCKAREA_DEBUG) + qDebug("insert at the end"); +#endif + } else if (dockLine == 0 && insertLine) { // insert before first line + dockWindows.insert(0, dockWindow); + dockWindows.at(1)->setNewLine(true); +#if defined(QDOCKAREA_DEBUG) + qDebug("insert at the begin"); +#endif + } else { // insert somewhere in between + // make sure each line start has a new line + for (int i = 0; i < lineStarts.size(); ++i) { + dw = lineStarts.at(i); + dw->setNewLine(true); + } + + // find the index of the first widget in the search line + int searchLine = dockLine; +#if defined(QDOCKAREA_DEBUG) + qDebug("search line start of %d", searchLine); +#endif + Q3DockWindow *lsw = lineStarts.at(searchLine); + int index = dockWindows.indexOf(lsw); + if (index == -1) { // the linestart widget hasn't been found, try to find it harder + if (lsw == w && dockWindowIndex <= dockWindows.count()) + index = dockWindowIndex; + else + index = 0; + } +#if defined(QDOCKAREA_DEBUG) + qDebug(" which starts at %d", index); +#endif + if (!insertLine) { // if we insert the docking widget in the existing line + // find the index for the widget + bool inc = true; + bool firstTime = true; + for (int i = index; i < dockWindows.size(); ++i) { + dw = dockWindows.at(i); + if (orientation() == Qt::Horizontal) + dw->setFixedExtentWidth(-1); + else + dw->setFixedExtentHeight(-1); + if (!firstTime && lineStarts.contains(dw)) // we are in the next line, so break + break; + if (point_pos(pos, orientation()) < + point_pos(fix_pos(dw), orientation()) + size_extent(dw->size(), orientation()) / 2) { + inc = false; + } + if (inc) + index++; + firstTime = false; + } +#if defined(QDOCKAREA_DEBUG) + qDebug("insert at index: %d", index); +#endif + // if we insert it just before a widget which has a new line, transfer the newline to the docking widget + // but not if we didn't only mave a widget in its line which was alone in the line before + if (!(wasAloneInLine && lr.contains(pos)) + && index >= 0 && index < dockWindows.count() && + dockWindows.at(index)->newLine() && lineOf(index) == dockLine) { +#if defined(QDOCKAREA_DEBUG) + qDebug("get rid of the old newline and get me one"); +#endif + dockWindows.at(index)->setNewLine(false); + dockWindow->setNewLine(true); + } else if (wasAloneInLine && lr.contains(pos)) { + dockWindow->setNewLine(true); + } else { // if we are somewhere in a line, get rid of the newline + dockWindow->setNewLine(false); + } + } else { // insert in a new line, so make sure the dock widget and the widget which will be after it have a newline +#if defined(QDOCKAREA_DEBUG) + qDebug("insert a new line"); +#endif + if (index < dockWindows.count()) { +#if defined(QDOCKAREA_DEBUG) + qDebug("give the widget at %d a newline", index); +#endif + Q3DockWindow* nldw = dockWindows.at(index); + if (nldw) + nldw->setNewLine(true); + } +#if defined(QDOCKAREA_DEBUG) + qDebug("give me a newline"); +#endif + dockWindow->setNewLine(true); + } + // finally insert the widget + dockWindows.insert(index, dockWindow); + } + } + + if (mse != -10 && w->isResizeEnabled()) { + if (orientation() != Qt::Horizontal) + w->setFixedExtentWidth(qMin(qMax(w->minimumWidth(), mse), w->sizeHint().width())); + else + w->setFixedExtentHeight(qMin(qMax(w->minimumHeight(), mse), w->sizeHint().height())); + } + + updateLayout(); + setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum, + orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum)); +} + +/*! + Removes the dock window \a w from the dock area. If \a + makeFloating is true, \a w gets floated, and if \a swap is true, + the orientation of \a w gets swapped. If \a fixNewLines is true + (the default) newlines in the area will be fixed. + + You should never need to call this function yourself. Use + Q3DockWindow::dock() and Q3DockWindow::undock() instead. +*/ + +void Q3DockArea::removeDockWindow(Q3DockWindow *w, bool makeFloating, bool swap, bool fixNewLines) +{ + w->removeEventFilter(this); + Q3DockWindow *dockWindow = 0; + int i = findDockWindow(w); + if (i == -1) + return; + dockWindow = dockWindows.at(i); + dockWindows.removeAt(i); + QList<Q3DockWindow *> lineStarts = layout->lineStarts(); + if (fixNewLines && lineStarts.contains(dockWindow) && i < dockWindows.count()) + dockWindows.at(i)->setNewLine(true); + if (makeFloating) { + QWidget *p = parentWidget() ? parentWidget() : window(); + dockWindow->setParent(p, Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_Tool); + dockWindow->move(0, 0); + } + if (swap) + dockWindow->resize(dockWindow->height(), dockWindow->width()); + updateLayout(); + if (dockWindows.isEmpty()) + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); +} + +int Q3DockArea::findDockWindow(Q3DockWindow *w) +{ + return dockWindows.indexOf(w); +} + +void Q3DockArea::updateLayout() +{ + layout->invalidate(); + layout->activate(); +} + +/*! \reimp + */ + +bool Q3DockArea::eventFilter(QObject *o, QEvent *e) +{ + if (e->type() == QEvent::Close) { + if (qobject_cast<Q3DockWindow*>(o)) { + o->removeEventFilter(this); + QApplication::sendEvent(o, e); + if (((QCloseEvent*)e)->isAccepted()) + removeDockWindow((Q3DockWindow*)o, false, false); + return true; + } + } + return false; +} + +/*! \internal + + Invalidates the offset of the next dock window in the dock area. + */ + +void Q3DockArea::invalidNextOffset(Q3DockWindow *dw) +{ + int i = dockWindows.indexOf(dw); + if (i == -1 || i >= (int)dockWindows.count() - 1) + return; + if ((dw = dockWindows.at(++i))) + dw->setOffset(0); +} + +/*! + \property Q3DockArea::count + \brief the number of dock windows in the dock area +*/ +int Q3DockArea::count() const +{ + return dockWindows.count(); +} + +/*! + \property Q3DockArea::empty + \brief whether the dock area is empty +*/ + +bool Q3DockArea::isEmpty() const +{ + return dockWindows.isEmpty(); +} + + +/*! + Returns a list of the dock windows in the dock area. +*/ + +QList<Q3DockWindow *> Q3DockArea::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 Q3DockArea::lineUp(bool keepNewLines) +{ + for (int i = 0; i < dockWindows.size(); ++i) { + Q3DockWindow *dw = dockWindows.at(i); + dw->setOffset(0); + if (!keepNewLines) + dw->setNewLine(false); + } + layout->activate(); +} + +Q3DockArea::DockWindowData *Q3DockArea::dockWindowData(Q3DockWindow *w) +{ + DockWindowData *data = new DockWindowData; + data->index = findDockWindow(w); + if (data->index == -1) { + delete data; + return 0; + } + QList<Q3DockWindow *> lineStarts = layout->lineStarts(); + int i = -1; + for (int j = 0; j < dockWindows.size(); ++j) { + Q3DockWindow *dw = dockWindows.at(j); + if (lineStarts.contains(dw)) + ++i; + if (dw == w) + break; + } + data->line = i; + data->offset = point_pos(QPoint(fix_x(w), w->y()), orientation()); + data->area = this; + data->fixedExtent = w->fixedExtent(); + return data; +} + +void Q3DockArea::dockWindow(Q3DockWindow *dockWindow, DockWindowData *data) +{ + if (!data) + return; + + dockWindow->setParent(this); + dockWindow->move(0, 0); + + dockWindow->installEventFilter(this); + dockWindow->dockArea = this; + dockWindow->updateGui(); + + if (dockWindows.isEmpty()) { + dockWindows.append(dockWindow); + } else { + QList<Q3DockWindow *> lineStarts = layout->lineStarts(); + int index = 0; + if (lineStarts.count() > data->line) + index = dockWindows.indexOf(lineStarts.at(data->line)); + if (index == -1) + index = 0; + bool firstTime = true; + int offset = data->offset; + for (int i = index; i < dockWindows.size(); ++i) { + Q3DockWindow *dw = dockWindows.at(i); + if (!firstTime && lineStarts.contains(dw)) + break; + if (offset < + point_pos(fix_pos(dw), orientation()) + size_extent(dw->size(), orientation()) / 2) + break; + index++; + firstTime = false; + } + if (index >= 0 && index < dockWindows.count() && + dockWindows.at(index)->newLine() && lineOf(index) == data->line) { + dockWindows.at(index)->setNewLine(false); + dockWindow->setNewLine(true); + } else { + dockWindow->setNewLine(false); + } + + dockWindows.insert(index, dockWindow); + } + dockWindow->show(); + + dockWindow->setFixedExtentWidth(data->fixedExtent.width()); + dockWindow->setFixedExtentHeight(data->fixedExtent.height()); + + updateLayout(); + setSizePolicy(QSizePolicy(orientation() == Qt::Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum, + orientation() == Qt::Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum)); + +} + +/*! + Returns true if dock window \a dw could be docked into the dock + area; otherwise returns false. + + \sa setAcceptDockWindow() +*/ + +bool Q3DockArea::isDockWindowAccepted(Q3DockWindow *dw) +{ + if (!dw) + return false; + if (forbiddenWidgets.contains(dw)) + return false; + + Q3MainWindow *mw = qobject_cast<Q3MainWindow*>(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 Q3DockArea::setAcceptDockWindow(Q3DockWindow *dw, bool accept) +{ + if (accept) + forbiddenWidgets.removeAll(dw); + else if (forbiddenWidgets.contains(dw)) + forbiddenWidgets.append(dw); +} + +void Q3DockArea::invalidateFixedSizes() +{ + for (int i = 0; i < dockWindows.size(); ++i) { + Q3DockWindow *dw = dockWindows.at(i); + if (orientation() == Qt::Horizontal) + dw->setFixedExtentWidth(-1); + else + dw->setFixedExtentHeight(-1); + } +} + +int Q3DockArea::maxSpace(int hint, Q3DockWindow *dw) +{ + int index = findDockWindow(dw); + if (index == -1 || index + 1 >= (int)dockWindows.count()) { + if (orientation() == Qt::Horizontal) + return dw->width(); + return dw->height(); + } + + Q3DockWindow *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() == Qt::Horizontal) + return dw->width(); + return dw->height(); + } + int min = 0; + Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(w); + if (orientation() == Qt::Horizontal) { + w->setFixedExtentWidth(-1); + if (!tb) + min = qMax(w->minimumSize().width(), w->minimumSizeHint().width()); + else + min = w->sizeHint().width(); + } else { + w->setFixedExtentHeight(-1); + if (!tb) + min = qMax(w->minimumSize().height(), w->minimumSizeHint().height()); + else + min = w->sizeHint().height(); + } + + int diff = hint - (orientation() == Qt::Horizontal ? dw->width() : dw->height()); + + if ((orientation() == Qt::Horizontal ? w->width() : w->height()) - diff < min) + hint = (orientation() == Qt::Horizontal ? dw->width() : dw->height()) + (orientation() == Qt::Horizontal ? w->width() : w->height()) - min; + + diff = hint - (orientation() == Qt::Horizontal ? dw->width() : dw->height()); + if (orientation() == Qt::Horizontal) + w->setFixedExtentWidth(w->width() - diff); + else + w->setFixedExtentHeight(w->height() - diff); + return hint; +} + +void Q3DockArea::setFixedExtent(int d, Q3DockWindow *dw) +{ + QList<Q3DockWindow *> lst; + for (int i = 0; i < dockWindows.size(); ++i) { + Q3DockWindow *w = dockWindows.at(i); + if (w->isHidden()) + continue; + if (orientation() == Qt::Horizontal) { + if (dw->y() != w->y()) + continue; + } else { + if (dw->x() != w->x()) + continue; + } + if (orientation() == Qt::Horizontal) + d = qMax(d, w->minimumHeight()); + else + d = qMax(d, w->minimumWidth()); + if (w->isResizeEnabled()) + lst.append(w); + } + for (int i = 0; i < lst.size(); ++i) { + Q3DockWindow *w = lst.at(i); + if (orientation() == Qt::Horizontal) + w->setFixedExtentHeight(d); + else + w->setFixedExtentWidth(d); + } +} + +bool Q3DockArea::isLastDockWindow(Q3DockWindow *dw) +{ + int i = dockWindows.indexOf(dw); + if (i == -1 || i >= (int)dockWindows.count() - 1) + return true; + Q3DockWindow *w = 0; + if ((w = dockWindows.at(++i))) { + if (orientation() == Qt::Horizontal && dw->y() < w->y()) + return true; + if (orientation() == Qt::Vertical && dw->x() < w->x()) + return true; + } else { + return true; + } + return false; +} + +#ifndef QT_NO_TEXTSTREAM + +/*! + \relates Q3DockArea + + Writes the layout of the dock windows in dock area \a dockArea to + the text stream \a ts. +*/ + +QTextStream &operator<<(QTextStream &ts, const Q3DockArea &dockArea) +{ + QString str; + QList<Q3DockWindow *> l = dockArea.dockWindowList(); + + for (int i = 0; i < l.size(); ++i) { + Q3DockWindow *dw = l.at(i); + str += QLatin1Char('[') + QString(dw->windowTitle()) + QLatin1Char(',') + QString::number((int)dw->offset()) + + QLatin1Char(',') + QString::number((int)dw->newLine()) + QLatin1Char(',') + QString::number(dw->fixedExtent().width()) + + QLatin1Char(',') + QString::number(dw->fixedExtent().height()) + QLatin1Char(',') + QString::number((int)!dw->isHidden()) + QLatin1Char(']'); + } + ts << str << endl; + + return ts; +} + +/*! + \relates Q3DockArea + + 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. +*/ + +QTextStream &operator>>(QTextStream &ts, Q3DockArea &dockArea) +{ + QString s = ts.readLine(); + + QString name, offset, newLine, width, height, visible; + + enum State { Pre, Name, Offset, NewLine, Width, Height, Visible, Post }; + int state = Pre; + QChar c; + QList<Q3DockWindow *> l = dockArea.dockWindowList(); + + for (int i = 0; i < s.length(); ++i) { + c = s[i]; + if (state == Pre && c == QLatin1Char('[')) { + state++; + continue; + } + if (c == QLatin1Char(',') && + (state == Name || state == Offset || state == NewLine || state == Width || state == Height)) { + state++; + continue; + } + if (state == Visible && c == QLatin1Char(']')) { + for (int j = 0; j < l.size(); ++j) { + Q3DockWindow *dw = l.at(j); + if (QString(dw->windowTitle()) == 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 = QLatin1String(""); + + state = Pre; + continue; + } + if (state == Name) + name += c; + else if (state == Offset) + offset += c; + else if (state == NewLine) + newLine += c; + else if (state == Width) + width += c; + else if (state == Height) + height += c; + else if (state == Visible) + visible += c; + } + + dockArea.QWidget::layout()->invalidate(); + dockArea.QWidget::layout()->activate(); + return ts; +} +#endif + +QT_END_NAMESPACE + +#endif //QT_NO_MAINWINDOW diff --git a/src/qt3support/widgets/q3dockarea.h b/src/qt3support/widgets/q3dockarea.h new file mode 100644 index 0000000..20aba63 --- /dev/null +++ b/src/qt3support/widgets/q3dockarea.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DOCKAREA_H +#define Q3DOCKAREA_H + +#include <QtGui/qwidget.h> +#include <QtCore/qlist.h> +#include <Qt3Support/q3dockwindow.h> +#include <QtGui/qlayout.h> +#include <QtCore/qpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_MAINWINDOW + +class QBoxLayout; +class Q3DockAreaLayout; +class QMouseEvent; +class Q3DockWindowResizeHandle; +class Q3DockAreaPrivate; +class QTextStream; + +class Q_COMPAT_EXPORT Q3DockAreaLayout : public QLayout +{ + Q_OBJECT + friend class Q3DockArea; + +public: + Q3DockAreaLayout(QWidget* parent, Qt::Orientation o, QList<Q3DockWindow *> *wl, int space = -1, int margin = -1, const char *name = 0) + : QLayout(parent), orient(o), dirty(true), dockWindows(wl), parentWidget(parent) + { + if (space != -1) + setSpacing(space); + if (margin != -1) + setMargin(margin); + setObjectName(QString::fromAscii(name)); + init(); + } + ~Q3DockAreaLayout() {} + + void addItem(QLayoutItem *) {} + bool hasHeightForWidth() const; + int heightForWidth(int) const; + int widthForHeight(int) const; + QSize sizeHint() const; + QSize minimumSize() const; + QLayoutItem *itemAt(int) const; + QLayoutItem *takeAt(int); + int count() const; + Qt::Orientations expandingDirections() const { return Qt::Orientations(0); } + void invalidate(); + Qt::Orientation orientation() const { return orient; } + QList<QRect> lineList() const { return lines; } + QList<Q3DockWindow *> lineStarts() const { return ls; } + +protected: + void setGeometry(const QRect&); + +private: + Q_DISABLE_COPY(Q3DockAreaLayout) + + void init(); + int layoutItems(const QRect&, bool testonly = false); + Qt::Orientation orient; + bool dirty; + int cached_width, cached_height; + int cached_hfw, cached_wfh; + QList<Q3DockWindow *> *dockWindows; + QWidget *parentWidget; + QList<QRect> lines; + QList<Q3DockWindow *> ls; +}; + +class Q_COMPAT_EXPORT Q3DockArea : public QWidget +{ + Q_OBJECT + Q_ENUMS(HandlePosition) + Q_PROPERTY(Qt::Orientation orientation READ orientation) + Q_PROPERTY(int count READ count) + Q_PROPERTY(bool empty READ isEmpty) + Q_PROPERTY(HandlePosition handlePosition READ handlePosition) + + friend class Q3DockWindow; + friend class Q3DockWindowResizeHandle; + friend class Q3DockAreaLayout; + +public: + enum HandlePosition { Normal, Reverse }; + + Q3DockArea(Qt::Orientation o, HandlePosition h = Normal, QWidget* parent=0, const char* name=0); + ~Q3DockArea(); + + void moveDockWindow(Q3DockWindow *w, const QPoint &globalPos, const QRect &rect, bool swap); + void removeDockWindow(Q3DockWindow *w, bool makeFloating, bool swap, bool fixNewLines = true); + void moveDockWindow(Q3DockWindow *w, int index = -1); + bool hasDockWindow(Q3DockWindow *w, int *index = 0); + + void invalidNextOffset(Q3DockWindow *dw); + + Qt::Orientation orientation() const { return orient; } + HandlePosition handlePosition() const { return hPos; } + + bool eventFilter(QObject *, QEvent *); + bool isEmpty() const; + int count() const; + QList<Q3DockWindow *> dockWindowList() const; + + bool isDockWindowAccepted(Q3DockWindow *dw); + void setAcceptDockWindow(Q3DockWindow *dw, bool accept); + +public Q_SLOTS: + void lineUp(bool keepNewLines); + +private: + struct DockWindowData + { + int index; + int offset; + int line; + QSize fixedExtent; + QPointer<Q3DockArea> area; + }; + + int findDockWindow(Q3DockWindow *w); + int lineOf(int index); + DockWindowData *dockWindowData(Q3DockWindow *w); + void dockWindow(Q3DockWindow *dockWindow, DockWindowData *data); + void updateLayout(); + void invalidateFixedSizes(); + int maxSpace(int hint, Q3DockWindow *dw); + void setFixedExtent(int d, Q3DockWindow *dw); + bool isLastDockWindow(Q3DockWindow *dw); + +private: + Q_DISABLE_COPY(Q3DockArea) + + Qt::Orientation orient; + QList<Q3DockWindow *> dockWindows; + Q3DockAreaLayout *layout; + HandlePosition hPos; + QList<Q3DockWindow *> forbiddenWidgets; + Q3DockAreaPrivate *d; +}; + +#ifndef QT_NO_TEXTSTREAM +Q_COMPAT_EXPORT QTextStream &operator<<(QTextStream &, const Q3DockArea &); +Q_COMPAT_EXPORT QTextStream &operator>>(QTextStream &, Q3DockArea &); +#endif + +#endif // QT_NO_MAINWINDOW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3DOCKAREA_H diff --git a/src/qt3support/widgets/q3dockwindow.cpp b/src/qt3support/widgets/q3dockwindow.cpp new file mode 100644 index 0000000..7bb2275 --- /dev/null +++ b/src/qt3support/widgets/q3dockwindow.cpp @@ -0,0 +1,2115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3dockwindow.h" + +#ifndef QT_NO_MAINWINDOW +#include "qapplication.h" +#include "qcursor.h" +#include "qdesktopwidget.h" +#include "q3dockarea.h" +#include "qevent.h" +#include "qlayout.h" +#include "q3mainwindow.h" +#include "qpainter.h" +#include "qpointer.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtimer.h" +#include "q3toolbar.h" +#include "qtoolbutton.h" +#include "qtooltip.h" +#include <private/q3titlebar_p.h> +#include <private/qwidgetresizehandler_p.h> +#include <qrubberband.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +#ifdef Q_WS_MAC +static bool default_opaque = true; +#else +static bool default_opaque = false; +#endif + +class Q3DockWindowPrivate +{ +}; + +class Q3DockWindowResizeHandle : public QWidget +{ + Q_OBJECT + +public: + Q3DockWindowResizeHandle(Qt::Orientation o, QWidget *parent, Q3DockWindow *w, const char* /*name*/=0); + void setOrientation(Qt::Orientation o); + Qt::Orientation orientation() const { return orient; } + + QSize sizeHint() const; + +protected: + void paintEvent(QPaintEvent *); + void mouseMoveEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + bool event(QEvent *event); + +private: + void startLineDraw(); + void endLineDraw(); + void drawLine(const QPoint &globalPos); + +private: + Qt::Orientation orient; + bool mousePressed; + QRubberBand *rubberBand; + QPoint lastPos, firstPos; + Q3DockWindow *dockWindow; + bool mouseOver; +}; + +Q3DockWindowResizeHandle::Q3DockWindowResizeHandle(Qt::Orientation o, QWidget *parent, + Q3DockWindow *w, const char *) + : QWidget(parent, "qt_dockwidget_internal"), mousePressed(false), rubberBand(0), dockWindow(w), + mouseOver(false) +{ + setOrientation(o); +} + +QSize Q3DockWindowResizeHandle::sizeHint() const +{ + QStyleOptionQ3DockWindow opt; + opt.init(this); + if (!dockWindow->area() || dockWindow->area()->orientation() == Qt::Horizontal) + opt.state |= QStyle::State_Horizontal; + + opt.rect = rect(); + opt.docked = dockWindow->area(); + opt.closeEnabled = dockWindow->isCloseEnabled(); + int sw = 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, &opt, this) / 3; + return (style()->sizeFromContents(QStyle::CT_Q3DockWindow, &opt, QSize(sw, sw), this).expandedTo(QApplication::globalStrut())); +} + +void Q3DockWindowResizeHandle::setOrientation(Qt::Orientation o) +{ + orient = o; + if (o == Qt::Horizontal) { +#ifndef QT_NO_CURSOR + setCursor(Qt::splitVCursor); +#endif + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + } else { +#ifndef QT_NO_CURSOR + setCursor(Qt::splitHCursor); +#endif + setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding)); + } +} + +void Q3DockWindowResizeHandle::mousePressEvent(QMouseEvent *e) +{ + e->ignore(); + if (e->button() != Qt::LeftButton) + return; + e->accept(); + mousePressed = true; + if (!dockWindow->opaqueMoving()) + startLineDraw(); + lastPos = firstPos = e->globalPos(); + if (!dockWindow->opaqueMoving()) + drawLine(e->globalPos()); +} + +void Q3DockWindowResizeHandle::mouseMoveEvent(QMouseEvent *e) +{ + if (!mousePressed) + return; + if (!dockWindow->opaqueMoving()) { + if (orientation() != dockWindow->area()->orientation()) { + if (orientation() == Qt::Horizontal) { + int minpos = dockWindow->area()->mapToGlobal(QPoint(0, 0)).y(); + int maxpos = dockWindow->area()->mapToGlobal(QPoint(0, 0)).y() + dockWindow->area()->height(); + if (e->globalPos().y() < minpos || e->globalPos().y() > maxpos) + return; + } else { + int minpos = dockWindow->area()->mapToGlobal(QPoint(0, 0)).x(); + int maxpos = dockWindow->area()->mapToGlobal(QPoint(0, 0)).x() + dockWindow->area()->width(); + if (e->globalPos().x() < minpos || e->globalPos().x() > maxpos) + return; + } + } else { + QWidget *w = dockWindow->area()->window(); + if (w) { + if (orientation() == Qt::Horizontal) { + int minpos = w->mapToGlobal(QPoint(0, 0)).y(); + int maxpos = w->mapToGlobal(QPoint(0, 0)).y() + w->height(); + if (e->globalPos().y() < minpos || e->globalPos().y() > maxpos) + return; + } else { + int minpos = w->mapToGlobal(QPoint(0, 0)).x(); + int maxpos = w->mapToGlobal(QPoint(0, 0)).x() + w->width(); + if (e->globalPos().x() < minpos || e->globalPos().x() > maxpos) + return; + } + } + } + } + + if (!dockWindow->opaqueMoving()) + drawLine(lastPos); + lastPos = e->globalPos(); + if (dockWindow->opaqueMoving()) { + mouseReleaseEvent(e); + mousePressed = true; + firstPos = e->globalPos(); + } + if (!dockWindow->opaqueMoving()) + drawLine(e->globalPos()); +} + +void Q3DockWindowResizeHandle::mouseReleaseEvent(QMouseEvent *e) +{ + if (mousePressed) { + if (!dockWindow->opaqueMoving()) { + drawLine(lastPos); + endLineDraw(); + } + if (orientation() != dockWindow->area()->orientation()) + dockWindow->area()->invalidNextOffset(dockWindow); + if (orientation() == Qt::Horizontal) { + int dy; + if (dockWindow->area()->handlePosition() == Q3DockArea::Normal || orientation() != dockWindow->area()->orientation()) + dy = e->globalPos().y() - firstPos.y(); + else + dy = firstPos.y() - e->globalPos().y(); + int d = dockWindow->height() + dy; + if (orientation() != dockWindow->area()->orientation()) { + dockWindow->setFixedExtentHeight(-1); + d = qMax(d, dockWindow->minimumHeight()); + int ms = dockWindow->area()->maxSpace(d, dockWindow); + d = qMin(d, ms); + dockWindow->setFixedExtentHeight(d); + } else { + dockWindow->area()->setFixedExtent(d, dockWindow); + } + } else { + int dx; + if (dockWindow->area()->handlePosition() == Q3DockArea::Normal || orientation() != dockWindow->area()->orientation()) + dx = e->globalPos().x() - firstPos.x(); + else + dx = firstPos.x() - e->globalPos().x(); + int d = dockWindow->width() + dx; + if (orientation() != dockWindow->area()->orientation()) { + dockWindow->setFixedExtentWidth(-1); + d = qMax(d, dockWindow->minimumWidth()); + int ms = dockWindow->area()->maxSpace(d, dockWindow); + d = qMin(d, ms); + dockWindow->setFixedExtentWidth(d); + } else { + dockWindow->area()->setFixedExtent(d, dockWindow); + } + } + } + + QApplication::postEvent(dockWindow->area(), new QEvent(QEvent::LayoutHint)); + mousePressed = false; +} + +bool Q3DockWindowResizeHandle::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::HoverEnter: + if (!mouseOver) { + mouseOver = true; + update(); + } + break; + case QEvent::HoverLeave: + if (mouseOver) { + mouseOver = false; + update(); + } + break; + default: + break; + } + return QWidget::event(event); +} + +void Q3DockWindowResizeHandle::paintEvent(QPaintEvent *) +{ + QPainter p(this); + QStyleOption opt(0); + opt.init(this); + if (orientation() == Qt::Horizontal) + opt.state |= QStyle::State_Horizontal; + style()->drawPrimitive(QStyle::PE_IndicatorDockWidgetResizeHandle, &opt, &p, this); +} + +void Q3DockWindowResizeHandle::startLineDraw() +{ + if (rubberBand) + endLineDraw(); + rubberBand = new QRubberBand(QRubberBand::Line); + rubberBand->setGeometry(-1, -1, 1, 1); + rubberBand->show(); +} + +void Q3DockWindowResizeHandle::endLineDraw() +{ + delete rubberBand; + rubberBand = 0; +} + +void Q3DockWindowResizeHandle::drawLine(const QPoint &globalPos) +{ + QPoint start = mapToGlobal(QPoint(0, 0)); + QPoint starta = dockWindow->area()->mapToGlobal(QPoint(0, 0)); + QPoint end = globalPos; + if (orientation() == Qt::Horizontal) { + if (orientation() == dockWindow->orientation()) + rubberBand->setGeometry(starta.x(), end.y(), dockWindow->area()->width(), height()); + else + rubberBand->setGeometry(start.x(), end.y(), width(), height()); + } else { + if (orientation() == dockWindow->orientation()) + rubberBand->setGeometry(end.x(), starta.y(), width(), dockWindow->area()->height()); + else + rubberBand->setGeometry(end.x(), start.y(), width(), height()); + } +} + +static QPoint realWidgetPos(Q3DockWindow *w) +{ + if (!w->parentWidget() || w->place() == Q3DockWindow::OutsideDock) + return w->pos(); + return w->parentWidget()->mapToGlobal(w->geometry().topLeft()); +} + +class Q3DockWindowHandle : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QString windowTitle READ windowTitle) + friend class Q3DockWindow; + friend class Q3DockWindowTitleBar; + +public: + Q3DockWindowHandle(Q3DockWindow *dw); + void updateGui(); + + QSize minimumSizeHint() const; + QSize minimumSize() const { return minimumSizeHint(); } + QSize sizeHint() const { return minimumSize(); } + void setOpaqueMoving(bool b) { opaque = b; } + + QString windowTitle() const { return dockWindow->windowTitle(); } + +signals: + void doubleClicked(); + +protected: + void paintEvent(QPaintEvent *e); + void resizeEvent(QResizeEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + void keyReleaseEvent(QKeyEvent *e); + void changeEvent(QEvent *); + +private slots: + void minimize(); + +private: + Q3DockWindow *dockWindow; + QPoint offset; + QToolButton *closeButton; + QTimer *timer; + uint opaque : 1; + uint mousePressed : 1; + uint hadDblClick : 1; + uint ctrlDown : 1; + QPointer<QWidget> oldFocus; +}; + +class Q3DockWindowTitleBar : public Q3TitleBar +{ + Q_OBJECT + friend class Q3DockWindow; + friend class Q3DockWindowHandle; + +public: + Q3DockWindowTitleBar(Q3DockWindow *dw); + void updateGui(); + void setOpaqueMoving(bool b) { opaque = b; } + +protected: + void resizeEvent(QResizeEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + void keyReleaseEvent(QKeyEvent *e); + +private: + Q3DockWindow *dockWindow; + QPoint offset; + uint mousePressed : 1; + uint hadDblClick : 1; + uint opaque : 1; + uint ctrlDown : 1; + QPointer<QWidget> oldFocus; + +}; + +Q3DockWindowHandle::Q3DockWindowHandle(Q3DockWindow *dw) + : QWidget(dw, "qt_dockwidget_internal"), dockWindow(dw), + closeButton(0), opaque(default_opaque), mousePressed(false) +{ + ctrlDown = false; + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(minimize())); +#ifdef Q_WS_WIN + setCursor(Qt::SizeAllCursor); +#endif +} + +void Q3DockWindowHandle::paintEvent(QPaintEvent *e) +{ + if (!dockWindow->dockArea && !opaque) + return; + QPainter p(this); + QStyleOptionQ3DockWindow opt; + opt.init(this); + if (!dockWindow->area() || dockWindow->area()->orientation() == Qt::Horizontal) + opt.state |= QStyle::State_Horizontal; + + opt.rect = rect(); + opt.docked = dockWindow->area(); + opt.closeEnabled = dockWindow->isCloseEnabled(); + opt.rect = QStyle::visualRect(opt.direction, opt.rect, + style()->subElementRect(QStyle::SE_Q3DockWindowHandleRect, &opt, this)); + style()->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &p, this); + QWidget::paintEvent(e); +} + +void Q3DockWindowHandle::keyPressEvent(QKeyEvent *e) +{ + if (!mousePressed) + return; + if (e->key() == Qt::Key_Control) { + ctrlDown = true; + dockWindow->handleMove(mapFromGlobal(QCursor::pos()) - offset, QCursor::pos(), !opaque); + } +} + +void Q3DockWindowHandle::keyReleaseEvent(QKeyEvent *e) +{ + if (!mousePressed) + return; + if (e->key() == Qt::Key_Control) { + ctrlDown = false; + dockWindow->handleMove(mapFromGlobal(QCursor::pos()) - offset, QCursor::pos(), !opaque); + } +} + +void Q3DockWindowHandle::mousePressEvent(QMouseEvent *e) +{ + if (!dockWindow->dockArea) + return; + ctrlDown = (e->state() & Qt::ControlButton) == Qt::ControlButton; + oldFocus = qApp->focusWidget(); + setFocus(); + e->ignore(); + if (e->button() != Qt::LeftButton) + return; + e->accept(); + hadDblClick = false; + mousePressed = true; + offset = e->pos(); + dockWindow->startRectDraw(mapToGlobal(e->pos()), !opaque); + if (!opaque) + qApp->installEventFilter(dockWindow); +} + +void Q3DockWindowHandle::mouseMoveEvent(QMouseEvent *e) +{ + if (!mousePressed || e->pos() == offset) + return; + ctrlDown = (e->state() & Qt::ControlButton) == Qt::ControlButton; + dockWindow->handleMove(e->pos() - offset, e->globalPos(), !opaque); + if (opaque) + dockWindow->updatePosition(e->globalPos()); +} + +void Q3DockWindowHandle::mouseReleaseEvent(QMouseEvent *e) +{ + ctrlDown = false; + qApp->removeEventFilter(dockWindow); + if (oldFocus) + oldFocus->setFocus(); + if (!mousePressed) + return; + dockWindow->endRectDraw(!opaque); + mousePressed = false; +#ifdef Q_WS_MAC + releaseMouse(); +#endif + if (!hadDblClick && offset == e->pos()) { + timer->start(QApplication::doubleClickInterval(), true); + } else if (!hadDblClick) { + dockWindow->updatePosition(e->globalPos()); + } + if (opaque) + dockWindow->titleBar->mousePressed = false; + if (dockWindow->parentWidget()) + QApplication::postEvent(dockWindow->parentWidget(), new QEvent(QEvent::LayoutHint)); +} + +void Q3DockWindowHandle::minimize() +{ + if (!dockWindow->area()) + return; + + Q3MainWindow *mw = qobject_cast<Q3MainWindow*>(dockWindow->area()->parentWidget()); + if (mw && mw->isDockEnabled(dockWindow, Qt::DockMinimized)) + mw->moveDockWindow(dockWindow, Qt::DockMinimized); +} + +void Q3DockWindowHandle::resizeEvent(QResizeEvent *) +{ + updateGui(); +} + +void Q3DockWindowHandle::updateGui() +{ + updateGeometry(); + + if (!closeButton) { + closeButton = new QToolButton(this, "qt_close_button1"); +#ifndef QT_NO_CURSOR + closeButton->setCursor(Qt::ArrowCursor); +#endif + QStyleOption opt(0); + opt.init(closeButton); + closeButton->setIcon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton, &opt, + 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() == Qt::Horizontal) { + int off = (width() - closeButton->width() - 1) / 2; + closeButton->move(off, 2); + } else { + int off = (height() - closeButton->height() - 1) / 2; + int x = QApplication::reverseLayout() ? 2 : width() - closeButton->width() - 2; + closeButton->move(x, off); + } +} + +void Q3DockWindowHandle::changeEvent(QEvent *ev) +{ + if(ev->type() == QEvent::StyleChange) { + if (closeButton) { + QStyleOption opt(0); + opt.init(closeButton); + closeButton->setIcon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton, + &opt, closeButton)); + } + } + QWidget::changeEvent(ev); +} + +QSize Q3DockWindowHandle::minimumSizeHint() const +{ + if (!dockWindow->dockArea) + return QSize(0, 0); + int wh = dockWindow->isCloseEnabled() ? 17 : style()->pixelMetric(QStyle::PM_ToolBarHandleExtent, 0, this); + if (dockWindow->orientation() == Qt::Horizontal) + return QSize(wh, 0); + return QSize(0, wh); +} + +void Q3DockWindowHandle::mouseDoubleClickEvent(QMouseEvent *e) +{ + e->ignore(); + if (e->button() != Qt::LeftButton) + return; + e->accept(); + timer->stop(); + emit doubleClicked(); + hadDblClick = true; + if (dockWindow->parentWidget()) + QApplication::postEvent(dockWindow->parentWidget(), new QEvent(QEvent::LayoutHint)); +} + +Q3DockWindowTitleBar::Q3DockWindowTitleBar(Q3DockWindow *dw) + : Q3TitleBar(0, dw), dockWindow(dw), + mousePressed(false), hadDblClick(false), opaque(default_opaque) +{ + setObjectName(QLatin1String("qt_dockwidget_internal")); + ctrlDown = false; + setMouseTracking(true); + QStyleOptionTitleBar opt = getStyleOption(); + setFixedHeight(style()->pixelMetric(QStyle::PM_TitleBarHeight, &opt, this)); + connect(this, SIGNAL(doClose()), dockWindow, SLOT(hide())); +} + +void Q3DockWindowTitleBar::keyPressEvent(QKeyEvent *e) +{ + if (!mousePressed) + return; + if (e->key() == Qt::Key_Control) { + ctrlDown = true; + dockWindow->handleMove(mapFromGlobal(QCursor::pos()) - offset, QCursor::pos(), !opaque); + } +} + +void Q3DockWindowTitleBar::keyReleaseEvent(QKeyEvent *e) +{ + if (!mousePressed) + return; + if (e->key() == Qt::Key_Control) { + ctrlDown = false; + dockWindow->handleMove(mapFromGlobal(QCursor::pos()) - offset, QCursor::pos(), !opaque); + } +} + +void Q3DockWindowTitleBar::mousePressEvent(QMouseEvent *e) +{ + QStyleOptionTitleBar opt; + opt.init(this); + opt.subControls = QStyle::SC_All; + opt.activeSubControls = QStyle::SC_None; + opt.text = windowTitle(); + //################ + QIcon icon = windowIcon(); + QSize s = icon.actualSize(QSize(64, 64)); + opt.icon = icon.pixmap(s); + opt.titleBarState = window() ? window()->windowState() : static_cast<Qt::WindowStates>(Qt::WindowNoState); + opt.titleBarFlags = fakeWindowFlags(); + QStyle::SubControl tbctrl = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt, + e->pos(), this); + + if (tbctrl < QStyle::SC_TitleBarLabel && tbctrl != QStyle::SC_None) { + Q3TitleBar::mousePressEvent(e); + return; + } + + ctrlDown = (e->state() & Qt::ControlButton) == Qt::ControlButton; + oldFocus = qApp->focusWidget(); +// setFocus activates the window, which deactivates the main window +// not what we want, and not required anyway on Windows +#ifndef Q_WS_WIN + setFocus(); +#endif + + e->ignore(); + if (e->button() != Qt::LeftButton) + return; + if (e->y() < 3 && dockWindow->isResizeEnabled()) + return; + + e->accept(); + bool oldPressed = mousePressed; + mousePressed = true; + hadDblClick = false; + offset = e->pos(); + dockWindow->startRectDraw(mapToGlobal(e->pos()), !opaque); +// grabMouse resets the Windows mouse press count, so we never receive a double click on Windows +// not required on Windows, and did work on X11, too, but no problem there in the first place +#ifndef Q_WS_WIN + if(!oldPressed && dockWindow->opaqueMoving()) + grabMouse(); +#else + Q_UNUSED(oldPressed); +#endif +} + +void Q3DockWindowTitleBar::mouseMoveEvent(QMouseEvent *e) +{ + if (!mousePressed) { + Q3TitleBar::mouseMoveEvent(e); + return; + } + + ctrlDown = (e->state() & Qt::ControlButton) == Qt::ControlButton; + e->accept(); + dockWindow->handleMove(e->pos() - offset, e->globalPos(), !opaque); +} + +void Q3DockWindowTitleBar::mouseReleaseEvent(QMouseEvent *e) +{ + if (!mousePressed) { + Q3TitleBar::mouseReleaseEvent(e); + return; + } + + ctrlDown = false; + qApp->removeEventFilter(dockWindow); + if (oldFocus) + oldFocus->setFocus(); + + if (dockWindow->place() == Q3DockWindow::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; + } + if (dockWindow->parentWidget()) + QApplication::postEvent(dockWindow->parentWidget(), new QEvent(QEvent::LayoutHint)); +} + +void Q3DockWindowTitleBar::resizeEvent(QResizeEvent *e) +{ + updateGui(); + Q3TitleBar::resizeEvent(e); +} + +void Q3DockWindowTitleBar::updateGui() +{ + if (dockWindow->isCloseEnabled()) { + setFakeWindowFlags(fakeWindowFlags() | Qt::WStyle_SysMenu); + } else { + setFakeWindowFlags(fakeWindowFlags() & ~Qt::WStyle_SysMenu); + } +} + +void Q3DockWindowTitleBar::mouseDoubleClickEvent(QMouseEvent *) +{ + emit doubleClicked(); + hadDblClick = true; + if (dockWindow->parentWidget()) + QApplication::postEvent(dockWindow->parentWidget(), new QEvent(QEvent::LayoutHint)); +} + +/*! + \class Q3DockWindow + \brief The Q3DockWindow class provides a widget which can be docked + inside a Q3DockArea or floated as a top level window on the + desktop. + + \compat + + This class handles moving, resizing, docking and undocking dock + windows. Q3ToolBar is a subclass of Q3DockWindow so the + functionality provided for dock windows is available with the same + API for toolbars. + + \img qmainwindow-qdockareas.png Q3DockWindows in a Q3DockArea + \caption Two Q3DockWindows (\l{Q3ToolBar}s) in a \l Q3DockArea + + \img qdockwindow.png A Q3DockWindow + \caption A Floating Q3DockWindow + + 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 title bar 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 appears on floating + dock windows by default) the dock window will disappear. You can + control whether or not a dock window has a close button with + setCloseMode(). + + Q3MainWindow 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 Q3MainWindow is sufficient. (See the \l + Q3DockArea documentation if you want to create your own dock + areas.) In Q3MainWindow 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 QWidget::setWindowTitle() caption\endlink.) + + When you construct a dock window you \e must pass it a Q3DockArea + or a Q3MainWindow as its parent if you want it docked. Pass 0 for + the parent if you want it floated. + + \snippet doc/src/snippets/code/src_qt3support_widgets_q3dockwindow.cpp 0 + + In the example above we create a new Q3ToolBar in the constructor + of a Q3MainWindow subclass (so that the \e this pointer points to + the Q3MainWindow). By default the toolbar will be added to the \c + Top dock area, but we've moved it to the \c Left dock area. + + A dock window is often used to contain a single widget. In these + cases the widget can be set by calling setWidget(). If you're + constructing a dock window that contains multiple widgets, e.g. a + toolbar, arrange the widgets within a box layout inside the dock + window. To do this use the boxLayout() function to get a pointer + to the dock window's box layout, then add widgets to the layout + using the box layout's QBoxLayout::addWidget() function. The dock + window will dynamically set the orientation of the layout to be + vertical or horizontal as necessary, although you can control this + yourself with setOrientation(). + + Although a common use of dock windows is for toolbars, they can be + used with any widgets. 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. + + Qt::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 Q3DockArea::moveDockWindow(). If you're using a + Q3MainWindow, Q3MainWindow::moveDockWindow() and + Q3MainWindow::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 line 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 Q3DockWindow::Place + + This enum specifies the possible locations for a Q3DockWindow: + + \value InDock Inside a Q3DockArea. + \value OutsideDock Floating as a top level window on the desktop. +*/ + +/*! + \enum Q3DockWindow::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 Q3DockWindow::setHorizontalStretchable(bool b) + + If \a b is true the dock window is set to be horizontally + stretchable. +*/ +/*! + \fn void Q3DockWindow::setVerticalStretchable(bool b) + + If \a b is true the dock window is set to be vertically + stretchable. +*/ +/*! + \fn bool Q3DockWindow::isHorizontalStretchable() const + + Returns true if the dock window can be stretched horizontally; + otherwise returns false. +*/ +/*! + \fn bool Q3DockWindow::isVerticalStretchable() const + + Returns true if the dock window can be stretched vertically; + otherwise returns false. +*/ +/*! + \fn void Q3DockWindow::orientationChanged(Qt::Orientation o) + + This signal is emitted when the orientation of the dock window is + changed. The new orientation is \a o. +*/ + +/*! + \fn void Q3DockWindow::placeChanged(Q3DockWindow::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 Q3DockArea::moveDockWindow(), Q3DockArea::removeDockWindow(), + Q3MainWindow::moveDockWindow(), Q3MainWindow::removeDockWindow() +*/ + +/*! + \fn void Q3DockWindow::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 + Q3DockWindow 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 Q3MainWindow a dock window can + have its visibility changed (hidden or shown) by clicking its name + in the dock window menu that lists the Q3MainWindow's dock windows. +*/ + +/*! + \fn Q3DockArea *Q3DockWindow::area() const + + Returns the dock area in which this dock window is docked, or 0 if + the dock window is floating. +*/ + +/*! + \property Q3DockWindow::place + \brief the location where the dock window is placed + + This is either \c InDock or \c OutsideDock. + + \sa Q3DockArea::moveDockWindow(), Q3DockArea::removeDockWindow(), + Q3MainWindow::moveDockWindow(), Q3MainWindow::removeDockWindow() +*/ + +/*! + Constructs a Q3DockWindow with parent \a parent, called \a name and + with widget flags \a f. +*/ + +Q3DockWindow::Q3DockWindow(QWidget* parent, const char* name, Qt::WindowFlags f) + : Q3Frame(parent, name, f | Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder) +{ + curPlace = InDock; + isToolbar = false; + init(); +} + +/*! + Constructs a Q3DockWindow 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 Q3DockArea or a Q3MainWindow. If the \a + parent is a Q3MainWindow 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 Q3MainWindow + as parent then calling Q3MainWindow::moveDockWindow() to move the + dock window where you want it. +*/ + +Q3DockWindow::Q3DockWindow(Place p, QWidget *parent, const char *name, Qt::WindowFlags f) + : Q3Frame(parent, name, f | Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder) +{ + curPlace = p; + isToolbar = false; + init(); +} + +/*! \internal +*/ + +Q3DockWindow::Q3DockWindow(Place p, QWidget *parent, const char *name, Qt::WindowFlags f, bool toolbar) + : Q3Frame(parent, name, f | Qt::WType_Dialog | Qt::WStyle_Customize | Qt::WStyle_NoBorder) +{ + curPlace = p; + isToolbar = toolbar; + init(); +} + +class Q3DockWindowGridLayout : public QGridLayout +{ +public: + Q3DockWindowGridLayout(QWidget *parent, int nRows, int nCols) + : QGridLayout(parent, nRows, nCols) {}; + + Qt::Orientations expandingDirections() const + { + return 0; + } +}; + +void Q3DockWindow::init() +{ + wid = 0; + rubberBand = 0; + dockArea = 0; + tmpDockArea = 0; + resizeEnabled = false; + moveEnabled = true; + nl = false; + opaque = default_opaque; + cMode = Never; + offs = 0; + fExtent = QSize(-1, -1); + dockWindowData = 0; + lastPos = QPoint(-1, -1); + lastSize = QSize(-1, -1); + stretchable[Qt::Horizontal] = false; + stretchable[Qt::Vertical] = false; + + widgetResizeHandler = new QWidgetResizeHandler(this); + widgetResizeHandler->setMovingEnabled(false); + + titleBar = new Q3DockWindowTitleBar(this); + verHandle = new Q3DockWindowHandle(this); + verHandle->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred)); + horHandle = new Q3DockWindowHandle(this); + horHandle->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + + vHandleLeft = new Q3DockWindowResizeHandle(Qt::Vertical, this, this, "vert. handle"); + vHandleRight = new Q3DockWindowResizeHandle(Qt::Vertical, this, this, "vert. handle"); + hHandleTop = new Q3DockWindowResizeHandle(Qt::Horizontal, this, this, "horz. handle"); + hHandleBottom = new Q3DockWindowResizeHandle(Qt::Horizontal, this, this, "horz. handle"); + + // Creating inner layout + hbox = new QVBoxLayout(); + vbox = new QHBoxLayout(); + childBox = new QBoxLayout(QBoxLayout::LeftToRight); + vbox->addSpacing(2); + vbox->addWidget(verHandle); + vbox->addStretch(0); + vbox->addLayout(childBox, 1); + vbox->addStretch(0); + + hbox->setResizeMode(QLayout::FreeResize); + hbox->setMargin(isResizeEnabled() || curPlace == OutsideDock ? 2 : 0); + hbox->setSpacing(1); + hbox->addWidget(titleBar); + hbox->addWidget(horHandle); + hbox->addLayout(vbox); + + // Set up the initial handle layout for Qt::Vertical + // Handle layout will change on calls to setOrienation() + QGridLayout *glayout = new Q3DockWindowGridLayout(this, 3, 3); + glayout->setResizeMode(QLayout::Minimum); + glayout->addMultiCellWidget(hHandleTop, 0, 0, 1, 1); + glayout->addMultiCellWidget(hHandleBottom, 2, 2, 1, 1); + glayout->addMultiCellWidget(vHandleLeft, 0, 2, 0, 0); + glayout->addMultiCellWidget(vHandleRight, 0, 2, 2, 2); + glayout->addLayout(hbox, 1, 1); + glayout->setRowStretch(1, 1); + glayout->setColStretch(1, 1); + + hHandleBottom->hide(); + vHandleRight->hide(); + hHandleTop->hide(); + vHandleLeft->hide(); + setFrameStyle(Q3Frame::StyledPanel | Q3Frame::Raised); + setLineWidth(2); + + if (parentWidget()) + parentWidget()->installEventFilter(this); + QWidget *mw = parentWidget(); + Q3DockArea *da = qobject_cast<Q3DockArea*>(parentWidget()); + if (da) { + if (curPlace == InDock) + da->moveDockWindow(this); + mw = da->parentWidget(); + } + if (qobject_cast<Q3MainWindow*>(mw)) { + if (place() == InDock) { + Qt::Dock myDock = Qt::DockTop; + // make sure we put the window in the correct dock. + if (dockArea) { + Q3MainWindow *mainw = (Q3MainWindow*)mw; + // I'm not checking if it matches the top because I've + // done the assignment to it above. + if (dockArea == mainw->leftDock()) + myDock = Qt::DockLeft; + else if (dockArea == mainw->rightDock()) + myDock = Qt::DockRight; + else if (dockArea == mainw->bottomDock()) + myDock = Qt::DockBottom; + } + ((Q3MainWindow*)mw)->addDockWindow(this, myDock); + } + moveEnabled = ((Q3MainWindow*)mw)->dockWindowsMovable(); + opaque = ((Q3MainWindow*)mw)->opaqueMoving(); + } + + updateGui(); + + 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(Qt::Orientation)), + this, SLOT(setOrientation(Qt::Orientation))); +} + +/*! + Sets the orientation of the dock window to \a o. The orientation + is propagated to the layout boxLayout(). + + \warning All undocked Q3ToolBars will always have a horizontal orientation. +*/ + +void Q3DockWindow::setOrientation(Qt::Orientation o) +{ + QGridLayout *glayout = (QGridLayout*)layout(); + glayout->removeWidget(hHandleTop); + glayout->removeWidget(hHandleBottom); + glayout->removeWidget(vHandleLeft); + glayout->removeWidget(vHandleRight); + + if (o == Qt::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 == Qt::Horizontal ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom); + QApplication::sendPostedEvents(this, QEvent::LayoutHint); + QEvent *e = new QEvent(QEvent::LayoutHint); + QApplication::postEvent(this, e); +} + +/*! + Destroys the dock window and its child widgets. +*/ + +Q3DockWindow::~Q3DockWindow() +{ + qApp->removeEventFilter(this); + if (area()) + area()->removeDockWindow(this, false, false); + Q3DockArea *a = area(); + if (!a && dockWindowData) + a = ((Q3DockArea::DockWindowData*)dockWindowData)->area; + Q3MainWindow *mw = a ? qobject_cast<Q3MainWindow*>(a->parentWidget()) : 0; + if (mw) + mw->removeDockWindow(this); + + delete (Q3DockArea::DockWindowData*)dockWindowData; +} + +/*! \reimp +*/ + +void Q3DockWindow::resizeEvent(QResizeEvent *e) +{ + Q3Frame::resizeEvent(e); + updateGui(); +} + + +void Q3DockWindow::swapRect(QRect &r, Qt::Orientation o, const QPoint &offset, Q3DockArea *) +{ + r.setSize(QSize(r.height(), r.width())); + bool reverse = QApplication::reverseLayout(); + if (o == Qt::Horizontal) + r.moveBy(-r.width()/2, 0); + else + r.moveBy(reverse ? - r.width() : 0, -r.height() / 2 ); + r.moveBy(offset.x(), offset.y()); +} + +QWidget *Q3DockWindow::areaAt(const QPoint &gp) +{ + QWidget *w = qApp->widgetAt(gp); + + if (w && (w == this || w == titleBar) && parentWidget()) + w = parentWidget()->childAt(parentWidget()->mapFromGlobal(gp)); + + while (w) { + if (qobject_cast<Q3DockArea*>(w)) { + Q3DockArea *a = (Q3DockArea*)w; + if (a->isDockWindowAccepted(this)) + return w; + } + if (qobject_cast<Q3MainWindow*>(w)) { + Q3MainWindow *mw = (Q3MainWindow*)w; + Q3DockArea *a = mw->dockingArea(mw->mapFromGlobal(gp)); + if (a && a->isDockWindowAccepted(this)) + return a; + } + w = w->isWindow() ? 0 : (QWidget *)w->parent(); + } + return 0; +} + +void Q3DockWindow::handleMove(const QPoint &pos, const QPoint &gp, bool drawRect) +{ + if (!rubberBand) + return; + + currRect = QRect(realWidgetPos(this), size()); + QWidget *w = areaAt(gp); + if (titleBar->ctrlDown || horHandle->ctrlDown || verHandle->ctrlDown) + w = 0; + currRect.moveBy(pos.x(), pos.y()); + if (!qobject_cast<Q3DockArea*>(w)) { + if (startOrientation != Qt::Horizontal && qobject_cast<Q3ToolBar*>(this)) + swapRect(currRect, Qt::Horizontal, startOffset, (Q3DockArea*)w); + if (drawRect) { + rubberBand->setGeometry(currRect); + } else { + QPoint mp(mapToGlobal(pos)); + if(place() == InDock) { + undock(); + if(titleBar) { + mp = QPoint(titleBar->width() / 2, titleBar->height() / 2); + QMouseEvent me(QEvent::MouseButtonPress, mp, Qt::LeftButton, 0); + QApplication::sendEvent(titleBar, &me); + mp = titleBar->mapToGlobal(mp); + } + } + move(mp); + } + state = OutsideDock; + return; + } + + Q3DockArea *area = (Q3DockArea*)w; + if(area->isVisible()) { + state = InDock; + Qt::Orientation o = (area ? area->orientation() : + (boxLayout()->direction() == QBoxLayout::LeftToRight || + boxLayout()->direction() == QBoxLayout::RightToLeft ? + Qt::Horizontal : Qt::Vertical)); + if (startOrientation != o) + swapRect(currRect, o, startOffset, area); + if (drawRect) { + rubberBand->setGeometry(currRect); + } + tmpDockArea = area; + } +} + +void Q3DockWindow::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(0); + titleBar->hide(); + if (orientation() == Qt::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() == Qt::Horizontal) { + hHandleBottom->raise(); + hHandleTop->raise(); + } else { + vHandleRight->raise(); + vHandleLeft->raise(); + } + + if (area()) { + if (orientation() == Qt::Horizontal) { + if (area()->handlePosition() == Q3DockArea::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() == Q3DockArea::Normal) != QApplication::reverseLayout()) { + vHandleRight->show(); + vHandleLeft->hide(); + } else { + vHandleLeft->show(); + vHandleRight->hide(); + } + if (!area()->isLastDockWindow(this)) + hHandleBottom->show(); + else + hHandleBottom->hide(); + hHandleTop->hide(); + } + } + } +#ifndef Q_OS_WINCE + if (moveEnabled) + setLineWidth(1); + else + setLineWidth(0); +#endif + widgetResizeHandler->setActive(false); + } +} + +void Q3DockWindow::updatePosition(const QPoint &globalPos) +{ + if (curPlace == OutsideDock && state == InDock) + lastSize = size(); + + bool doAdjustSize = curPlace != state && state == OutsideDock; + bool doUpdate = true; + bool doOrientationChange = true; + if (state != curPlace && state == InDock) { + doUpdate = false; + curPlace = state; + updateGui(); + QApplication::sendPostedEvents(); + } + Qt::Orientation oo = orientation(); + + if (state == InDock) { + if (tmpDockArea) { + bool differentDocks = false; + if (dockArea && dockArea != tmpDockArea) { + differentDocks = true; + delete (Q3DockArea::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) { + Q3MainWindow *mw = (Q3MainWindow*)dockArea->parentWidget(); + if (qobject_cast<Q3MainWindow*>(mw) && + (!mw->isDockEnabled(Qt::DockTornOff) || + !mw->isDockEnabled(this, Qt::DockTornOff))) + return; + delete (Q3DockArea::DockWindowData*)dockWindowData; + dockWindowData = dockArea->dockWindowData(this); + dockArea->removeDockWindow(this, true, + startOrientation != Qt::Horizontal && qobject_cast<Q3ToolBar*>(this)); + } + dockArea = 0; + QPoint topLeft = currRect.topLeft(); + QRect screen = qApp->desktop()->availableGeometry(topLeft); + if (!screen.contains(topLeft)) { + topLeft.setY(qMax(topLeft.y(), screen.top())); + topLeft.setY(qMin(topLeft.y(), screen.bottom()-height())); + topLeft.setX(qMax(topLeft.x(), screen.left())); + topLeft.setX(qMin(topLeft.x(), screen.right()-width())); + } + move(topLeft); + } + + if (curPlace == InDock && state == OutsideDock && !qobject_cast<Q3ToolBar*>(this)) { + if (lastSize != QSize(-1, -1)) + resize(lastSize); + } + + if (doUpdate) { + curPlace = state; + updateGui(); + } + if (doOrientationChange) + emit orientationChanged(orientation()); + tmpDockArea = 0; + if (doAdjustSize) { + QApplication::sendPostedEvents(this, QEvent::LayoutHint); + if (qobject_cast<Q3ToolBar*>(this)) + adjustSize(); + if (lastSize == QSize(-1, -1)) + setAttribute(Qt::WA_Resized, false); // Ensures size is recalculated (non-opaque). + show(); + if (parentWidget() && isWindow()) + parentWidget()->setActiveWindow(); + + } + + emit placeChanged(curPlace); +} + +/*! + Sets the dock window's main widget to \a w. + + \sa boxLayout() +*/ + +void Q3DockWindow::setWidget(QWidget *w) +{ + wid = w; + boxLayout()->addWidget(w); + updateGui(); +} + +/*! + Returns the dock window's main widget. + + \sa setWidget() +*/ + +QWidget *Q3DockWindow::widget() const +{ + return wid; +} + +void Q3DockWindow::startRectDraw(const QPoint &so, bool drawRect) +{ + state = place(); + if (rubberBand) + endRectDraw(!opaque); + rubberBand = new QRubberBand(QRubberBand::Rectangle); + currRect = QRect(realWidgetPos(this), size()); + if (drawRect) { + rubberBand->setGeometry(currRect); + } + startOrientation = orientation(); + startOffset = mapFromGlobal(so); + rubberBand->show(); +} + +void Q3DockWindow::endRectDraw(bool) +{ + delete rubberBand; + rubberBand = 0; +} + +/*! + \reimp +*/ +void Q3DockWindow::drawFrame(QPainter *p) +{ + if (place() == InDock) { + Q3Frame::drawFrame(p); + return; + } + + QStyleOptionFrame opt; + opt.rect = rect(); + opt.palette = palette(); + opt.state = QStyle::State_None; + if (titleBar->isActive()) + opt.state |= QStyle::State_Active; + opt.lineWidth = lineWidth(); + opt.midLineWidth = midLineWidth(); + + style()->drawPrimitive(QStyle::PE_FrameWindow, &opt, p, this); +} + +/*! + \reimp +*/ +void Q3DockWindow::drawContents(QPainter *p) +{ + // This is only used by the PocketPC style. We probably need to revist later. + QStyleOption opt(0, QStyleOption::SO_Default); + opt.init(this); + if (titleBar->isActive()) + opt.state |= QStyle::State_Active; + style()->drawControl(QStyle::CE_Q3DockWindowEmptyArea, &opt, p, this); +} + +/*! + \property Q3DockWindow::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 Q3DockWindow::setResizeEnabled(bool b) +{ + resizeEnabled = b; + updateGui(); +} + +/*! + \property Q3DockWindow::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 Q3DockWindow::setMovingEnabled(bool b) +{ + moveEnabled = b; + updateGui(); +} + +bool Q3DockWindow::isResizeEnabled() const +{ + return resizeEnabled; +} + +bool Q3DockWindow::isMovingEnabled() const +{ + return moveEnabled; +} + +/*! + \property Q3DockWindow::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 Q3DockWindow::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 Q3DockWindow::isCloseEnabled() const +{ + return (((cMode & Docked) == Docked && place() == InDock) || + ((cMode & Undocked) == Undocked && place() == OutsideDock)); +} + +int Q3DockWindow::closeMode() const +{ + return cMode; +} + +/*! + \property Q3DockWindow::horizontallyStretchable + \brief whether the dock window is horizontally stretchable. + + A dock window is horizontally stretchable if you call + setHorizontallyStretchable(true) or setResizeEnabled(true). + + \warning Stretchability is broken. You must call + setResizeEnabled(true) to get proper behavior and even then + Q3DockWindow does not limit stretchablilty. + + \sa setResizeEnabled() +*/ + +void Q3DockWindow::setHorizontallyStretchable(bool b) +{ + stretchable[Qt::Horizontal] = b; +} + +/*! + \property Q3DockWindow::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() + + \warning Stretchability is broken. You must call + setResizeEnabled(true) to get proper behavior and even then + Q3DockWindow does not limit stretchablilty. +*/ + +void Q3DockWindow::setVerticallyStretchable(bool b) +{ + stretchable[Qt::Vertical] = b; +} + +bool Q3DockWindow::isHorizontallyStretchable() const +{ + return isResizeEnabled() || stretchable[Qt::Horizontal]; +} + +bool Q3DockWindow::isVerticallyStretchable() const +{ + return isResizeEnabled() || stretchable[Qt::Vertical]; +} + +/*! + \property Q3DockWindow::stretchable + \brief whether the dock window is stretchable in the current + orientation() + + This property can be set using setHorizontallyStretchable() and + setVerticallyStretchable(), or with setResizeEnabled(). + + \warning Stretchability is broken. You must call + setResizeEnabled(true) to get proper behavior and even then + Q3DockWindow does not limit stretchablilty. + + \sa setResizeEnabled() +*/ + +bool Q3DockWindow::isStretchable() const +{ + if (orientation() == Qt::Horizontal) + return isHorizontallyStretchable(); + return isVerticallyStretchable(); +} + +/*! + Returns the orientation of the dock window. + + \sa orientationChanged() +*/ + +Qt::Orientation Q3DockWindow::orientation() const +{ + if (dockArea) + return dockArea->orientation(); + if (qobject_cast<const Q3ToolBar*>(this)) + return Qt::Horizontal; + return (((Q3DockWindow*)this)->boxLayout()->direction() == QBoxLayout::LeftToRight || + ((Q3DockWindow*)this)->boxLayout()->direction() == QBoxLayout::RightToLeft ? + Qt::Horizontal : Qt::Vertical); +} + +int Q3DockWindow::offset() const +{ + return offs; +} + +/*! + \property Q3DockWindow::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 Q3DockWindow::setOffset(int o) +{ + offs = o; +} + +/*! + Returns the dock window's preferred size (fixed extent). + + \sa setFixedExtentWidth() setFixedExtentHeight() +*/ + +QSize Q3DockWindow::fixedExtent() const +{ + return fExtent; +} + +/*! + Sets the dock window's preferred width for its fixed extent (size) + to \a w. + + \sa setFixedExtentHeight() +*/ + +void Q3DockWindow::setFixedExtentWidth(int w) +{ + fExtent.setWidth(w); +} + +/*! + Sets the dock window's preferred height for its fixed extent + (size) to \a h. + + \sa setFixedExtentWidth() +*/ + +void Q3DockWindow::setFixedExtentHeight(int h) +{ + fExtent.setHeight(h); +} + +/*! + \property Q3DockWindow::newLine + \brief whether the dock window prefers to start a new line in the + dock area. + + The default is false, i.e. the dock window doesn't require a new + line in the dock area. +*/ + +void Q3DockWindow::setNewLine(bool b) +{ + nl = b; +} + +bool Q3DockWindow::newLine() const +{ + return nl; +} + +/*! + Returns the layout which is used for adding widgets to the dock + window. The layout's orientation is set automatically to match the + orientation of the dock window. You can add widgets to the layout + using the box layout's QBoxLayout::addWidget() function. + + If the dock window only needs to contain a single widget use + setWidget() instead. + + \sa setWidget() setOrientation() +*/ + +QBoxLayout *Q3DockWindow::boxLayout() +{ + return childBox; +} + +/*! \reimp + */ + +QSize Q3DockWindow::sizeHint() const +{ + QSize sh(Q3Frame::sizeHint()); + if (place() == InDock) + sh = sh.expandedTo(fixedExtent()); + sh = sh.expandedTo(QSize(16, 16)); + if (area()) { + if (area()->orientation() == Qt::Horizontal && !vHandleRight->isVisible()) + sh.setWidth(sh.width() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3); + else if (area()->orientation() == Qt::Vertical && !hHandleBottom->isVisible()) + sh.setHeight(sh.height() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3); + } + return sh; +} + +/*! \internal + */ + +QSize Q3DockWindow::minimumSize() const +{ + QSize ms(Q3Frame::minimumSize()); + if (place() == InDock) + ms = ms.expandedTo(fixedExtent()); + ms = ms.expandedTo(QSize(16, 16)); + if (area()) { + if (area()->orientation() == Qt::Horizontal && !vHandleRight->isVisible()) + ms.setWidth(ms.width() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3); + else if (area()->orientation() == Qt::Vertical && !hHandleBottom->isVisible()) + ms.setHeight(ms.height() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3); + } + return ms; +} + +/*! \reimp + */ + +QSize Q3DockWindow::minimumSizeHint() const +{ + QSize msh(Q3Frame::minimumSize()); + if (place() == InDock) + msh = msh.expandedTo(fixedExtent()); + msh = msh.expandedTo(QSize(16, 16)); + if (area()) { + if (area()->orientation() == Qt::Horizontal && !vHandleRight->isVisible()) + msh.setWidth(msh.width() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3); + else if (area()->orientation() == Qt::Vertical && !hHandleBottom->isVisible()) + msh.setHeight(msh.height() + 2 * style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this) / 3); + } + return msh; +} + +/*! + \fn void Q3DockWindow::undock() + + Undocks the Q3DockWindow from its current dock area if it is + docked; otherwise does nothing. + + \sa dock() Q3DockArea::moveDockWindow(), + Q3DockArea::removeDockWindow(), Q3MainWindow::moveDockWindow(), + Q3MainWindow::removeDockWindow() +*/ + +/*! + \fn void Q3DockWindow::undock(QWidget *widget) + + Undocks the specified \a widget from its current dock area if it is + docked; otherwise does nothing. + + \sa dock() Q3DockArea::moveDockWindow(), + Q3DockArea::removeDockWindow(), Q3MainWindow::moveDockWindow(), + Q3MainWindow::removeDockWindow() +*/ +void Q3DockWindow::undock(QWidget *w) +{ + Q3MainWindow *mw = 0; + if (area()) + mw = qobject_cast<Q3MainWindow*>(area()->parentWidget()); + if (mw && !mw->isDockEnabled(this, Qt::DockTornOff)) + return; + if ((place() == OutsideDock && !w)) + return; + + QPoint p(50, 50); + if (window()) + p = window()->pos() + QPoint(20, 20); + if (dockArea) { + delete (Q3DockArea::DockWindowData*)dockWindowData; + dockWindowData = dockArea->dockWindowData(this); + dockArea->removeDockWindow(this, true, orientation() != Qt::Horizontal && qobject_cast<Q3ToolBar*>(this)); + } + dockArea = 0; + if (lastPos != QPoint(-1, -1) && lastPos.x() > 0 && lastPos.y() > 0) + move(lastPos); + else + move(p); + if (lastSize != QSize(-1, -1)) + resize(lastSize); + curPlace = OutsideDock; + updateGui(); + emit orientationChanged(orientation()); + QApplication::sendPostedEvents(this, QEvent::LayoutHint); + if (qobject_cast<Q3ToolBar*>(this)) + adjustSize(); + if (!w) { + if (!parentWidget() || parentWidget()->isVisible()) { + if (lastSize == QSize(-1, -1)) + setAttribute(Qt::WA_Resized, false);// Ensures size is recalculated (opaque). + show(); + } + } else { + setParent(w, 0); + move(-width() - 5, -height() - 5); + resize(1, 1); + show(); + } + if (parentWidget() && isWindow()) + parentWidget()->setActiveWindow(); + emit placeChanged(place()); +} + +void Q3DockWindow::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 Q3DockArea::isDockWindowAccepted(). + + \sa undock() Q3DockArea::moveDockWindow(), + Q3DockArea::removeDockWindow(), Q3MainWindow::moveDockWindow(), + Q3MainWindow::removeDockWindow(), Q3DockArea::isDockWindowAccepted() + +*/ + +void Q3DockWindow::dock() +{ + if (!(Q3DockArea::DockWindowData*)dockWindowData || + !((Q3DockArea::DockWindowData*)dockWindowData)->area) + return; + curPlace = InDock; + lastPos = pos(); + lastSize = size(); + ((Q3DockArea::DockWindowData*)dockWindowData)-> + area->dockWindow(this, (Q3DockArea::DockWindowData*)dockWindowData); + emit orientationChanged(orientation()); + emit placeChanged(place()); +} + +/*! \reimp + */ + +void Q3DockWindow::hideEvent(QHideEvent *e) +{ + Q3Frame::hideEvent(e); +} + +/*! \reimp + */ + +void Q3DockWindow::showEvent(QShowEvent *e) +{ + if (curPlace == OutsideDock && (parent() && parent()->objectName() == QLatin1String("qt_hide_dock"))) { + QRect sr = qApp->desktop()->availableGeometry(this); + if (!sr.contains(pos())) { + int nx = qMin(qMax(x(), sr.x()), sr.right()-width()); + int ny = qMin(qMax(y(), sr.y()), sr.bottom()-height()); + move(nx, ny); + } + } + + Q3Frame::showEvent(e); +} + +/*! + \property Q3DockWindow::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 Q3DockWindow::setOpaqueMoving(bool b) +{ + opaque = b; + horHandle->setOpaqueMoving(b); + verHandle->setOpaqueMoving(b); + titleBar->setOpaqueMoving(b); +} + +bool Q3DockWindow::opaqueMoving() const +{ + return opaque; +} + +void Q3DockWindow::updateSplitterVisibility(bool visible) +{ + if (area() && isResizeEnabled()) { + if (orientation() == Qt::Horizontal) { + if (visible) + vHandleRight->show(); + else + vHandleRight->hide(); + vHandleLeft->hide(); + } else { + if (visible) + hHandleBottom->show(); + else + hHandleBottom->hide(); + hHandleTop->hide(); + } + } +} + +/*! \reimp */ +bool Q3DockWindow::eventFilter(QObject * o, QEvent *e) +{ + if (!o->isWidgetType()) + return false; + + if (e->type() == QEvent::KeyPress && + (horHandle->mousePressed || + verHandle->mousePressed || + titleBar->mousePressed)) { + QKeyEvent *ke = (QKeyEvent*)e; + if (ke->key() == Qt::Key_Escape) { + horHandle->mousePressed = + verHandle->mousePressed = + titleBar->mousePressed = false; + endRectDraw(!opaque); + qApp->removeEventFilter(this); + return true; + } + } else if (((QWidget*)o)->window() != this && place() == OutsideDock && isWindow()) { + if ((e->type() == QEvent::WindowDeactivate || + e->type() == QEvent::WindowActivate)) + event(e); + } + return false; +} + +/*! \reimp */ +bool Q3DockWindow::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::WindowDeactivate: + if (place() == OutsideDock && isWindow() && parentWidget() + && parentWidget()->isActiveWindow()) + return true; + break; + case QEvent::HideToParent: + emit visibilityChanged(false); + break; + case QEvent::ShowToParent: + emit visibilityChanged(true); + break; + case QEvent::WindowTitleChange: + { + QString s = Q3Frame::windowTitle(); + titleBar->setWindowTitle(s); +#ifndef QT_NO_TOOLTIP + horHandle->setToolTip(s); + verHandle->setToolTip(s); +#endif + break; + } + default: + break; + } + return Q3Frame::event(e); +} + +/*! + Returns the dock window's title. +*/ +QString Q3DockWindow::windowTitle() const +{ + return titleBar->windowTitle(); +} + +/*! \reimp */ +void Q3DockWindow::contextMenuEvent(QContextMenuEvent *e) +{ + QObject *o = this; + while (o) { + if (qobject_cast<Q3MainWindow*>(o)) + break; + o = o->parent(); + } + if (!o || ! ((Q3MainWindow*)o)->showDockMenu(e->globalPos())) + e->ignore(); +} + +QT_END_NAMESPACE + +#include "q3dockwindow.moc" + +#endif //QT_NO_MAINWINDOW diff --git a/src/qt3support/widgets/q3dockwindow.h b/src/qt3support/widgets/q3dockwindow.h new file mode 100644 index 0000000..a857a66 --- /dev/null +++ b/src/qt3support/widgets/q3dockwindow.h @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3DOCKWINDOW_H +#define Q3DOCKWINDOW_H + +#include <Qt3Support/q3frame.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_MAINWINDOW + +class Q3DockWindowHandle; +class Q3DockWindowTitleBar; +class QPainter; +class Q3DockWindowResizeHandle; +class QBoxLayout; +class QHBoxLayout; +class QVBoxLayout; +class Q3DockArea; +class QWidgetResizeHandler; +class Q3MainWindow; +class Q3DockAreaLayout; +class Q3DockWindowPrivate; +class Q3ToolBar; +class QWindowsXPStyle; +class QRubberBand; + +class Q_COMPAT_EXPORT Q3DockWindow : public Q3Frame +{ + Q_OBJECT + Q_ENUMS(CloseMode Place) + Q_PROPERTY(int closeMode READ closeMode WRITE setCloseMode) + 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 Q3DockWindowHandle; + friend class Q3DockWindowTitleBar; + friend class Q3DockArea; + friend class Q3DockAreaLayout; + friend class Q3MainWindow; + friend class QCEMainWindow; + friend class Q3ToolBar; + friend class QWindowsXPStyle; + +public: + enum Place { InDock, OutsideDock }; + enum CloseMode { Never = 0, Docked = 1, Undocked = 2, Always = Docked | Undocked }; + + Q3DockWindow(Place p = InDock, QWidget* parent=0, const char* name=0, Qt::WindowFlags f = 0); + Q3DockWindow(QWidget* parent, const char* name=0, Qt::WindowFlags f = 0); + ~Q3DockWindow(); + + virtual void setWidget(QWidget *w); + QWidget *widget() const; + + Place place() const { return curPlace; } + + Q3DockArea *area() const; + + virtual void setCloseMode(int m); + bool isCloseEnabled() const; + int closeMode() const; + + virtual void setResizeEnabled(bool b); + virtual void setMovingEnabled(bool b); + bool isResizeEnabled() const; + bool isMovingEnabled() const; + + virtual void setHorizontallyStretchable(bool b); + virtual void setVerticallyStretchable(bool b); + bool isHorizontallyStretchable() const; + bool isVerticallyStretchable() const; + void setHorizontalStretchable(bool b) { setHorizontallyStretchable(b); } + void setVerticalStretchable(bool b) { setVerticallyStretchable(b); } + bool isHorizontalStretchable() const { return isHorizontallyStretchable(); } + bool isVerticalStretchable() const { return isVerticallyStretchable(); } + bool isStretchable() const; + + virtual void setOffset(int o); + int offset() const; + + virtual void setFixedExtentWidth(int w); + virtual void setFixedExtentHeight(int h); + QSize fixedExtent() const; + + virtual void setNewLine(bool b); + bool newLine() const; + + Qt::Orientation orientation() const; + + QSize sizeHint() const; + QSize minimumSize() const; + QSize minimumSizeHint() const; + + QBoxLayout *boxLayout(); + + virtual void setOpaqueMoving(bool b); + bool opaqueMoving() const; + + bool eventFilter(QObject *o, QEvent *e); + + QString windowTitle() const; + +Q_SIGNALS: + void orientationChanged(Qt::Orientation o); + void placeChanged(Q3DockWindow::Place p); + void visibilityChanged(bool); + +public Q_SLOTS: + virtual void undock(QWidget *w); + virtual void undock() { undock(0); } + virtual void dock(); + virtual void setOrientation(Qt::Orientation o); + +protected: + void resizeEvent(QResizeEvent *e); + void showEvent(QShowEvent *e); + void hideEvent(QHideEvent *e); + void contextMenuEvent(QContextMenuEvent *e); + + void drawFrame(QPainter *); + void drawContents(QPainter *); + + bool event(QEvent *e); + +private Q_SLOTS: + void toggleVisible() { setVisible(!isVisible()); } + +private: + Q3DockWindow(Place p, QWidget* parent, const char* name, Qt::WindowFlags f, bool toolbar); + + void handleMove(const QPoint &pos, const QPoint &gp, bool drawRect); + void updateGui(); + void updateSplitterVisibility(bool visible); + + void startRectDraw(const QPoint &so, bool drawRect); + void endRectDraw(bool drawRect); + void updatePosition(const QPoint &globalPos ); + QWidget *areaAt(const QPoint &gp); + void removeFromDock(bool fixNewLines = true); + void swapRect(QRect &r, Qt::Orientation o, const QPoint &offset, Q3DockArea *area); + void init(); + +private: + Q3DockWindowHandle *horHandle, *verHandle; + Q3DockWindowTitleBar *titleBar; + QWidget *wid; + QRubberBand *rubberBand; + Q3DockArea *dockArea, *tmpDockArea; + QRect currRect; + Place curPlace; + Place state; + bool resizeEnabled : 1; + bool moveEnabled : 1; + bool nl : 1; + bool opaque : 1; + bool isToolbar : 1; + bool stretchable[3]; + Qt::Orientation startOrientation; + int cMode; + QPoint startOffset; + int offs; + QSize fExtent; + Q3DockWindowResizeHandle *hHandleTop, *hHandleBottom, *vHandleLeft, *vHandleRight; + QVBoxLayout *hbox; + QHBoxLayout *vbox; + QBoxLayout *childBox; + void *dockWindowData; + QPoint lastPos; + QSize lastSize; + QWidgetResizeHandler *widgetResizeHandler; + Q3DockWindowPrivate *d; + +private: + Q_DISABLE_COPY(Q3DockWindow) +}; + +inline Q3DockArea *Q3DockWindow::area() const +{ + return dockArea; +} + +#endif // QT_NO_MAINWINDOW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3DOCKWINDOW_H diff --git a/src/qt3support/widgets/q3frame.cpp b/src/qt3support/widgets/q3frame.cpp new file mode 100644 index 0000000..49a8e28 --- /dev/null +++ b/src/qt3support/widgets/q3frame.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3frame.h" +#include "qevent.h" +#include "qpainter.h" + +QT_BEGIN_NAMESPACE + +/*! \class Q3Frame + + \compat +*/ + +/*! + Creates a new frame with the given \a parent, object \a name, and + with widget flags \a f. +*/ +Q3Frame::Q3Frame(QWidget* parent, const char* name, Qt::WindowFlags f) + :QFrame(parent, f), marg(0) +{ + if (name) + setObjectName(QLatin1String(name)); + setAttribute(Qt::WA_LayoutOnEntireRect); +} + +/*! + Destructs the frame. +*/ +Q3Frame::~Q3Frame() +{ +} + +/*! + Paints the frame (or part of the frame) that's necessary, + depending on the \a event. +*/ +void Q3Frame::paintEvent(QPaintEvent * event) +{ + QPainter paint(this); + if (!contentsRect().contains(event->rect())) { + paint.save(); + paint.setClipRegion(event->region().intersected(frameRect())); + drawFrame(&paint); + paint.restore(); + } + if (event->rect().intersects(contentsRect())) { + paint.setClipRegion(event->region().intersected(contentsRect())); + drawContents(&paint); + } +} + +/*! + \fn void Q3Frame::drawContents(QPainter *painter) + + Virtual function that draws the contents of the frame on the given + \a painter. + + The QPainter is already open when you get it, and you must leave + it open. Painter \link QPainter::setWorldMatrix() + transformations\endlink are switched off on entry. If you + transform the painter, remember to take the frame into account and + \link QPainter::resetXForm() reset transformation\endlink before + returning. + + This function is reimplemented by subclasses that draw something + inside the frame. It should only draw inside contentsRect(). The + default function does nothing. + + \sa contentsRect(), QPainter::setClipRect() +*/ + +void Q3Frame::drawContents(QPainter *) +{ +} + +/*! + Draws the frame using the painter \a p and the current frame + attributes and color group. The rectangle inside the frame is not + affected. + + This function is virtual, but in general you do not need to + reimplement it. If you do, note that the QPainter is already open + and must remain open. + + \sa frameRect(), contentsRect(), drawContents(), frameStyle(), setPalette() +*/ + +void Q3Frame::drawFrame(QPainter *p) +{ + QFrame::drawFrame(p); +} + +/*! + \fn void Q3Frame::resizeEvent(QResizeEvent *event) + + This just calls frameChanged(); it does not make use of the \a + event itself. +*/ +void Q3Frame::resizeEvent(QResizeEvent *e) +{ + if (e->size() == e->oldSize()) + frameChanged(); +} + +/*! + 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. +*/ + +void Q3Frame::frameChanged() +{ +} + + +/*! + \property Q3Frame::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 lineWidth(), frameWidth() +*/ + +void Q3Frame::setMargin(int w) +{ + if (marg == w) + return; + marg = w; + update(); + frameChanged(); +} + +/*! + \property Q3Frame::contentsRect + \brief the frame's contents rectangle (including the margins) +*/ +QRect Q3Frame::contentsRect() const +{ + QRect cr(QFrame::contentsRect()); + cr.adjust(marg, marg, -marg, -marg); + return cr; +} + +/*! + Returns the width of the frame (including the margin). +*/ +int Q3Frame::frameWidth() const +{ + return QFrame::frameWidth() + marg; +} + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3frame.h b/src/qt3support/widgets/q3frame.h new file mode 100644 index 0000000..b94dd4c --- /dev/null +++ b/src/qt3support/widgets/q3frame.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3FRAME_H +#define Q3FRAME_H + +#include <QtGui/qframe.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class Q_COMPAT_EXPORT Q3Frame : public QFrame +{ + Q_OBJECT + Q_PROPERTY(int margin READ margin WRITE setMargin) + Q_PROPERTY(QRect contentsRect READ contentsRect) + +public: + Q3Frame(QWidget* parent, const char* name = 0, Qt::WindowFlags f = 0); + ~Q3Frame(); +#ifndef qdoc + bool lineShapesOk() const { return true; } +#endif + + int margin() const { return marg; } + void setMargin(int); + + QRect contentsRect() const; + int frameWidth() const; + +protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + + virtual void frameChanged(); + virtual void drawFrame(QPainter *); + virtual void drawContents(QPainter *); + +private: + Q_DISABLE_COPY(Q3Frame) + + int marg; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3FRAME_H diff --git a/src/qt3support/widgets/q3grid.cpp b/src/qt3support/widgets/q3grid.cpp new file mode 100644 index 0000000..169e01e --- /dev/null +++ b/src/qt3support/widgets/q3grid.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3grid.h" +#include "qlayout.h" +#include "qapplication.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Q3Grid + \brief The Q3Grid widget provides simple geometry management of its children. + + \compat + + 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. + + \sa Q3VBox Q3HBox QGridLayout +*/ + +/*! + \typedef Q3Grid::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 Q3Frame constructor. +*/ +Q3Grid::Q3Grid(int n, Qt::Orientation orient, QWidget *parent, const char *name, + Qt::WindowFlags f) + : Q3Frame(parent, name, f) +{ + int nCols, nRows; + if (orient == Qt::Horizontal) { + nCols = n; + nRows = -1; + } else { + nCols = -1; + nRows = n; + } + (new QGridLayout(this, nRows, nCols, 0, 0, name))->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 Q3Frame constructor. + */ +Q3Grid::Q3Grid(int n, QWidget *parent, const char *name, Qt::WindowFlags f) + : Q3Frame(parent, name, f) +{ + (new QGridLayout(this, -1, n, 0, 0, name))->setAutoAdd(true); +} + + +/*! + Sets the spacing between the child widgets to \a space. +*/ + +void Q3Grid::setSpacing(int space) +{ + if (layout()) + layout()->setSpacing(space); +} + + +/*!\reimp + */ +void Q3Grid::frameChanged() +{ + if (layout()) + layout()->setMargin(frameWidth()); +} + + +/*! + \reimp +*/ + +QSize Q3Grid::sizeHint() const +{ + QWidget *mThis = (QWidget*)this; + QApplication::sendPostedEvents(mThis, QEvent::ChildInserted); + return Q3Frame::sizeHint(); +} + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3grid.h b/src/qt3support/widgets/q3grid.h new file mode 100644 index 0000000..365889d --- /dev/null +++ b/src/qt3support/widgets/q3grid.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3GRID_H +#define Q3GRID_H + +#include <Qt3Support/q3frame.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class QGridLayout; + +class Q_COMPAT_EXPORT Q3Grid : public Q3Frame +{ + Q_OBJECT +public: + Q3Grid(int n, QWidget* parent=0, const char* name=0, Qt::WindowFlags f = 0); + Q3Grid(int n, Qt::Orientation orient, QWidget* parent=0, const char* name=0, + Qt::WindowFlags f = 0); + + void setSpacing(int); + QSize sizeHint() const; + + typedef Qt::Orientation Direction; + +protected: + void frameChanged(); + +private: + Q_DISABLE_COPY(Q3Grid) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3GRID_H diff --git a/src/qt3support/widgets/q3gridview.cpp b/src/qt3support/widgets/q3gridview.cpp new file mode 100644 index 0000000..af6f9aa --- /dev/null +++ b/src/qt3support/widgets/q3gridview.cpp @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "q3gridview.h" +#include "qpainter.h" + +QT_BEGIN_NAMESPACE + +using namespace Qt; + +/*! + \class Q3GridView + \brief The Q3GridView class provides an abstract base for + fixed-size grids. + + \compat + + 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{Q3GridView::numRows} {numRows}, + \l{Q3GridView::numCols} {numCols}, \l{Q3GridView::cellWidth} + {cellWidth} and \l{Q3GridView::cellHeight} {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). + + Q3GridView is a very simple abstract class based on Q3ScrollView. 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 Q3Table instead. If you need a + simple list of items, use a Q3ListBox. If you need to present + hierachical data use a Q3ListView, and if you need random objects + at random positions, consider using either a Q3IconView or a + Q3Canvas. +*/ + + +/*! + Constructs a grid view. + + The \a parent, \a name and widget flag, \a f, arguments are passed + to the Q3ScrollView constructor. +*/ +Q3GridView::Q3GridView(QWidget *parent, const char *name, Qt::WindowFlags f) + : Q3ScrollView(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. +*/ +Q3GridView::~Q3GridView() +{ +} + +void Q3GridView::updateGrid() +{ + resizeContents(ncols * cellw, nrows * cellh); +} + +/*! + \property Q3GridView::numRows + \brief The number of rows in the grid + + \sa numCols +*/ +void Q3GridView::setNumRows(int numRows) +{ + int oldnrows = nrows; + nrows = numRows; + dimensionChange(oldnrows, ncols); + updateGrid(); +} + +/*! + \property Q3GridView::numCols + \brief The number of columns in the grid + + \sa numRows +*/ +void Q3GridView::setNumCols(int numCols) +{ + int oldncols = ncols; + ncols = numCols; + dimensionChange(nrows, oldncols); + updateGrid(); +} + +/*! + \property Q3GridView::cellWidth + \brief The width of a grid column + + All columns in a grid view have the same width. + + \sa cellHeight +*/ +void Q3GridView::setCellWidth(int cellWidth) +{ + cellw = cellWidth; + updateGrid(); + updateContents(); +} + +/*! + \property Q3GridView::cellHeight + \brief The height of a grid row + + All rows in a grid view have the same height. + + \sa cellWidth +*/ +void Q3GridView::setCellHeight(int cellHeight) +{ + cellh = cellHeight; + updateGrid(); + updateContents(); +} + +/*! + Returns the geometry of cell (\a row, \a column) in the content + coordinate system. + + \sa cellRect() +*/ +QRect Q3GridView::cellGeometry(int row, int column) +{ + QRect r; + if (row >= 0 && row < nrows && column >= 0 && column < ncols) + r.setRect(cellw * column, cellh * row, cellw, cellh); + return r; +} + +/*! + Repaints cell (\a row, \a column). + + If \a erase is true, Qt erases the area of the cell before the + paintCell() call; otherwise no erasing takes place. + + \sa QWidget::repaint() +*/ +void Q3GridView::repaintCell(int row, int column, bool erase) +{ + repaintContents(cellGeometry(row, column), erase); +} + +/*! + Updates cell (\a row, \a column). + + \sa QWidget::update() +*/ +void Q3GridView::updateCell(int row, int column) +{ + updateContents(cellGeometry(row, column)); +} + +/*! + Ensures cell (\a row, \a column) is visible, scrolling the grid + view if necessary. +*/ +void Q3GridView::ensureCellVisible(int row, int column) +{ + QRect r = cellGeometry(row, column); + ensureVisible(r.x(), r.y(), r.width(), r.height()); +} + +/*! + This function fills the \a cw pixels wide and \a ch pixels high + rectangle starting at position (\a cx, \a cy) with the background + color using the painter \a p. + + paintEmptyArea() is invoked by drawContents() to erase or fill + unused areas. +*/ + +void Q3GridView::paintEmptyArea(QPainter *p, int cx ,int cy, int cw, int ch) +{ + if (gridSize().width() >= contentsWidth() && gridSize().height() >= contentsHeight()) + return; + // Region of the rect we should draw + contentsToViewport(cx, cy, cx, cy); + QRegion reg(QRect(cx, cy, cw, ch)); + // Subtract the table from it + reg = reg.subtracted(QRect(contentsToViewport(QPoint(0, 0)), gridSize())); + + // And draw the rectangles (transformed as needed) + QVector<QRect> r = reg.rects(); + const QBrush &brush = backgroundBrush(); + for (int i = 0; i < (int)r.count(); ++i) + p->fillRect(r[ i ], brush); +} + +/*!\reimp + */ +void Q3GridView::drawContents(QPainter *p, int cx, int cy, int cw, int ch) +{ + int colfirst = columnAt(cx); + int collast = columnAt(cx + cw); + int rowfirst = rowAt(cy); + int rowlast = rowAt(cy + ch); + + if (rowfirst == -1 || colfirst == -1) { + paintEmptyArea(p, cx, cy, cw, ch); + return; + } + + if (collast < 0 || collast >= ncols) + collast = ncols-1; + if (rowlast < 0 || rowlast >= nrows) + rowlast = nrows-1; + + // Go through the rows + for (int r = rowfirst; r <= rowlast; ++r) { + // get row position and height + int rowp = r * cellh; + + // Go through the columns in the row r + // if we know from where to where, go through [colfirst, collast], + // else go through all of them + for (int c = colfirst; c <= collast; ++c) { + // get position and width of column c + int colp = c * cellw; + // Translate painter and draw the cell + p->translate(colp, rowp); + paintCell(p, r, c); + p->translate(-colp, -rowp); + } + } + + // Paint empty rects + paintEmptyArea(p, cx, cy, cw, ch); +} + +/*! + \reimp + + (Implemented to get rid of a compiler warning.) +*/ +void Q3GridView::drawContents(QPainter *) +{ +} + +/*! + \fn void Q3GridView::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 Q3GridView::dimensionChange(int, int) {} + + + +/*! + \fn int Q3GridView::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 Q3GridView::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 Q3GridView::paintCell(QPainter *p, int row, int col) + + This pure virtual function is called to paint the single cell at + (\a row, \a col) using painter \a p. The painter must be open when + paintCell() is called and must remain open. + + The coordinate system is \link QPainter::translate() translated + \endlink so that the origin is at the top-left corner of the cell + to be painted, i.e. \e cell coordinates. Do not scale or shear + the coordinate system (or if you do, restore the transformation + matrix before you return). + + The painter is not clipped by default in order to get maximum + efficiency. If you want clipping, use + + \snippet doc/src/snippets/code/src_qt3support_widgets_q3gridview.cpp 0 +*/ + +/*! + \fn QRect Q3GridView::cellRect() const + + Returns the geometry of a cell in a cell's coordinate system. This + is a convenience function useful in paintCell(). It is equivalent + to QRect(0, 0, cellWidth(), cellHeight()). + + \sa cellGeometry() + +*/ + +/*! + \fn QSize Q3GridView::gridSize() const + + Returns the size of the grid in pixels. +*/ + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3gridview.h b/src/qt3support/widgets/q3gridview.h new file mode 100644 index 0000000..2f4f4ae --- /dev/null +++ b/src/qt3support/widgets/q3gridview.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3GRIDVIEW_H +#define Q3GRIDVIEW_H + +#include <Qt3Support/q3scrollview.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class Q3GridViewPrivate; + +class Q_COMPAT_EXPORT Q3GridView : public Q3ScrollView +{ + 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: + + Q3GridView(QWidget *parent=0, const char *name=0, Qt::WindowFlags f=0); + ~Q3GridView(); + + int numRows() const; + virtual void setNumRows(int); + int numCols() const; + virtual void setNumCols(int); + + int cellWidth() const; + virtual void setCellWidth(int); + int cellHeight() const; + virtual void setCellHeight(int); + + QRect cellRect() const; + QRect cellGeometry(int row, int column); + QSize gridSize() const; + + int rowAt(int y) const; + int columnAt(int x) const; + + void repaintCell(int row, int column, bool erase=true); + void updateCell(int row, int column); + void ensureCellVisible(int row, int column); + +protected: + virtual void paintCell(QPainter *, int row, int col) = 0; + virtual void paintEmptyArea(QPainter *p, int cx, int cy, int cw, int ch); + + void drawContents(QPainter *p, int cx, int cy, int cw, int ch); + + virtual void dimensionChange(int, int); + +private: + void drawContents(QPainter*); + void updateGrid(); + + int nrows; + int ncols; + int cellw; + int cellh; + Q3GridViewPrivate* d; + + Q_DISABLE_COPY(Q3GridView) +}; + +inline int Q3GridView::cellWidth() const +{ return cellw; } + +inline int Q3GridView::cellHeight() const +{ return cellh; } + +inline int Q3GridView::rowAt(int y) const +{ return y / cellh; } + +inline int Q3GridView::columnAt(int x) const +{ return x / cellw; } + +inline int Q3GridView::numRows() const +{ return nrows; } + +inline int Q3GridView::numCols() const +{return ncols; } + +inline QRect Q3GridView::cellRect() const +{ return QRect(0, 0, cellw, cellh); } + +inline QSize Q3GridView::gridSize() const +{ return QSize(ncols * cellw, nrows * cellh); } + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3GRIDVIEW_H diff --git a/src/qt3support/widgets/q3groupbox.cpp b/src/qt3support/widgets/q3groupbox.cpp new file mode 100644 index 0000000..1fa7e7c --- /dev/null +++ b/src/qt3support/widgets/q3groupbox.cpp @@ -0,0 +1,964 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3groupbox.h" + +#include "qlayout.h" +#include "qpainter.h" +#include "qbitmap.h" +#include "q3accel.h" +#include "qradiobutton.h" +#include "qdrawutil.h" +#include "qapplication.h" +#include "qstyle.h" +#include "qcheckbox.h" +#include "qaccessible.h" +#include "qstyleoption.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Q3GroupBox + \brief The Q3GroupBox widget provides a group box frame with a title. + + \compat + + 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(). + + Q3GroupBox 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(). + + \sa QButtonGroup +*/ + +class QCheckBox; + +class Q3GroupBoxPrivate +{ +public: + Q3GroupBoxPrivate(Q3GroupBox *w): + q(w), vbox(0), grid(0), row(0), col(0), nRows(0), nCols(0), dir(Qt::Horizontal), + spac(5), marg(11), + checkbox(0), + frameStyle(Q3GroupBox::GroupBoxPanel | Q3GroupBox::Sunken), + lineWidth(1), midLineWidth(0), frameWidth(0), + leftFrameWidth(0), rightFrameWidth(0), + topFrameWidth(0), bottomFrameWidth(0) {} + + void updateFrameWidth(); + void updateStyledFrameWidths(); + + Q3GroupBox *q; + QVBoxLayout *vbox; + QGridLayout *grid; + int row; + int col; + int nRows, nCols; + Qt::Orientation dir; + int spac, marg; + + QCheckBox *checkbox; + + int frameStyle; + int oldFrameStyle; + short lineWidth, //line width + midLineWidth; //midline width + int frameWidth; + short leftFrameWidth, rightFrameWidth, + topFrameWidth, bottomFrameWidth; +}; + +/*! + \internal + Updates the frame widths from the style. +*/ +void Q3GroupBoxPrivate::updateStyledFrameWidths() +{ + QStyleOptionFrameV2 opt; + opt.initFrom(q); + QRect cr = q->style()->subElementRect(QStyle::SE_FrameContents, &opt, q); + leftFrameWidth = cr.left() - opt.rect.left(); + topFrameWidth = cr.top() - opt.rect.top(); + rightFrameWidth = opt.rect.right() - cr.right(), + bottomFrameWidth = opt.rect.bottom() - cr.bottom(); + frameWidth = qMax(qMax(leftFrameWidth, rightFrameWidth), + qMax(topFrameWidth, bottomFrameWidth)); +} + +/*! + \internal + Updated the frameWidth parameter. +*/ + +void Q3GroupBoxPrivate::updateFrameWidth() +{ + QRect fr = q->frameRect(); + + int frameShape = frameStyle & QFrame::Shape_Mask; + int frameShadow = frameStyle & QFrame::Shadow_Mask; + + frameWidth = -1; + + switch (frameShape) { + + case QFrame::NoFrame: + frameWidth = 0; + break; + + case QFrame::Box: + case QFrame::HLine: + case QFrame::VLine: + switch (frameShadow) { + case QFrame::Plain: + frameWidth = lineWidth; + break; + case QFrame::Raised: + case QFrame::Sunken: + frameWidth = (short)(lineWidth*2 + midLineWidth); + break; + } + break; + + case QFrame::StyledPanel: + updateStyledFrameWidths(); + break; + + case QFrame::WinPanel: + frameWidth = 2; + break; + + + case QFrame::Panel: + switch (frameShadow) { + case QFrame::Plain: + case QFrame::Raised: + case QFrame::Sunken: + frameWidth = lineWidth; + break; + } + break; + } + + if (frameWidth == -1) // invalid style + frameWidth = 0; + + q->setFrameRect(fr); +} + + + + + +/*! + Constructs a group box widget with no title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. + + This constructor does not do automatic layout. +*/ + +Q3GroupBox::Q3GroupBox(QWidget *parent, const char *name) + : QGroupBox(parent, name) +{ + init(); +} + +/*! + Constructs a group box with the title \a title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. + + This constructor does not do automatic layout. +*/ + +Q3GroupBox::Q3GroupBox(const QString &title, QWidget *parent, const char *name) + : QGroupBox(parent, name) +{ + init(); + setTitle(title); +} + +/*! + Constructs a group box with no title. Child widgets will be + arranged in \a strips rows or columns (depending on \a + orientation). + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +Q3GroupBox::Q3GroupBox(int strips, Qt::Orientation orientation, + QWidget *parent, const char *name) + : QGroupBox(parent, name) +{ + init(); + setColumnLayout(strips, orientation); +} + +/*! + Constructs a group box titled \a title. Child widgets will be + arranged in \a strips rows or columns (depending on \a + orientation). + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +Q3GroupBox::Q3GroupBox(int strips, Qt::Orientation orientation, + const QString &title, QWidget *parent, + const char *name) + : QGroupBox(parent, name) +{ + init(); + setTitle(title); + setColumnLayout(strips, orientation); +} + +/*! + Destroys the group box. +*/ +Q3GroupBox::~Q3GroupBox() +{ + delete d; +} + +void Q3GroupBox::init() +{ + d = new Q3GroupBoxPrivate(this); +} + + +/*! \reimp +*/ +void Q3GroupBox::resizeEvent(QResizeEvent *e) +{ + QGroupBox::resizeEvent(e); +} + + +/*! + 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 + Q3GroupBox::columns or \l Q3GroupBox::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 Q3GroupBox::addSpace(int size) +{ + QApplication::sendPostedEvents(this, QEvent::ChildInserted); + + if (d->nCols <= 0 || d->nRows <= 0) + return; + + if (d->row >= d->nRows || d->col >= d->nCols) + d->grid->expand(d->row+1, d->col+1); + + if (size > 0) { + QSpacerItem *spacer + = new QSpacerItem((d->dir == Qt::Horizontal) ? 0 : size, + (d->dir == Qt::Vertical) ? 0 : size, + QSizePolicy::Fixed, QSizePolicy::Fixed); + d->grid->addItem(spacer, d->row, d->col); + } + + skip(); +} + +/*! + \property Q3GroupBox::columns + \brief the number of columns or rows (depending on \l Q3GroupBox::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 Q3GroupBox::columns() const +{ + if (d->dir == Qt::Horizontal) + return d->nCols; + return d->nRows; +} + +void Q3GroupBox::setColumns(int c) +{ + setColumnLayout(c, d->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 Q3GroupBox::insideMargin() const +{ + return d->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 Q3GroupBox::insideSpacing() const +{ + return d->spac; +} + +/*! + Sets the the width of the inside margin to \a m pixels. + + \sa insideMargin() +*/ +void Q3GroupBox::setInsideMargin(int m) +{ + d->marg = m; + setColumnLayout(columns(), d->dir); +} + +/*! + Sets the width of the empty space between each of the items in + the group to \a s pixels. + + \sa insideSpacing() +*/ +void Q3GroupBox::setInsideSpacing(int s) +{ + d->spac = s; + setColumnLayout(columns(), d->dir); +} + +/*! + \property Q3GroupBox::orientation + \brief the group box's orientation + + A horizontal group box arranges its 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 Q3GroupBox::setOrientation(Qt::Orientation o) +{ + setColumnLayout(columns(), o); +} + + +Qt::Orientation Q3GroupBox::orientation() const +{ + return d->dir; +} + +/*! + Changes the layout of the group box. This function is only useful + in combination with the default constructor that does not take any + layout information. This function will put all existing children + in the new layout. It is not good Qt programming style to call + this function after children have been inserted. Sets the number + of columns or rows to be \a strips, depending on \a direction. + + \sa orientation columns +*/ +void Q3GroupBox::setColumnLayout(int strips, Qt::Orientation direction) +{ + if (layout()) + delete layout(); + + d->vbox = 0; + d->grid = 0; + + if (strips < 0) // if 0, we create the d->vbox but not the d->grid. See below. + return; + + d->vbox = new QVBoxLayout(this, d->marg, 0); + + d->nCols = 0; + d->nRows = 0; + d->dir = direction; + + // Send all child events and ignore them. Otherwise we will end up + // with doubled insertion. This won't do anything because d->nCols == + // d->nRows == 0. + QApplication::sendPostedEvents(this, QEvent::ChildInserted); + + // if 0 or smaller , create a vbox-layout but no grid. This allows + // the designer to handle its own grid layout in a group box. + if (strips <= 0) + return; + + d->dir = direction; + if (d->dir == Qt::Horizontal) { + d->nCols = strips; + d->nRows = 1; + } else { + d->nCols = 1; + d->nRows = strips; + } + d->grid = new QGridLayout(d->nRows, d->nCols, d->spac); + d->row = d->col = 0; + d->grid->setAlignment(Qt::AlignTop); + d->vbox->addLayout(d->grid); + + // Add all children + QObjectList childList = children(); + if (!childList.isEmpty()) { + for (int i = 0; i < childList.size(); ++i) { + QObject *o = childList.at(i); + if (o->isWidgetType() && o != d->checkbox) + insertWid(static_cast<QWidget *>(o)); + } + } +} + +/*!\reimp */ +void Q3GroupBox::childEvent(QChildEvent *c) +{ + QGroupBox::childEvent(c); + if (!c->inserted() || !c->child()->isWidgetType()) + return; + if (d->grid) { + insertWid((QWidget*)c->child()); + } +} + +void Q3GroupBox::insertWid(QWidget* w) +{ + if (d->row >= d->nRows || d->col >= d->nCols) + d->grid->expand(d->row+1, d->col+1); + d->grid->addWidget(w, d->row, d->col); + skip(); +} + + +void Q3GroupBox::skip() +{ + // Same as QGrid::skip() + if (d->dir == Qt::Horizontal) { + if (d->col+1 < d->nCols) { + d->col++; + } else { + d->col = 0; + d->row++; + } + } else { //Vertical + if (d->row+1 < d->nRows) { + d->row++; + } else { + d->row = 0; + d->col++; + } + } +} + + +/*! \reimp */ +void Q3GroupBox::changeEvent(QEvent *ev) +{ + QGroupBox::changeEvent(ev); +} + +/*! \reimp */ +bool Q3GroupBox::event(QEvent *e) +{ + if (e->type()==QEvent::Paint) + { + QStyleOptionGroupBox opt; + initStyleOption(&opt); + opt.lineWidth=d->lineWidth; + opt.midLineWidth=d->midLineWidth; + QPainter p(this); + if (frameShape()==GroupBoxPanel) + { + style()->drawComplexControl(QStyle::CC_GroupBox, &opt, &p, this); + } + else { + //in case it is a Paint event with a frame shape different from the group box + const QRect textRect = style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxLabel, this); + const QRect checkBoxRect = style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxCheckBox, this); + + // Draw title + if ((opt.subControls & QStyle::SC_GroupBoxLabel) && !opt.text.isEmpty()) { + QColor textColor = opt.textColor; + if (textColor.isValid()) + p.setPen(textColor); + int alignment = int(opt.textAlignment); + if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) + alignment |= Qt::TextHideMnemonic; + + style()->drawItemText(&p, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment, + opt.palette, opt.state & QStyle::State_Enabled, opt.text, + textColor.isValid() ? QPalette::NoRole : QPalette::WindowText); + + if (opt.state & QStyle::State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(opt); + fropt.rect = textRect; + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, &p, this); + } + } + + // Draw checkbox + if (opt.subControls & QStyle::SC_GroupBoxCheckBox) { + QStyleOptionButton box; + box.QStyleOption::operator=(opt); + box.rect = checkBoxRect; + style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &box, &p, this); + } + + //sets clipping + QRegion region(rect()); + if (!title().isEmpty()) { + bool ltr = layoutDirection() == Qt::LeftToRight; + QRect finalRect = checkBoxRect.united(textRect); + if (isCheckable()) + finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0); + region -= finalRect; + } + p.setClipRegion(region); + + drawFrame(&p); + } + return false; + } + return QGroupBox::event(e); +} + +/*! + \fn void Q3GroupBox::drawFrame(QPainter *p) + \internal +*/ + +void Q3GroupBox::drawFrame(QPainter *p) +{ + QPoint p1, p2; + QStyleOptionFrame opt; + opt.init(this); + + int frameShape = d->frameStyle & QFrame::Shape_Mask; + int frameShadow = d->frameStyle & QFrame::Shadow_Mask; + + int lw = 0; + int mlw = 0; + opt.rect = frameRect(); + + switch (frameShape) { + case QFrame::Box: + case QFrame::HLine: + case QFrame::VLine: + case QFrame::StyledPanel: + lw = d->lineWidth; + mlw = d->midLineWidth; + break; + default: + // most frame styles do not handle customized line and midline widths + // (see updateFrameWidth()). + lw = d->frameWidth; + break; + } + opt.lineWidth = lw; + opt.midLineWidth = mlw; + if (frameShadow == Sunken) + opt.state |= QStyle::State_Sunken; + else if (frameShadow == Raised) + opt.state |= QStyle::State_Raised; + + switch (frameShape) { + case Box: + if (frameShadow == Plain) + qDrawPlainRect(p, opt.rect, opt.palette.foreground().color(), lw); + else + qDrawShadeRect(p, opt.rect, opt.palette, frameShadow == Sunken, lw, mlw); + break; + + case StyledPanel: + style()->drawPrimitive(QStyle::PE_Frame, &opt, p, this); + break; + + case Panel: + if (frameShadow == Plain) + qDrawPlainRect(p, opt.rect, opt.palette.foreground().color(), lw); + else + qDrawShadePanel(p, opt.rect, opt.palette, frameShadow == Sunken, lw); + break; + + case WinPanel: + if (frameShadow == Plain) + qDrawPlainRect(p, opt.rect, opt.palette.foreground().color(), lw); + else + qDrawWinPanel(p, opt.rect, opt.palette, frameShadow == Sunken); + break; + case HLine: + case VLine: + if (frameShape == HLine) { + p1 = QPoint(opt.rect.x(), opt.rect.height() / 2); + p2 = QPoint(opt.rect.x() + opt.rect.width(), p1.y()); + } else { + p1 = QPoint(opt.rect.x()+opt.rect.width() / 2, 0); + p2 = QPoint(p1.x(), opt.rect.height()); + } + if (frameShadow == Plain) { + QPen oldPen = p->pen(); + p->setPen(QPen(opt.palette.foreground().color(), lw)); + p->drawLine(p1, p2); + p->setPen(oldPen); + } else { + qDrawShadeLine(p, p1, p2, opt.palette, frameShadow == Sunken, lw, mlw); + } + break; + } + +#ifdef QT_KEYPAD_NAVIGATION + if (QApplication::keypadNavigationEnabled() && hasFocus()) { + QStyleOptionFocusRect fopt; + fopt.init(this); + fopt.state |= QStyle::State_KeyboardFocusChange; + fopt.rect = frameRect(); + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fopt, p, this); + } +#endif +} + +/*! + \property Q3GroupBox::frameShadow + \brief the frame shadow value from the frame style + + \sa frameStyle() +*/ + +/* + \enum Q3GroupBox::FrameShape + + This enum defines the available frame shapes a group box can + have. All values have equivalents in QFrame. + + \value Box QFrame::Box + \value Sunken QFrame::Sunken + \value Plain QFrame::Plain + \value Raised QFrame::Raised + \value MShadow QFrame::Shadow_Mask + \value NoFrame QFrame::NoFrame + \value Panel QFrame::Panel + \value StyledPanel QFrame::StyledPanel + \value HLine QFrame::HLine + \value VLine QFrame::VLine + \value WinPanel QFrame::WinPanel + \value ToolBarPanel QFrame::StyledPanel + \value MenuBarPanel = QFrame::StyledPanel + \value PopupPanel QFrame::StyledPanel + \value LineEditPanel QFrame::StyledPanel + \value TabWidgetPanel QFrame::StyledPanel + \value GroupBoxPanel 0x0007 + \value MShape QFrame::Shape_Mask +*/ + + +void Q3GroupBox::setFrameShadow(DummyFrame s) +{ + setFrameStyle((d->frameStyle & MShape) | s); +} + +Q3GroupBox::DummyFrame Q3GroupBox::frameShadow() const +{ + return (DummyFrame) (d->frameStyle & MShadow); +} + +/*! + \property Q3GroupBox::frameShape + \brief the frame shape value from the frame style + + \sa frameStyle(), frameShadow() +*/ + +void Q3GroupBox::setFrameShape(DummyFrame s) +{ + setFrameStyle((d->frameStyle & MShadow) | s); +} + +Q3GroupBox::DummyFrame Q3GroupBox::frameShape() const +{ + return (DummyFrame) (d->frameStyle & MShape); +} + +/*! + \fn void Q3GroupBox::setFrameStyle(int style) + + Sets the frame style to \a style. The style is the bitwise OR + between a frame shape and a frame shadow style. +*/ + +void Q3GroupBox::setFrameStyle(int style) +{ + if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) { + switch (style & MShape) { + case HLine: + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; + case VLine: + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); + break; + default: + if ((d->frameStyle & MShape) == HLine || (d->frameStyle & MShape) == VLine) + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + } + setAttribute(Qt::WA_WState_OwnSizePolicy, false); + } + d->frameStyle = style; + update(); + d->updateFrameWidth(); + d->oldFrameStyle = style; +} + +/*! + \fn int Q3GroupBox::frameStyle() const + + Returns the frame style. +*/ + +int Q3GroupBox::frameStyle() const +{ + return d->frameStyle; +} + +/*! + \property Q3GroupBox::lineWidth + \brief This property holds the width of the line. + + \sa frameStyle(), frameShadow() +*/ + +void Q3GroupBox::setLineWidth(int w) +{ + if (short(w) == d->lineWidth) + return; + d->lineWidth = short(w); + d->updateFrameWidth(); +} + +int Q3GroupBox::lineWidth() const +{ + return d->lineWidth; +} + +/*! + \property Q3GroupBox::midLineWidth + \brief This property holds the width of the mid-line. + + \sa frameStyle(), frameShadow() +*/ + +void Q3GroupBox::setMidLineWidth(int w) +{ + if (short(w) == d->midLineWidth) + return; + d->midLineWidth = short(w); + d->updateFrameWidth(); +} + +int Q3GroupBox::midLineWidth() const +{ + return d->midLineWidth; +} + +/*! + \property Q3GroupBox::frameRect + \brief the bounding rectangle of the frame of the group box. +*/ + +/*! + \fn QRect Q3GroupBox::frameRect() const + \internal +*/ + +QRect Q3GroupBox::frameRect() const +{ + QStyleOptionGroupBox opt; + initStyleOption(&opt); + QRect fr = style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxFrame, this); + return fr; +} + +/*! + \fn void Q3GroupBox::setFrameRect(QRect) + \internal +*/ + +void Q3GroupBox::setFrameRect(QRect r) +{ + QRect cr = r.isValid() ? r : rect(); + if ((d->frameStyle & QFrame::Shape_Mask) == StyledPanel) { + cr.adjust(d->leftFrameWidth, d->topFrameWidth, -d->rightFrameWidth, -d->bottomFrameWidth); + } else + cr.adjust(d->frameWidth, d->frameWidth, -d->frameWidth, -d->frameWidth); + setContentsMargins(cr.left(), cr.top(), rect().right() - cr.right(), rect().bottom() - cr.bottom()); +} + +/*! + \fn int Q3GroupBox::frameWidth() const + \internal +*/ + +int Q3GroupBox::frameWidth() const +{ + return d->frameWidth; +} + +#if defined(Q_MOC_RUN) +/*! + \enum Q3GroupBox::FrameShape + \internal + + \value Box + \value Sunken + \value Plain + \value Raised + \value MShadow + \value NoFrame + \value Panel + \value StyledPanel + \value HLine + \value VLine + \value GroupBoxPanel + \value WinPanel + \value ToolBarPanel + \value MenuBarPanel + \value PopupPanel + \value LineEditPanel + \value TabWidgetPanel + \value MShape +*/ +#else +/*! + \enum Q3GroupBox::DummyFrame + \internal + + \value Box + \value Sunken + \value Plain + \value Raised + \value MShadow + \value NoFrame + \value Panel + \value StyledPanel + \value HLine + \value VLine + \value GroupBoxPanel + \value WinPanel + \value ToolBarPanel + \value MenuBarPanel + \value PopupPanel + \value LineEditPanel + \value TabWidgetPanel + \value MShape +*/ +#endif + +/*! + \typedef Q3GroupBox::FrameShape + \internal +*/ + +/*! + \property Q3GroupBox::margin + \brief the width of the margin around the contents of the + group box. +*/ + +/*! + \fn void Q3GroupBox::setMargin(int margin) + \since 4.2 + + Sets the width of the margin around the contents of the widget to \a margin. + + This function uses QWidget::setContentsMargins() to set the margin. + \sa margin(), QWidget::setContentsMargins() +*/ + +/*! + \fn int Q3GroupBox::margin() const + \since 4.2 + + Returns the width of the the margin around the contents of the widget. + + This function uses QWidget::getContentsMargins() to get the margin. + + \sa setMargin(), QWidget::getContentsMargins() +*/ + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3groupbox.h b/src/qt3support/widgets/q3groupbox.h new file mode 100644 index 0000000..39dc7e3 --- /dev/null +++ b/src/qt3support/widgets/q3groupbox.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3GROUPBOX_H +#define Q3GROUPBOX_H + +#include <QtGui/qgroupbox.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class Q3GroupBoxPrivate; + +class Q_COMPAT_EXPORT Q3GroupBox : public QGroupBox +{ + Q_OBJECT +public: + enum +#if defined(Q_MOC_RUN) + FrameShape +#else + DummyFrame +#endif + { Box = QFrame::Box, Sunken = QFrame::Sunken, Plain = QFrame::Plain, + Raised = QFrame::Raised, MShadow=QFrame::Shadow_Mask, NoFrame = QFrame::NoFrame, + Panel = QFrame::Panel, StyledPanel = QFrame::StyledPanel, HLine = QFrame::HLine, + VLine = QFrame::VLine, + WinPanel = QFrame::WinPanel,ToolBarPanel = QFrame::StyledPanel, + MenuBarPanel = QFrame::StyledPanel, PopupPanel = QFrame::StyledPanel, + LineEditPanel = QFrame::StyledPanel,TabWidgetPanel = QFrame::StyledPanel, + GroupBoxPanel = 0x0007, + MShape = QFrame::Shape_Mask}; + + typedef DummyFrame FrameShape; + Q_ENUMS(FrameShape) + + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation DESIGNABLE false) + Q_PROPERTY(int columns READ columns WRITE setColumns DESIGNABLE false) + + Q_PROPERTY(QRect frameRect READ frameRect WRITE setFrameRect DESIGNABLE false) + Q_PROPERTY(FrameShape frameShape READ frameShape WRITE setFrameShape) + Q_PROPERTY(FrameShape frameShadow READ frameShadow WRITE setFrameShadow) + Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth) + Q_PROPERTY(int midLineWidth READ midLineWidth WRITE setMidLineWidth) + Q_PROPERTY(int margin READ margin WRITE setMargin) + +public: + explicit Q3GroupBox(QWidget* parent=0, const char* name=0); + explicit Q3GroupBox(const QString &title, + QWidget* parent=0, const char* name=0); + Q3GroupBox(int strips, Qt::Orientation o, + QWidget* parent=0, const char* name=0); + Q3GroupBox(int strips, Qt::Orientation o, const QString &title, + QWidget* parent=0, const char* name=0); + ~Q3GroupBox(); + + virtual void setColumnLayout(int strips, Qt::Orientation o); + + int columns() const; + void setColumns(int); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation); + + int insideMargin() const; + int insideSpacing() const; + void setInsideMargin(int m); + void setInsideSpacing(int s); + + void addSpace(int); + + void setFrameRect(QRect); + QRect frameRect() const; +#ifdef qdoc + void setFrameShadow(FrameShape); + FrameShape frameShadow() const; + void setFrameShape(FrameShape); + FrameShape frameShape() const; +#else + void setFrameShadow(DummyFrame); + DummyFrame frameShadow() const; + void setFrameShape(DummyFrame); + DummyFrame frameShape() const; +#endif + void setFrameStyle(int); + int frameStyle() const; + int frameWidth() const; + void setLineWidth(int); + int lineWidth() const; + void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); } + int margin() const + { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; } + void setMidLineWidth(int); + int midLineWidth() const; + +protected: + void childEvent(QChildEvent *); + void resizeEvent(QResizeEvent *); + void changeEvent(QEvent *); + bool event(QEvent *); + +private: + void skip(); + void init(); + void calculateFrame(); + void insertWid(QWidget*); + void drawFrame(QPainter *p); + + Q3GroupBoxPrivate * d; + + Q_DISABLE_COPY(Q3GroupBox) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3GROUPBOX_H diff --git a/src/qt3support/widgets/q3hbox.cpp b/src/qt3support/widgets/q3hbox.cpp new file mode 100644 index 0000000..87fe2fa --- /dev/null +++ b/src/qt3support/widgets/q3hbox.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3hbox.h" +#include "qlayout.h" +#include "qapplication.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Q3HBox + \brief The Q3HBox widget provides horizontal geometry management + for its child widgets. + + \compat + + 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 Q3HBox + + \sa QHBoxLayout Q3VBox Q3Grid +*/ + + +/*! + Constructs an hbox widget with parent \a parent, called \a name. + The parent, name and widget flags, \a f, are passed to the Q3Frame + constructor. +*/ +Q3HBox::Q3HBox(QWidget *parent, const char *name, Qt::WindowFlags f) + :Q3Frame(parent, name, f) +{ + (new QHBoxLayout(this, frameWidth(), frameWidth(), name))->setAutoAdd(true); +} + + +/*! + Constructs a horizontal hbox if \a horizontal is TRUE, otherwise + constructs a vertical hbox (also known as a vbox). + + This constructor is provided for the QVBox class. You should never + need to use it directly. + + The \a parent, \a name and widget flags, \a f, are passed to the + Q3Frame constructor. +*/ + +Q3HBox::Q3HBox(bool horizontal, QWidget *parent , const char *name, Qt::WindowFlags f) + :Q3Frame(parent, name, f) +{ + (new QBoxLayout(this, horizontal ? QBoxLayout::LeftToRight : QBoxLayout::Down, + frameWidth(), frameWidth(), name))->setAutoAdd(true); +} + +/*!\reimp + */ +void Q3HBox::frameChanged() +{ + if (layout()) + layout()->setMargin(frameWidth()); +} + + +/*! + Sets the spacing between the child widgets to \a space. +*/ + +void Q3HBox::setSpacing(int space) +{ + if (layout()) + layout()->setSpacing(space); +} + + +/*! + \reimp +*/ + +QSize Q3HBox::sizeHint() const +{ + QWidget *mThis = (QWidget*)this; + QApplication::sendPostedEvents(mThis, QEvent::ChildInserted); + return Q3Frame::sizeHint(); +} + +/*! + Sets the stretch factor of widget \a w to \a stretch. Returns true if + \a w is found. Otherwise returns false. + + \sa QBoxLayout::setStretchFactor() \link layout.html Layouts\endlink +*/ +bool Q3HBox::setStretchFactor(QWidget* w, int stretch) +{ + QApplication::sendPostedEvents(this, QEvent::ChildInserted); + if (QBoxLayout *lay = qobject_cast<QBoxLayout *>(layout())) + return lay->setStretchFactor(w, stretch); + return false; +} + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3hbox.h b/src/qt3support/widgets/q3hbox.h new file mode 100644 index 0000000..71a6d04 --- /dev/null +++ b/src/qt3support/widgets/q3hbox.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3HBOX_H +#define Q3HBOX_H + +#include <Qt3Support/q3frame.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class QBoxLayout; + +class Q_COMPAT_EXPORT Q3HBox : public Q3Frame +{ + Q_OBJECT +public: + Q3HBox(QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0); + + void setSpacing(int); + bool setStretchFactor(QWidget*, int stretch); + QSize sizeHint() const; + +protected: + Q3HBox(bool horizontal, QWidget* parent, const char* name, Qt::WindowFlags f = 0); + void frameChanged(); + +private: + Q_DISABLE_COPY(Q3HBox) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3HBOX_H diff --git a/src/qt3support/widgets/q3header.cpp b/src/qt3support/widgets/q3header.cpp new file mode 100644 index 0000000..69c5e8b --- /dev/null +++ b/src/qt3support/widgets/q3header.cpp @@ -0,0 +1,2040 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3header.h" +#ifndef QT_NO_HEADER +#include "qapplication.h" +#include "qbitarray.h" +#include "qcursor.h" +#include "qdrawutil.h" +#include "qevent.h" +#include "qpainter.h" +#include "qpixmap.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qvector.h" + +QT_BEGIN_NAMESPACE + +class Q3HeaderData +{ +public: + Q3HeaderData(int n) + { + count = n; + sizes.resize(n); + positions.resize(n); + labels.resize(n); + nullStringLabels.resize(n); + icons.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; + } + ~Q3HeaderData() + { + for (int i = 0; i < icons.size(); ++i) + delete icons.at(i); + } + + + QVector<int> sizes; + int height; // we abuse the heights as widths for vertical layout + bool heightDirty; + QVector<int> positions; // sorted by index + QVector<QString> labels; + QVector<QIcon *> icons; + QVector<int> i2s; + QVector<int> s2i; + + QBitArray clicks; + QBitArray resize; + QBitArray nullStringLabels; + 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; + } +}; + +static QStyleOptionHeader getStyleOption(const Q3Header *header, int section) +{ + QStyleOptionHeader opt; + opt.init(header); + opt.section = section; + opt.textAlignment = Qt::AlignVCenter; + opt.iconAlignment = Qt::AlignVCenter; + if (header->iconSet(section)) + opt.icon = *header->iconSet(section); + opt.text = header->label(section); + if (header->orientation() == Qt::Horizontal) + opt.state = QStyle::State_Horizontal; + return opt; +} + +bool qt_get_null_label_bit(Q3HeaderData *data, int section) +{ + return data->nullStringLabels.testBit(section); +} + +void qt_set_null_label_bit(Q3HeaderData *data, int section, bool b) +{ + data->nullStringLabels.setBit(section, b); +} + +/*! + \class Q3Header + \brief The Q3Header class provides a header row or column, e.g. for + tables and listviews. + + \compat + + 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 Q3Table and Q3ListView for example. + + A header is composed of one or more \e sections, each of which can + display a text label and an \link QIcon icon\endlink. A sort + indicator (an arrow) can also be displayed using + setSortIndicator(). + + Sections are added with addLabel() and removed with removeLabel(). + The label and icon 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 + Q3Header 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. + + Q3Header 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. + + \sa Q3ListView Q3Table +*/ + + + +/*! + Constructs a horizontal header called \a name, with parent \a + parent. +*/ + +Q3Header::Q3Header(QWidget *parent, const char *name) + : QWidget(parent, name, Qt::WStaticContents) +{ + orient = Qt::Horizontal; + init(0); +} + +/*! + Constructs a horizontal header called \a name, with \a n sections + and parent \a parent. +*/ + +Q3Header::Q3Header(int n, QWidget *parent, const char *name) + : QWidget(parent, name, Qt::WStaticContents) +{ + orient = Qt::Horizontal; + init(n); +} + +/*! + Destroys the header and all its sections. +*/ + +Q3Header::~Q3Header() +{ + delete d; + d = 0; +} + +/*! \reimp + */ + +void Q3Header::showEvent(QShowEvent *e) +{ + calculatePositions(); + QWidget::showEvent(e); +} + +/*! + \fn void Q3Header::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 Q3Header::clicked(int section) + + If isClickEnabled() is true, this signal is emitted when the user + clicks section \a section. + + \sa pressed(), released() +*/ + +/*! + \fn void Q3Header::pressed(int section) + + This signal is emitted when the user presses section \a section + down. + + \sa released() +*/ + +/*! + \fn void Q3Header::released(int section) + + This signal is emitted when section \a section is released. + + \sa pressed() +*/ + + +/*! + \fn void Q3Header::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 Q3Header::moved(int fromIndex, int toIndex) + + 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 Q3Header::sectionClicked(int index) + + 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 Q3Header::cellSize(int) const + + Use sectionSize() instead. + + Returns the size in pixels of the section that is displayed at + the index \a i. +*/ + +/*! + \fn void Q3Header::sectionHandleDoubleClicked(int section) + + This signal is emitted when the user doubleclicks on the edge + (handle) of section \a section. +*/ + +/*! + + 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 Q3Header::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 Q3Header::count + \brief the number of sections in the header +*/ + +int Q3Header::count() const +{ + return d->count; +} + + +/*! + \property Q3Header::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 Q3Header::init(int n) +{ + state = Idle; + cachedPos = 0; // unused + d = new Q3HeaderData(n); + d->height = 0; + d->heightDirty = true; + offs = 0; + if(reverse()) + offs = d->lastPos - width(); + oldHandleIdx = oldHIdxSize = handleIdx = 0; + + setMouseTracking(true); + trackingIsOn = false; + setBackgroundRole(QPalette::Button); + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + setAttribute(Qt::WA_PaintOutsidePaintEvent); +} + +/*! + \property Q3Header::orientation + \brief the header's orientation + + The orientation is either Qt::Vertical or Qt::Horizontal (the + default). + + Call setOrientation() before adding labels if you don't provide a + size parameter otherwise the sizes will be incorrect. +*/ + +void Q3Header::setOrientation(Qt::Orientation orientation) +{ + if (orient == orientation) + return; + orient = orientation; + if (orient == Qt::Horizontal) + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + else + setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred)); + update(); + updateGeometry(); +} + + +/* + Paints a rectangle starting at \a p, with length \s. +*/ +void Q3Header::paintRect(int p, int s) +{ + QPainter paint(this); + paint.setPen(QPen(Qt::black, 1, Qt::DotLine)); + if (reverse()) + paint.drawRect(p - s, 3, s, height() - 5); + else if (orient == Qt::Horizontal) + paint.drawRect(p, 3, s, height() - 5); + else + paint.drawRect(3, p, height() - 5, s); +} + +/* + Marks the division line at \a idx. +*/ +void Q3Header::markLine(int idx) +{ + QPainter paint(this); + paint.setPen(QPen(Qt::black, 1, Qt::DotLine)); + int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize); + int p = pPos(idx); + int x = p - MARKSIZE/2; + int y = 2; + int x2 = p + MARKSIZE/2; + int y2 = height() - 3; + if (orient == Qt::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 Q3Header::unMarkLine(int idx) +{ + if (idx < 0) + return; + int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize); + int p = pPos(idx); + int x = p - MARKSIZE/2; + int y = 2; + int x2 = p + MARKSIZE/2; + int y2 = height() - 3; + if (orient == Qt::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 Q3Header::cellAt(int) const + + 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 Q3Header::findLine(int c) +{ + int i = 0; + if (c > d->lastPos || (reverse() && c < 0)) { + return d->count; + } else { + int section = sectionAt(c); + if (section < 0) + return handleIdx; + i = d->s2i[section]; + } + int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize); + if (i == handleIdx) + return i; + if (i == handleIdx - 1 && pPos(handleIdx) - c > MARKSIZE/2) + return i; + if (i == handleIdx + 1 && c - pPos(i) > MARKSIZE/2) + return i + 1; + if (c - pPos(i) > pSize(i) / 2) + return i + 1; + else + return i; +} + +/*! + Returns the handle at position \a p, or -1 if there is no handle at \a p. +*/ +int Q3Header::handleAt(int p) +{ + int section = d->sectionAt(p); + if (section >= 0) { + int GripMargin = (bool)d->resize[section] ? + style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0; + int index = d->s2i[section]; + if ((index > 0 && p < d->positions[index] + GripMargin) || + (p > d->positions[index] + d->sizes[section] - GripMargin)) { + if (index > 0 && p < d->positions[index] + GripMargin) + section = d->i2s[--index]; + // don't 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; +} + +/*! + Use moveSection() instead. + + Moves the section that is currently displayed at index \a fromIdx + to index \a toIdx. +*/ + +void Q3Header::moveCell(int fromIdx, int toIdx) +{ + moveSection(mapToSection(fromIdx), toIdx); +} + + + +/*! + Move and signal and repaint. + */ + +void Q3Header::handleColumnMove(int fromIdx, int toIdx) +{ + int s = d->i2s[fromIdx]; + if (fromIdx < toIdx) + toIdx++; //Convert to + QRect r = sRect(fromIdx); + r |= sRect(toIdx); + moveSection(s, toIdx); + update(r); + emit moved(fromIdx, toIdx); + emit indexChange(s, fromIdx, toIdx); +} + +/*! + \reimp +*/ +void Q3Header::keyPressEvent(QKeyEvent *e) +{ + int i = d->focusIdx; + if (e->key() == Qt::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() == Qt::Horizontal && (e->key() == Qt::Key_Right || e->key() == Qt::Key_Left)) + || (orientation() == Qt::Vertical && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down))) { + int dir = e->key() == Qt::Key_Right || e->key() == Qt::Key_Down ? 1 : -1; + int s = d->i2s[i]; + if (e->state() & Qt::ControlButton && d->resize[s]) { + //resize + int step = e->state() & Qt::ShiftButton ? dir : 10*dir; + int c = d->positions[i] + d->sizes[s] + step; + handleColumnResize(i, c, true); + } else if (e->state() & (Qt::AltButton|Qt::MetaButton) && d->move) { + //move section + int i2 = (i + count() + dir) % count(); + d->focusIdx = i2; + handleColumnMove(i, i2); + } else { + //focus on different section + QRect r = sRect(d->focusIdx); + d->focusIdx = (d->focusIdx + count() + dir) % count(); + r |= sRect(d->focusIdx); + update(r); + } + } else { + e->ignore(); + } +} + +/*! + \reimp +*/ +void Q3Header::keyReleaseEvent(QKeyEvent *e) +{ + switch (e->key()) { + case Qt::Key_Space: + //double check that this wasn't started with the mouse + if (state == Pressed && handleIdx == d->focusIdx) { + repaint(sRect(handleIdx)); + 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 Q3Header::mousePressEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton || state != Idle) + return; + oldHIdxSize = handleIdx; + handleIdx = 0; + int c = orient == Qt::Horizontal ? e->pos().x() : e->pos().y(); + c += offset(); + if (reverse()) + c = d->lastPos - c; + + int section = d->sectionAt(c); + if (section < 0) + return; + int GripMargin = (bool)d->resize[section] ? + style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0; + int index = d->s2i[section]; + + if ((index > 0 && c < d->positions[index] + GripMargin) || + (c > d->positions[index] + d->sizes[section] - GripMargin)) { + if (c < d->positions[index] + GripMargin) + handleIdx = index-1; + else + handleIdx = index; + if (d->lastPos <= (orient == Qt::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 Q3Header::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() != Qt::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)); + if (oldOldHandleIdx != handleIdx) + repaint(sRect(oldOldHandleIdx)); + } break; + case Sliding: { + int c = orient == Qt::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)); + if(oldOldHandleIdx != handleIdx) + repaint(sRect(oldOldHandleIdx)); + } + break; + } + case Blocked: + //nothing + break; + default: + // empty, probably. Idle, at any rate. + break; + } +} + +/*! + \reimp +*/ +void Q3Header::mouseMoveEvent(QMouseEvent *e) +{ + int c = orient == Qt::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 == Qt::Horizontal) + setCursor(Qt::splitHCursor); + else + setCursor(Qt::splitVCursor); +#endif + break; + case Blocked: + break; + case Pressed: + if (QABS(c - clickPos) > 4 && d->move) { + state = Moving; + moveToIdx = -1; +#ifndef QT_NO_CURSOR + if (orient == Qt::Horizontal) + setCursor(Qt::SizeHorCursor); + else + setCursor(Qt::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("Q3Header::mouseMoveEvent: (%s) unknown state", objectName().toLocal8Bit().data()); + break; + } +} + +/*! \reimp */ + +void Q3Header::mouseDoubleClickEvent(QMouseEvent *e) +{ + int p = orient == Qt::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 Q3Header::handleColumnResize(int index, int c, bool final, bool recalcAll) +{ + int section = d->i2s[index]; + int GripMargin = (bool)d->resize[section] ? + style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0; + int lim = d->positions[index] + 2*GripMargin; + if (c == lim) + return; + if (c < lim) + c = lim; + int oldSize = d->sizes[section]; + int newSize = c - d->positions[index]; + d->sizes[section] = newSize; + + calculatePositions(!recalcAll, !recalcAll ? section : 0); + + int pos = d->positions[index]-offset(); + if(reverse()) // repaint the whole thing. Could be optimized (lars) + repaint(0, 0, width(), height()); + else if (orient == Qt::Horizontal) + repaint(pos, 0, width() - pos, height()); + else + repaint(0, pos, width(), height() - pos); + + int os = 0, ns = 0; + if (tracking() && oldSize != newSize) { + os = oldSize; + ns = newSize; + emit sizeChange(section, oldSize, newSize); + } else if (!tracking() && final && oldHIdxSize != newSize) { + os = oldHIdxSize; + ns = newSize; + emit sizeChange(section, oldHIdxSize, newSize); + } + + if (os != ns) { + if (d->fullSize == -1) { + d->fullSize = count() - 1; + adjustHeaderSize(); + d->fullSize = -1; + } else if (d->fullSize >= 0) { + int old = d->fullSize; + d->fullSize = count() - 1; + adjustHeaderSize(); + d->fullSize = old; + } + } +} + +/*! + Returns the rectangle covered by the section at index \a index. +*/ + +QRect Q3Header::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 == Qt::Horizontal) + return QRect(s, 0, width() - s + 10, height()); + else + return QRect(0, s, width(), height() - s + 10); + } + if (section < 0) + return rect(); // ### eeeeevil + + if (reverse()) + return QRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(), + 0, d->sizes[section], height()); + else if (orient == Qt::Horizontal) + return QRect( d->positions[index]-offset(), 0, d->sizes[section], height()); + else + return QRect(0, d->positions[index]-offset(), width(), d->sizes[section]); +} + +/*! + Returns the rectangle covered by section \a section. +*/ + +QRect Q3Header::sectionRect(int section) const +{ + int index = mapToIndex(section); + if (section < 0) + return rect(); // ### eeeeevil + + if (reverse()) + return QRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(), + 0, d->sizes[section], height()); + else if (orient == Qt::Horizontal) + return QRect( d->positions[index]-offset(), 0, d->sizes[section], height()); + else + return QRect(0, d->positions[index]-offset(), width(), d->sizes[section]); +} + +/*! + \overload + + Sets the icon for section \a section to \a icon 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 Q3Header::setLabel(int section, const QIcon& icon, + const QString &s, int size) +{ + if (section < 0 || section >= count()) + return; + delete d->icons[section]; + d->icons[section] = new QIcon(icon); + 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 Q3Header::setLabel(int section, const QString &s, int size) +{ + if (section < 0 || section >= count()) + return; + d->labels[section] = s; + d->nullStringLabels.setBit(section, s.isNull()); + + setSectionSizeAndHeight(section, size); + + if (updatesEnabled()) { + updateGeometry(); + calculatePositions(); + update(); + } +} + + +bool qt_qheader_label_return_null_strings = false; +/*! + Returns the text for section \a section. If the section does not + exist, returns an empty string. +*/ +QString Q3Header::label(int section) const +{ + if (section < 0 || section >= count()) + return QString(); + QString l = d->labels.value(section); + if (!l.isNull()) + return l; + if (d->nullStringLabels.testBit(section) || qt_qheader_label_return_null_strings) + return l; + else + return QString::number(section + 1); +} + +/*! + Returns the icon set for section \a section. If the section does + not exist, 0 is returned. +*/ + +QIcon *Q3Header::iconSet(int section) const +{ + if (section < 0 || section >= count()) + return 0; + return d->icons[section]; +} + + +/*! + \overload + + Adds a new section with icon \a icon 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 Q3Header::addLabel(const QIcon& icon, const QString &s, int size) +{ + int n = count() + 1; + d->icons.resize(n + 1); + d->icons.insert(n - 1, new QIcon(icon)); + return addLabel(s, size); +} + +/*! + Removes section \a section. If the section does not exist, nothing + happens. +*/ +void Q3Header::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[i] = d->labels[i+1]; + d->labels[i+1] = QString(); + d->nullStringLabels[i] = d->nullStringLabels[i+1]; + d->nullStringLabels[i+1] = 0; + d->icons[i] = d->icons[i+1]; + d->icons[i+1] = 0; + } + + d->sizes.resize(n); + d->positions.resize(n); + d->labels.resize(n); + d->nullStringLabels.resize(n); + d->icons.resize(n); + + for (i = section; i < n; ++i) + d->s2i[i] = d->s2i[i+1]; + d->s2i.resize(n); + + if (updatesEnabled()) { + 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 (updatesEnabled()) { + for (i = 0; i < n; ++i) + if (d->i2s[i] > section) + --d->i2s[i]; + } + + if (updatesEnabled()) { + updateGeometry(); + calculatePositions(); + update(); + } +} + +QSize Q3Header::sectionSizeHint(int section, const QFontMetrics& fm) const +{ + int iw = 0; + int ih = 0; + if (d->icons[section] != 0) { + QSize isize = d->icons[section]->pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize), + QIcon::Normal).size(); + iw = isize.width() + 2; + ih = isize.height(); + } + + QRect bound; + QString label = d->labels[section]; + if (!label.isNull() || d->nullStringLabels.testBit(section)) { + int lines = label.count(QLatin1Char('\n')) + 1; + int w = 0; + if (lines > 1) { + bound.setHeight(fm.height() + fm.lineSpacing() * (lines - 1)); + QStringList list = label.split(QLatin1Char('\n')); + for (int i=0; i < list.count(); ++i) { + int tmpw = fm.width(list.at(i)); + w = QMAX(w, tmpw); + } + } else { + bound.setHeight(fm.height()); + w = fm.width(label); + } + bound.setWidth(w); + } + int arrowWidth = 0; + if (d->sortSection == section) + arrowWidth = ((orient == Qt::Horizontal ? height() : width()) / 2) + 8; + int height = qMax(bound.height() + 2, ih) + 4; + int width = bound.width() + style()->pixelMetric(QStyle::PM_HeaderMargin) * 4 + + iw + arrowWidth; + return QSize(width, height); +} + +/* + Sets d->sizes[\a section] to a bounding rect based on its size + hint and font metrics, but constrained by \a size. It also updates + d->height. +*/ +void Q3Header::setSectionSizeAndHeight(int section, int size) +{ + QSize sz = sectionSizeHint(section, fontMetrics()); + + if (size < 0) { + if (d->sizes[section] < 0) + d->sizes[section] = (orient == Qt::Horizontal) ? sz.width() + : sz.height(); + } else { + d->sizes[section] = size; + } + + int newHeight = (orient == Qt::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 Q3Header::addLabel(const QString &s, int size) +{ + int n = ++d->count; + if ((int)d->icons.size() < n ) + d->icons.resize(n); + if ((int)d->sizes.size() < n ) { + d->labels.resize(n); + d->nullStringLabels.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, s); + d->nullStringLabels.setBit(section, s.isNull()); + } + + 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 (updatesEnabled()) { + updateGeometry(); + calculatePositions(); + update(); + } + return index; +} + +void Q3Header::resizeArrays(int size) +{ + d->icons.resize(size); + d->labels.resize(size); + d->nullStringLabels.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 Q3Header::setIsATableHeader(bool b) +{ + d->is_a_table_header = b; +} + +/*! \reimp */ +QSize Q3Header::sizeHint() const +{ + int width; + int height; + + ensurePolished(); + QFontMetrics fm = fontMetrics(); + + if (d->heightDirty) { + d->height = fm.lineSpacing() + 6; + for (int i = 0; i < count(); i++) { + int h = orient == Qt::Horizontal ? + sectionSizeHint(i, fm).height() : sectionSizeHint(i, fm).width(); + d->height = qMax(d->height, h); + } + d->heightDirty = false; + } + + if (orient == Qt::Horizontal) { + height = fm.lineSpacing() + 6; + width = 0; + height = qMax(height, d->height); + for (int i = 0; i < count(); i++) + width += d->sizes[i]; + } else { + width = fm.width(QLatin1Char(' ')); + height = 0; + width = qMax(width, d->height); + for (int i = 0; i < count(); i++) + height += d->sizes[i]; + } + QStyleOptionHeader opt = getStyleOption(this, 0); + return style()->sizeFromContents(QStyle::CT_Q3Header, &opt, QSize(width, height), + this).expandedTo(QApplication::globalStrut()); +} + +/*! + \property Q3Header::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 Q3Header::offset() const +{ + if (reverse()) + return d->lastPos - width() - offs; + return offs; +} + +void Q3Header::setOffset(int x) +{ + int oldOff = offset(); + offs = x; + if(d->lastPos < (orient == Qt::Horizontal ? width() : height())) + offs = 0; + else if (reverse()) + offs = d->lastPos - width() - x; + if (orient == Qt::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 Q3Header::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 Q3Header::pSize(int i) const +{ + return d->sizes[d->i2s[i]]; +} + +/*! + 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 Q3Header::mapToLogical(int a) const +{ + return mapToSection(a); +} + + +/*! + 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 Q3Header::mapToActual(int l) const +{ + return mapToIndex(l); +} + + +/*! + Use resizeSection() instead. + + Sets the size of the section \a section to \a s pixels. + + \warning does not repaint or send out signals +*/ + +void Q3Header::setCellSize(int section, int s) +{ + if (section < 0 || section >= count()) + return; + d->sizes[section] = s; + if (updatesEnabled()) + calculatePositions(); + else + d->positionsDirty = true; +} + + +/*! + 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: + \snippet doc/src/snippets/code/src_qt3support_widgets_q3header.cpp 0 + + If the user resizes a section, a sizeChange() signal is emitted. + + \sa setMovingEnabled() setClickEnabled() setTracking() +*/ + +void Q3Header::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 Q3Header::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 Q3Header::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 Q3Header::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 Q3Header::paintSection(QPainter *p, int index, const QRect& fr) +{ + int section = mapToSection(index); + QStyleOptionHeader opt = getStyleOption(this, section); + opt.state |= QStyle::State_Raised; + opt.rect = fr; + + if (section < 0) { + style()->drawControl(QStyle::CE_Header, &opt, p, this); + return; + } + + if (sectionSize(section) <= 0) + return; + + opt.state = (orient == Qt::Horizontal ? QStyle::State_Horizontal : QStyle::State_None); + if (d->sortSection == section) + opt.sortIndicator = d->sortDirection ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp; + + if (isEnabled()) + opt.state |= QStyle::State_Enabled; + if (isClickEnabled(section) && (state == Pressed || state == Moving) && index == handleIdx) + opt.state |= QStyle::State_Sunken; //currently pressed + if (!(opt.state & QStyle::State_Sunken)) + opt.state |= QStyle::State_Raised; + p->setBrushOrigin(fr.topLeft()); + if (d->clicks[section]) { + style()->drawControl(QStyle::CE_Header, &opt, p, this); + } else { + p->save(); + p->setClipRect(fr); // hack to keep styles working + opt.rect.setRect(fr.x() + 1, fr.y(), fr.width(), fr.height()); + style()->drawControl(QStyle::CE_Header, &opt, p, this); + if (orient == Qt::Horizontal) { + p->setPen(palette().color(QPalette::Mid)); + p->drawLine(fr.x() - 1, 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); + } else { + p->setPen(palette().color(QPalette::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(palette().color(QPalette::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(palette().color(QPalette::Mid)); + p->drawLine(fr.x(), fr.y() + fr.height() - 2, + fr.x() + fr.width() - 1, fr.y() + fr.height() - 2); + } + } + p->restore(); + } +} + +/*! + 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 Q3Header::paintSectionLabel(QPainter *p, int index, const QRect& fr) +{ + int section = mapToSection(index); + if (section < 0) + return; + + int dx = 0, dy = 0; + QStyleOptionHeader opt = getStyleOption(this, section); + if (d->sortSection == section) + opt.sortIndicator = d->sortDirection ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp; + if (index == handleIdx && (state == Pressed || state == Moving)) { + dx = style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &opt, this); + dy = style()->pixelMetric(QStyle::PM_ButtonShiftVertical, &opt, this); + opt.state |= QStyle::State_Sunken; + } + if (isEnabled()) + opt.state |= QStyle::State_Enabled; + + + opt.rect.setRect(fr.x() + style()->pixelMetric(QStyle::PM_HeaderMargin) + dx, fr.y() + 2 + dy, + fr.width() - 6, fr.height() - 4); + + style()->drawControl(QStyle::CE_HeaderLabel, &opt, p, this); + + int arrowWidth = (orient == Qt::Horizontal ? height() : width()) / 2; + int arrowHeight = fr.height() - 6; + QSize ssh = sectionSizeHint(section, p->fontMetrics()); + int tw = (orient == Qt::Horizontal ? ssh.width() : ssh.height()); + int ew = 0; + + if (style()->styleHint(QStyle::SH_Header_ArrowAlignment, 0, this) & Qt::AlignRight) + ew = fr.width() - tw - 8; + if (d->sortSection == section && tw <= fr.width()) { + if (reverse()) { + tw = fr.width() - tw; + ew = fr.width() - ew - tw; + } + opt.state = QStyle::State_None; + if (isEnabled()) + opt.state |= QStyle::State_Enabled; + if (d->sortDirection) + opt.state |= QStyle::State_DownArrow; + else + opt.state |= QStyle::State_UpArrow; + QRect ar(fr.x() + tw - arrowWidth - 6 + ew, 4, arrowWidth, arrowHeight); + if (label(section).isRightToLeft()) + ar.moveBy( 2*(fr.right() - ar.right()) + ar.width() - fr.width(), 0 ); + opt.rect = ar; + style()->drawPrimitive(QStyle::PE_IndicatorHeaderArrow, &opt, p, this); + } +} + + +/*! \reimp */ +void Q3Header::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + p.setPen(palette().buttonText().color()); + int pos = orient == Qt::Horizontal ? e->rect().left() : e->rect().top(); + int id = mapToIndex(sectionAt(pos + offset())); + if (id < 0) { + if (pos > 0) + id = d->count; + else if (reverse()) + id = d->count - 1; + else + id = 0; + } + if (reverse()) { + for (int i = id; i >= 0; i--) { + QRect r = sRect(i); + paintSection(&p, i, r); + if (r.right() >= e->rect().right()) + return; + } + } else { + if (count() > 0) { + for (int i = id; i <= count(); i++) { + QRect r = sRect(i); + /* + If the last section is clickable (and thus is + painted raised), draw the virtual section count() + as well. Otherwise it looks ugly. + */ + if (i < count() || d->clicks[mapToSection(count() - 1)]) + paintSection(&p, i, r); + if (hasFocus() && d->focusIdx == i) { + QStyleOptionFocusRect opt; + opt.rect.setRect(r.x()+2, r.y()+2, r.width()-4, r.height()-4); + opt.palette = palette(); + opt.state = QStyle::State_None; + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this); + } + if ((orient == Qt::Horizontal && r. right() >= e->rect().right()) + || (orient == Qt::Vertical && r. bottom() >= e->rect().bottom())) + return; + } + } + } +} + +/*! + \overload + + Sets the sort indicator to \a ascending. Use the other overload instead. +*/ + +void Q3Header::setSortIndicator(int section, bool ascending) +{ + d->sortSection = section; + if (section != -1) + oldHandleIdx = section; + d->sortDirection = ascending; + update(); + updateGeometry(); +} + +/*! + \fn void Q3Header::setSortIndicator(int section, Qt::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 Q3Header::sortIndicatorSection() const +{ + return d->sortSection; +} + +/*! + Returns the implied sort order of the Q3Headers sort indicator. + + \sa setSortIndicator(), sortIndicatorSection() +*/ + +Qt::SortOrder Q3Header::sortIndicatorOrder() const +{ + return d->sortDirection ? Qt::AscendingOrder : Qt::DescendingOrder; +} + +/*! + Resizes section \a section to \a s pixels wide (or high). +*/ + +void Q3Header::resizeSection(int section, int s) +{ + setCellSize(section, s); + update(); +} + +/*! + Returns the width (or height) of the \a section in pixels. +*/ + +int Q3Header::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 Q3Header::sectionPos(int section) const +{ + if (d->positionsDirty) + ((Q3Header *)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 Q3Header::sectionAt(int pos) const +{ + if (reverse()) + pos = d->lastPos - pos; + return d->sectionAt(pos); +} + +/*! + Returns the number of the section that is displayed at index + position \a index. +*/ + +int Q3Header::mapToSection(int index) const +{ + return (index >= 0 && index < count()) ? d->i2s[index] : -1; +} + +/*! + Returns the index position at which section \a section is + displayed. +*/ + +int Q3Header::mapToIndex(int section) const +{ + return (section >= 0 && section < count()) ? d->s2i[section] : -1; +} + +/*! + Moves section \a section to index position \a toIndex. +*/ + +void Q3Header::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 Q3Header::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 Q3Header::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 Q3Header::isMovingEnabled() const +{ + return d->move; +} + +/*! \internal */ + +void Q3Header::setUpdatesEnabled(bool enable) +{ + if (enable) + calculatePositions(); + QWidget::setUpdatesEnabled(enable); +} + + +bool Q3Header::reverse () const +{ +#if 0 + return (orient == Qt::Horizontal && QApplication::reverseLayout()); +#else + return false; +#endif +} + +/*! \reimp */ +void Q3Header::resizeEvent(QResizeEvent *e) +{ + if (e) + QWidget::resizeEvent(e); + + if(d->lastPos < width()) { + offs = 0; + } + + if (e) { + adjustHeaderSize(orientation() == Qt::Horizontal ? + width() - e->oldSize().width() : height() - e->oldSize().height()); + if ((orientation() == Qt::Horizontal && height() != e->oldSize().height()) + || (orientation() == Qt::Vertical && width() != e->oldSize().width())) + update(); + } else + adjustHeaderSize(); +} + +/*! + \fn void Q3Header::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 Q3Header::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 == Qt::Horizontal ? width() : height())) + return; + + if (d->fullSize >= 0) { + int sec = mapToSection(d->fullSize); + int lsec = mapToSection(count() - 1); + int ns = sectionSize(sec) + + (orientation() == Qt::Horizontal ? + width() : height()) - (sectionPos(lsec) + sectionSize(lsec)); + int os = sectionSize(sec); + if (ns < 20) + ns = 20; + setCellSize(sec, ns); + repaint(); + emit sizeChange(sec, os, ns); + } else if (d->fullSize == -1) { + int df = diff / count(); + int part = orientation() == Qt::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() == Qt::Horizontal ? width() : height()) - sectionPos(sec); + int os = sectionSize(sec); + if (ns < 20) + ns = 20; + setCellSize(sec, ns); + repaint(); + emit sizeChange(sec, os, ns); + } +} + +/*! + Returns the total width of all the header columns. +*/ +int Q3Header::headerWidth() const +{ + if (d->pos_dirty) { + ((Q3Header*)this)->calculatePositions(); + d->pos_dirty = false; + } + return d->lastPos; +} + +void Q3Header::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() == Qt::Horizontal ? width() : height())) + break; + } + d->pos_dirty = onlyVisible; +} + + +/*! + \property Q3Header::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 Q3Header::setStretchEnabled(bool b, int section) +{ + if (b) + d->fullSize = section; + else + d->fullSize = -2; + adjustHeaderSize(); +} + +bool Q3Header::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 Q3Header::isStretchEnabled(int section) const +{ + return d->fullSize == section; +} + +/*! + \reimp +*/ +void Q3Header::changeEvent(QEvent *ev) +{ + if(ev->type() == QEvent::FontChange) { + QFontMetrics fm = fontMetrics(); + d->height = (orient == Qt::Horizontal) ? fm.lineSpacing() + 6 : fm.width(QLatin1Char(' ')); + } + QWidget::changeEvent(ev); +} + +QT_END_NAMESPACE + +#endif // QT_NO_HEADER diff --git a/src/qt3support/widgets/q3header.h b/src/qt3support/widgets/q3header.h new file mode 100644 index 0000000..3768aa0 --- /dev/null +++ b/src/qt3support/widgets/q3header.h @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3HEADER_H +#define Q3HEADER_H + +#include <QtGui/qicon.h> +#include <QtGui/qwidget.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_HEADER + +class QShowEvent; +class Q3HeaderData; +class Q3Table; +class Q3ListView; + +class Q_COMPAT_EXPORT Q3Header : public QWidget +{ + friend class Q3Table; + friend class Q3TableHeader; + friend class Q3ListView; + + Q_OBJECT + Q_PROPERTY(Qt::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: + Q3Header(QWidget* parent=0, const char* name=0); + Q3Header(int, QWidget* parent=0, const char* name=0); + ~Q3Header(); + + int addLabel(const QString &, int size = -1); + int addLabel(const QIcon&, const QString &, int size = -1); + void removeLabel(int section); + virtual void setLabel(int, const QString &, int size = -1); + virtual void setLabel(int, const QIcon&, const QString &, int size = -1); + QString label(int section) const; + QIcon* iconSet(int section) const; + + virtual void setOrientation(Qt::Orientation); + Qt::Orientation orientation() const; + virtual void setTracking(bool enable); + bool tracking() const; + + virtual void setClickEnabled(bool, int section = -1); + virtual void setResizeEnabled(bool, int section = -1); + virtual void setMovingEnabled(bool); + virtual void setStretchEnabled(bool b, int section); + void setStretchEnabled(bool b) { setStretchEnabled(b, -1); } + bool isClickEnabled(int section = -1) const; + bool isResizeEnabled(int section = -1) const; + bool isMovingEnabled() const; + bool isStretchEnabled() const; + bool isStretchEnabled(int section) const; + + void resizeSection(int section, int s); + int sectionSize(int section) const; + int sectionPos(int section) const; + int sectionAt(int pos) const; + int count() const; + int headerWidth() const; + QRect sectionRect(int section) const; + + virtual void setCellSize(int , int); // obsolete, do not use + int cellSize(int i) const { return sectionSize(mapToSection(i)); } // obsolete, do not use + int cellPos(int) const; // obsolete, do not use + int cellAt(int pos) const { return mapToIndex(sectionAt(pos + offset())); } // obsolete, do not use + + int offset() const; + + QSize sizeHint() const; + + int mapToSection(int index) const; + int mapToIndex(int section) const; + int mapToLogical(int) const; // obsolete, do not use + int mapToActual(int) const; // obsolete, do not use + + void moveSection(int section, int toIndex); + virtual void moveCell(int, int); // obsolete, do not use + + void setSortIndicator(int section, bool ascending = true); // obsolete, do not use + inline void setSortIndicator(int section, Qt::SortOrder order) + { setSortIndicator(section, (order == Qt::AscendingOrder)); } + int sortIndicatorSection() const; + Qt::SortOrder sortIndicatorOrder() const; + + void adjustHeaderSize() { adjustHeaderSize(-1); } + +public Q_SLOTS: + void setUpdatesEnabled(bool enable); + virtual void setOffset(int pos); + +Q_SIGNALS: + void clicked(int section); + void pressed(int section); + void released(int section); + void sizeChange(int section, int oldSize, int newSize); + void indexChange(int section, int fromIndex, int toIndex); + void sectionClicked(int); // obsolete, do not use + void moved(int, int); // obsolete, do not use + void sectionHandleDoubleClicked(int section); + +protected: + void paintEvent(QPaintEvent *); + void showEvent(QShowEvent *e); + void resizeEvent(QResizeEvent *e); + QRect sRect(int index); + + virtual void paintSection(QPainter *p, int index, const QRect& fr); + virtual void paintSectionLabel(QPainter* p, int index, const QRect& fr); + + void changeEvent(QEvent *); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + void mouseDoubleClickEvent(QMouseEvent *); + + void keyPressEvent(QKeyEvent *); + void keyReleaseEvent(QKeyEvent *); + +private: + void handleColumnMove(int fromIdx, int toIdx); + void adjustHeaderSize(int diff); + void init(int); + + void paintRect(int p, int s); + void markLine(int idx); + void unMarkLine(int idx); + int pPos(int i) const; + int pSize(int i) const; + int findLine(int); + int handleAt(int p); + bool reverse() const; + void calculatePositions(bool onlyVisible = false, int start = 0); + void handleColumnResize(int, int, bool, bool = true); + QSize sectionSizeHint(int section, const QFontMetrics& fm) const; + void setSectionSizeAndHeight(int section, int size); + + void resizeArrays(int size); + void setIsATableHeader(bool b); + int offs; + int handleIdx; + int oldHIdxSize; + int moveToIdx; + enum State { Idle, Sliding, Pressed, Moving, Blocked }; + State state; + int clickPos; + bool trackingIsOn; + int oldHandleIdx; + int cachedPos; // not used + Qt::Orientation orient; + + Q3HeaderData *d; + +private: + Q_DISABLE_COPY(Q3Header) +}; + + +inline Qt::Orientation Q3Header::orientation() const +{ + return orient; +} + +inline void Q3Header::setTracking(bool enable) { trackingIsOn = enable; } +inline bool Q3Header::tracking() const { return trackingIsOn; } + +extern Q_COMPAT_EXPORT bool qt_qheader_label_return_null_strings; // needed for professional edition + +#endif // QT_NO_HEADER + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3HEADER_H diff --git a/src/qt3support/widgets/q3hgroupbox.cpp b/src/qt3support/widgets/q3hgroupbox.cpp new file mode 100644 index 0000000..cf08389 --- /dev/null +++ b/src/qt3support/widgets/q3hgroupbox.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3hgroupbox.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Q3HGroupBox + + \brief The Q3HGroupBox widget organizes widgets in a group with one + horizontal row. + + \compat + + Q3HGroupBox is a convenience class that offers a thin layer on top + of Q3GroupBox. Think of it as a Q3HBox that offers a frame with a + title. + + \sa Q3VGroupBox +*/ + +/*! + Constructs a horizontal group box with no title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ +Q3HGroupBox::Q3HGroupBox( QWidget *parent, const char *name ) + : Q3GroupBox( 1, Qt::Vertical /* sic! */, parent, name ) +{ +} + +/*! + Constructs a horizontal group box with the title \a title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +Q3HGroupBox::Q3HGroupBox( const QString &title, QWidget *parent, + const char *name ) + : Q3GroupBox( 1, Qt::Vertical /* sic! */, title, parent, name ) +{ +} + +/*! + Destroys the horizontal group box, deleting its child widgets. +*/ +Q3HGroupBox::~Q3HGroupBox() +{ +} + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3hgroupbox.h b/src/qt3support/widgets/q3hgroupbox.h new file mode 100644 index 0000000..71baf08 --- /dev/null +++ b/src/qt3support/widgets/q3hgroupbox.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3HGROUPBOX_H +#define Q3HGROUPBOX_H + +#include <Qt3Support/q3groupbox.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class Q_COMPAT_EXPORT Q3HGroupBox : public Q3GroupBox +{ + Q_OBJECT +public: + Q3HGroupBox( QWidget* parent=0, const char* name=0 ); + Q3HGroupBox( const QString &title, QWidget* parent=0, const char* name=0 ); + ~Q3HGroupBox(); + +private: + Q_DISABLE_COPY(Q3HGroupBox) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3HGROUPBOX_H diff --git a/src/qt3support/widgets/q3mainwindow.cpp b/src/qt3support/widgets/q3mainwindow.cpp new file mode 100644 index 0000000..2ee3bdc --- /dev/null +++ b/src/qt3support/widgets/q3mainwindow.cpp @@ -0,0 +1,2427 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3mainwindow.h" +#ifndef QT_NO_MAINWINDOW + +#include "qapplication.h" +#include "qbitmap.h" +#include "qcursor.h" +#include "qdatetime.h" +#include "q3dockarea.h" +#include "qevent.h" +#include "qlayout.h" +#include "qmap.h" +#include "qmenubar.h" +#include "qpainter.h" +#include "q3popupmenu.h" +#include "q3scrollview.h" +#include "qstatusbar.h" +#include "qstringlist.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtimer.h" +#include "q3toolbar.h" +#include "qtooltip.h" +#include "qwhatsthis.h" +#ifdef Q_WS_MAC +# include <private/qt_mac_p.h> +#endif + +#include <private/q3mainwindow_p.h> + +QT_BEGIN_NAMESPACE + +class QHideDock; + +/* Q3MainWindowLayout, respects widthForHeight layouts (like the left + and right docks are) +*/ + +class Q3MainWindowLayout : public QLayout +{ + Q_OBJECT + +public: + Q3MainWindowLayout(Q3MainWindow *mw); + ~Q3MainWindowLayout() {} + + void addItem(QLayoutItem *); + void setLeftDock(Q3DockArea *l); + void setRightDock(Q3DockArea *r); + void setCentralWidget(QWidget *w); + bool hasHeightForWidth() const { return false; } + QSize sizeHint() const; + QSize minimumSize() const; + QLayoutItem *itemAt(int) const { return 0; } //### + QLayoutItem *takeAt(int) { return 0; } //### + int count() const { return 0; } //### + +protected: + void setGeometry(const QRect &r) { + QLayout::setGeometry(r); + layoutItems(r); + } + +private: + int layoutItems(const QRect&, bool testonly = false); + int extraPixels() const; + + Q3DockArea *left, *right; + QWidget *central; + Q3MainWindow *mainWindow; + +}; + +QSize Q3MainWindowLayout::sizeHint() const +{ + int w = 0; + int h = 0; + + if (left) { + w += left->sizeHint().width(); + h = qMax(h, left->sizeHint().height()); + } + if (right) { + w += right->sizeHint().width(); + h = qMax(h, right->sizeHint().height()); + } + if (central) { + w += central->sizeHint().width(); + int diff = extraPixels(); + h = qMax(h, central->sizeHint().height() + diff); + } + return QSize(w, h); +} + +QSize Q3MainWindowLayout::minimumSize() const +{ + int w = 0; + int h = 0; + + if (left) { + QSize ms = left->minimumSizeHint().expandedTo(left->minimumSize()); + w += ms.width(); + h = qMax(h, ms.height()); + } + if (right) { + QSize ms = right->minimumSizeHint().expandedTo(right->minimumSize()); + w += ms.width(); + h = qMax(h, ms.height()); + } + if (central) { + QSize min = central->minimumSize().isNull() ? + central->minimumSizeHint() : central->minimumSize(); + w += min.width(); + int diff = extraPixels(); + h = qMax(h, min.height() + diff); + } + return QSize(w, h); +} + +Q3MainWindowLayout::Q3MainWindowLayout(Q3MainWindow *mw) + : left(0), right(0), central(0) +{ + mainWindow = mw; +} + +void Q3MainWindowLayout::setLeftDock(Q3DockArea *l) +{ + left = l; +} + +void Q3MainWindowLayout::setRightDock(Q3DockArea *r) +{ + right = r; +} + +void Q3MainWindowLayout::setCentralWidget(QWidget *w) +{ + central = w; +} + +int Q3MainWindowLayout::layoutItems(const QRect &r, bool testonly) +{ + if (!left && !central && !right) + return 0; + + int wl = 0, wr = 0; + if (left) + wl = ((Q3DockAreaLayout*)left->QWidget::layout())->widthForHeight(r.height()); + if (right) + wr = ((Q3DockAreaLayout*)right->QWidget::layout())->widthForHeight(r.height()); + int w = r.width() - wr - wl; + if (w < 0) + w = 0; + + int diff = extraPixels(); + if (!testonly) { + QRect g(geometry()); + if (left) + left->setGeometry(QRect(g.x(), g.y() + diff, wl, r.height() - diff)); + if (right) + right->setGeometry(QRect(g.x() + g.width() - wr, g.y() + diff, wr, r.height() - diff)); + if (central) + central->setGeometry(g.x() + wl, g.y() + diff, w, r.height() - diff); + } + + w = wl + wr; + if (central) + w += central->minimumSize().width(); + return w; +} + +int Q3MainWindowLayout::extraPixels() const +{ + if (mainWindow->d_func()->topDock->isEmpty() && + !(mainWindow->d_func()->leftDock->isEmpty() && + mainWindow->d_func()->rightDock->isEmpty())) { + return 2; + } else { + return 0; + } +} + +void Q3MainWindowLayout::addItem(QLayoutItem * /* item */) +{ +} + +/* + QHideToolTip and QHideDock - minimized dock +*/ + +#if 0 +class QHideToolTip : public QToolTip +{ +public: + QHideToolTip(QWidget *parent) : QToolTip(parent) {} + + void maybeTip(const QPoint &pos); +}; +#endif + + +class QHideDock : public QWidget +{ + Q_OBJECT + +public: + QHideDock(Q3MainWindow *parent) : QWidget(parent, "qt_hide_dock") { + hide(); + setFixedHeight(style()->pixelMetric(QStyle::PM_DockWidgetHandleExtent, 0, this) + 3); + pressedHandle = -1; + pressed = false; + setMouseTracking(true); + win = parent; +#if 0 + tip = new QHideToolTip(this); +#endif + } + ~QHideDock() + { +#if 0 + delete tip; +#endif + } + +protected: + void paintEvent(QPaintEvent *e) { + QObjectList childList = children(); + if (childList.isEmpty()) + return; + QPainter p(this); + p.setClipRegion(e->rect()); + p.fillRect(e->rect(), palette().brush(QPalette::Window)); + int x = 0; + for (int i = 0; i < childList.size(); ++i) { + QObject *o = childList.at(i); + Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(o); + if (!dw || !dw->isVisible()) + continue; + QStyleOptionQ3DockWindow opt; + opt.rect.setRect(x, 0, 30, 10); + opt.palette = palette(); + opt.docked = dw->area(); + opt.closeEnabled = dw->isCloseEnabled(); + opt.state = QStyle::State_None; + if (i == pressedHandle) + opt.state |= QStyle::State_On; + + style()->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &p, this); + x += 30; + } + } + + void mousePressEvent(QMouseEvent *e) { + pressed = true; + QObjectList childList = children(); + if (childList.isEmpty()) + return; + mouseMoveEvent(e); + pressedHandle = -1; + + if (e->button() == Qt::RightButton && win->isDockMenuEnabled()) { + // ### TODO: HideDock menu + } else { + mouseMoveEvent(e); + } + } + + void mouseMoveEvent(QMouseEvent *e) { + QObjectList childList = children(); + if (childList.isEmpty()) + return; + if (!pressed) + return; + int x = 0; + if (e->y() >= 0 && e->y() <= height()) { + for (int i = 0; i < childList.size(); ++i) { + QObject *o = childList.at(i); + Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(o); + if (!dw || !dw->isVisible()) + continue; + if (e->x() >= x && e->x() <= x + 30) { + int old = pressedHandle; + pressedHandle = i; + if (pressedHandle != old) + repaint(); + return; + } + x += 30; + } + } + int old = pressedHandle; + pressedHandle = -1; + if (old != -1) + repaint(); + } + + void mouseReleaseEvent(QMouseEvent *e) { + pressed = false; + if (pressedHandle == -1) + return; + QObjectList childList = children(); + if (childList.isEmpty()) + return; + if (e->button() == Qt::LeftButton) { + if (e->y() >= 0 && e->y() <= height()) { + QObject *o = childList.at(pressedHandle); + Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(o); + if (dw) { + dw->show(); + dw->dock(); + } + } + } + pressedHandle = -1; + repaint(); + } + + bool eventFilter(QObject *o, QEvent *e) { + if (o == this || !o->isWidgetType()) + return QWidget::eventFilter(o, e); + if (e->type() == QEvent::HideToParent || + e->type() == QEvent::ShowToParent) + updateState(); + return QWidget::eventFilter(o, e); + } + + void updateState() { + bool visible = true; + QObjectList childList = children(); + if (childList.isEmpty()) + return; + for (int i = 0; i < childList.size(); ++i) { + QObject *o = childList.at(i); + Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(o); + if (!dw) + continue; + if (dw->isHidden()) { + visible = false; + continue; + } + if (!dw->isVisible()) + continue; + visible = true; + break; + } + + if (visible) + show(); + else + hide(); + win->triggerLayout(false); + update(); + } + + void childEvent(QChildEvent *e) { + QWidget::childEvent(e); + if (e->type() == QEvent::ChildInserted) + e->child()->installEventFilter(this); + else + e->child()->removeEventFilter(this); + updateState(); + } + +private: + Q3MainWindow *win; + int pressedHandle; + bool pressed; +#if 0 + QHideToolTip *tip; + friend class QHideToolTip; +#endif +}; + +#if 0 +void QHideToolTip::maybeTip(const QPoint &pos) +{ + if (!parentWidget()) + return; + QHideDock *dock = (QHideDock*)parentWidget(); + + QObjectList dchilds = dock->children(); + if (dchilds.isEmpty()) + return; + int x = 0; + for (int i = 0; i < dchilds.size(); ++i) { + QObject *o = dchilds.at(i); + Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(o); + if (!dw || !dw->isVisible()) + continue; + if (pos.x() >= x && pos.x() <= x + 30) { + Q3DockWindow *dw = (Q3DockWindow*)o; + if (!dw->windowTitle().isEmpty()) + tip(QRect(x, 0, 30, dock->height()), dw->windowTitle()); + return; + } + x += 30; + } +} +#endif + +/*! + \class Q3MainWindow + \brief The Q3MainWindow class provides a main application window, + with a menu bar, dock windows (e.g. for toolbars), and a status + bar. + + \compat + + Main windows are most often used to provide menus, toolbars and a + status bar around a large central widget, such as a text edit, + drawing canvas or QWorkspace (for MDI applications). Q3MainWindow + 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. + + We'll briefly review adding menu items and + toolbar buttons then describe the facilities of Q3MainWindow + itself. + + \snippet doc/src/snippets/code/src_qt3support_widgets_q3mainwindow.cpp 0 + + Q3MainWindows 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. + + 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. Q3MainWindow automatically detects the creation of + a menu bar or status bar if you specify the Q3MainWindow 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. + + New dock windows and toolbars can be added to a Q3MainWindow using + addDockWindow(). Qt::Dock windows can be moved using moveDockWindow() + and removed with removeDockWindow(). Q3MainWindow allows default + dock window (toolbar) docking in all its dock areas (\c Top, \c + Left, \c Right, \c Bottom). You can use setDockEnabled() to + enable and disable docking areas for dock windows. When adding or + moving dock windows you can specify their 'edge' (dock area). The + currently available edges are: \c Top, \c Left, \c Right, \c + Bottom, \c Minimized (effectively a 'hidden' dock area) and \c + TornOff (floating). See \l Qt::Dock for an explanation of these + areas. Note that the *ToolBar functions are included for backward + compatibility; all new code should use the *DockWindow functions. + QToolbar is a subclass of Q3DockWindow so all functions that work + with dock windows work on toolbars in the same way. + + \target dwm + If the user clicks the close button, then the dock window is + hidden. A dock window can be hidden or unhidden by the user by + right clicking a dock area and clicking the name of the relevant + dock window on the pop up dock window menu. This menu lists the + names of every dock window; visible dock windows have a tick + beside their names. The dock window menu is created automatically + as required by createDockWindowMenu(). Since it may not always be + appropriate for a dock window to appear on this menu the + setAppropriate() function is used to inform the main window + whether or not the dock window menu should include a particular + dock window. Double clicking a dock window handle (usually on the + left-hand side of the dock window) undocks (floats) the dock + window. Double clicking a floating dock window's title bar will + dock the floating dock window. (See also + \l{Q3MainWindow::DockWindows}.) + + Some functions change the appearance of a Q3MainWindow globally: + \list + \i Q3DockWindow::setHorizontalStretchable() and + Q3DockWindow::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 QIcon for more information). + \i setUsesTextLabel() is used to set whether tool buttons + should display a textual label in addition to pixmaps + (see QToolButton for more information). + \endlist + + The user can drag dock windows into any enabled docking area. Qt::Dock + windows can also be dragged \e within a docking area, for example + to rearrange the order of some toolbars. Qt::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 + Q3DockWindow::windowTitle() or Q3ToolBar::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. + + Qt::Dock windows are moved transparently by default, i.e. during the + drag an outline rectangle is drawn on the screen representing the + position of the dock window as it moves. If you want the dock + window to be shown normally whilst it is moved use + setOpaqueMoving(). + + The location of a dock window, i.e. its dock area and position + within the dock area, can be determined by calling getLocation(). + Movable dock windows can be lined up to minimize wasted space with + lineUpDockWindows(). Pointers to the dock areas are available from + topDock(), leftDock(), rightDock() and bottomDock(). A customize + menu item is added to the pop up dock window menu if + isCustomizable() returns true; it returns false by default. + Reimplement isCustomizable() and customize() if you want to offer + this extra menu item, for example, to allow the user to change + settings relating to the main window and its toolbars and dock + windows. + + The main window's menu bar is fixed (at the top) by default. If + you want a movable menu bar, create a QMenuBar as a stretchable + widget inside its own movable dock window and restrict this dock + window to only live within the \c Top or \c Bottom dock: + + \snippet doc/src/snippets/code/src_qt3support_widgets_q3mainwindow.cpp 1 + + 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 Q3MainWindow. + + To save the layout and positions of all the dock windows do this: + + \snippet doc/src/snippets/code/src_qt3support_widgets_q3mainwindow.cpp 2 + + To restore the dock window positions and sizes (normally when the + application is next started), do the following: + + \snippet doc/src/snippets/code/src_qt3support_widgets_q3mainwindow.cpp 3 + + The QSettings class can be used in conjunction with the streaming + operators to store the application's settings. + + Q3MainWindow's management of dock windows and toolbars is done + transparently behind-the-scenes by Q3DockArea. + + For multi-document interfaces (MDI), use a QWorkspace as the + central widget. + + Adding dock windows, e.g. toolbars, to Q3MainWindow's dock areas is + straightforward. If the supplied dock areas are not sufficient for + your application we suggest that you create a QWidget subclass and + add your own dock areas (see \l Q3DockArea) to the subclass since + Q3MainWindow provides functionality specific to the standard dock + areas it provides. + + \sa Q3ToolBar Q3DockWindow QStatusBar QAction QMenuBar Q3PopupMenu QDialog +*/ + +/*! + \enum Q3MainWindow::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.) +*/ + +/*! + \fn void Q3MainWindow::addToolBar(Q3DockWindow *dockWindow, + Qt::Dock position, bool newLine); + + Adds a new toolbar to the \a dockWindow. The toolbar is placed in + the given \a position. If \a newLine is true the toolbar is put on + a new line. +*/ + +/*! + \fn void Q3MainWindow::addToolBar(Q3DockWindow *dockWindow, const + QString &label, Qt::Dock position, bool newLine) + \overload + + The toolbar has the caption \a label and is placed in the given \a + position. +*/ + +/*! + \fn void Q3MainWindow::moveToolBar(Q3DockWindow *dockWindow, Qt::Dock position); + + Moves the given \a dockWindow into the given \a position. +*/ + +/*! + \fn void Q3MainWindow::moveToolBar(Q3DockWindow *dockWindow, + Qt::Dock position, bool nl, int index, int extraOffset) + \overload + + The \a dockWindow is made the \a{index}-th item in the toolbar, + moved over by \a extraOffset. If \a nl is true, the dock window is + put on a new line. +*/ + +/*! + \fn void Q3MainWindow::removeToolBar(Q3DockWindow *dockWindow); + + Removes the toolbar from the given \a dockWindow. +*/ + +/*! + \fn void Q3MainWindow::lineUpToolBars(bool keepNewLines); + + Lines up the toolbars. Line breaks are preserved if \a + keepNewLines is true. +*/ + +/*! + \fn void Q3MainWindow::toolBarPositionChanged(Q3ToolBar *toolbar); + + This signal is emitted when a \a toolbar is moved. +*/ + +/*! + \fn bool Q3MainWindow::toolBarsMovable() const + + Returns true if the window allows its toolbars to be moved; otherwise + returns false. +*/ + +/*! + \fn void Q3MainWindow::setToolBarsMovable(bool b) + If \a b is true the tool bars can be moved. +*/ + +/*! + Constructs an empty main window. The \a parent, \a name and widget + flags \a f, are passed on to the QWidget constructor. + + By default, the widget flags are set to Qt::WType_TopLevel rather + than 0 as they are with QWidget. If you don't want your + Q3MainWindow to be a top level widget then you will need to set \a + f to 0. +*/ + +Q3MainWindow::Q3MainWindow(QWidget * parent, const char * name, Qt::WindowFlags f) + : QWidget(*new Q3MainWindowPrivate, parent, f) +{ + Q_D(Q3MainWindow); + setObjectName(QLatin1String(name)); +#ifdef Q_WS_MAC + d->opaque = true; +#else + d->opaque = false; +#endif + installEventFilter(this); + d->topDock = new Q3DockArea(Qt::Horizontal, Q3DockArea::Normal, this, "qt_top_dock"); + d->topDock->installEventFilter(this); + d->bottomDock = new Q3DockArea(Qt::Horizontal, Q3DockArea::Reverse, this, "qt_bottom_dock"); + d->bottomDock->installEventFilter(this); + d->leftDock = new Q3DockArea(Qt::Vertical, Q3DockArea::Normal, this, "qt_left_dock"); + d->leftDock->installEventFilter(this); + d->rightDock = new Q3DockArea(Qt::Vertical, Q3DockArea::Reverse, this, "qt_right_dock"); + d->rightDock->installEventFilter(this); + d->hideDock = new QHideDock(this); +} + + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3MainWindow::~Q3MainWindow() +{ + delete layout(); +} + +#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 Q3MainWindow::setMenuBar(QMenuBar * newMenuBar) +{ + Q_D(Q3MainWindow); + if (!newMenuBar) + return; + if (d->mb) + delete d->mb; + d->mb = newMenuBar; + d->mb->installEventFilter(this); + triggerLayout(); +} + + +/*! + Returns the menu bar for this window. + + If there isn't one, then menuBar() creates an empty menu bar. + + \sa statusBar() +*/ + +QMenuBar * Q3MainWindow::menuBar() const +{ + Q_D(const Q3MainWindow); + if (d->mb) + return d->mb; + + QObjectList l = queryList("QMenuBar", 0, false, false); + QMenuBar * b; + if (l.size()) { + b = static_cast<QMenuBar *>(l.at(0)); + } else { + b = new QMenuBar((Q3MainWindow *)this); + b->setObjectName(QLatin1String("automatic menu bar")); + b->show(); + } + d->mb = b; + d->mb->installEventFilter(const_cast<Q3MainWindow *>(this)); + ((Q3MainWindow *)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 Q3MainWindow::setStatusBar(QStatusBar * newStatusBar) +{ + Q_D(Q3MainWindow); + if (!newStatusBar || newStatusBar == d->sb) + return; + if (d->sb) + delete d->sb; + d->sb = newStatusBar; +#if 0 + // ### this code can cause unnecessary creation of a tool tip group + connect(toolTipGroup(), SIGNAL(showTip(QString)), + d->sb, SLOT(showMessage(QString))); + connect(toolTipGroup(), SIGNAL(removeTip()), + d->sb, SLOT(clearMessage())); +#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() +*/ + +QStatusBar * Q3MainWindow::statusBar() const +{ + Q_D(const Q3MainWindow); + if (d->sb) + return d->sb; + + QObjectList l = queryList("QStatusBar", 0, false, false); + QStatusBar * s; + if (l.size()) { + s = (QStatusBar *)l.at(0); + } else { + s = new QStatusBar((Q3MainWindow *)this, "automatic status bar"); + s->show(); + } + ((Q3MainWindow *)this)->setStatusBar(s); + ((Q3MainWindow *)this)->triggerLayout(true); + return s; +} + + +#if 0 +/*! + 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() +*/ + +void Q3MainWindow::setToolTipGroup(QToolTipGroup * newToolTipGroup) +{ + Q_D(Q3MainWindow); + if (!newToolTipGroup || newToolTipGroup == d->ttg) + return; + if (d->ttg) + delete d->ttg; + d->ttg = newToolTipGroup; + + connect(toolTipGroup(), SIGNAL(showTip(QString)), + statusBar(), SLOT(showMessage(QString))); + connect(toolTipGroup(), SIGNAL(removeTip()), + statusBar(), SLOT(clearMessage())); +} + + +/*! + Returns this main window's tool tip group. If there isn't one, + toolTipGroup() creates an empty tool tip group. + + \sa menuBar() statusBar() +*/ +QToolTipGroup * Q3MainWindow::toolTipGroup() const +{ + Q_D(const Q3MainWindow); + if (d->ttg) + return d->ttg; + + QToolTipGroup * t = new QToolTipGroup((Q3MainWindow*)this, + "automatic tool tip group"); + ((Q3MainWindowPrivate*)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 Q3MainWindow::setDockEnabled(Qt::Dock dock, bool enable) +{ + d_func()->docks.insert(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 Q3MainWindow::isDockEnabled(Qt::Dock dock) const +{ + return d_func()->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 Q3MainWindow::isDockEnabled(Q3DockArea *area) const +{ + Q_D(const Q3MainWindow); + if (area == d->leftDock) + return d->docks[Qt::DockLeft]; + if (area == d->rightDock) + return d->docks[Qt::DockRight]; + if (area == d->topDock) + return d->docks[Qt::DockTop]; + if (area == d->bottomDock) + return d->docks[Qt::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 Q3MainWindow::setDockEnabled(Q3DockWindow *dw, Qt::Dock dock, bool enable) +{ + Q_D(Q3MainWindow); + if (!d->dockWindows.contains(dw)) { + d->dockWindows.append(dw); + connect(dw, SIGNAL(placeChanged(Q3DockWindow::Place)), + this, SLOT(slotPlaceChanged())); + } + QString s; + s.sprintf("%p_%d", (void*)dw, (int)dock); + if (enable) + d->disabledDocks.removeAll(s); + else if (!d->disabledDocks.contains(s)) + d->disabledDocks << s; + switch (dock) { + case Qt::DockTop: + topDock()->setAcceptDockWindow(dw, enable); + break; + case Qt::DockLeft: + leftDock()->setAcceptDockWindow(dw, enable); + break; + case Qt::DockRight: + rightDock()->setAcceptDockWindow(dw, enable); + break; + case Qt::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 Q3MainWindow::isDockEnabled(Q3DockWindow *dw, Q3DockArea *area) const +{ + Q_D(const Q3MainWindow); + if (!isDockEnabled(area)) + return false; + Qt::Dock dock; + if (area == d->leftDock) + dock = Qt::DockLeft; + else if (area == d->rightDock) + dock = Qt::DockRight; + else if (area == d->topDock) + dock = Qt::DockTop; + else if (area == d->bottomDock) + dock = Qt::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 Q3MainWindow::isDockEnabled(Q3DockWindow *tb, Qt::Dock dock) const +{ + if (!isDockEnabled(dock)) + return false; + QString s; + s.sprintf("%p_%d", (void*)tb, (int)dock); + return !d_func()->disabledDocks.contains(s); +} + + + +/*! + 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 Q3MainWindow::addDockWindow(Q3DockWindow *dockWindow, + Qt::Dock edge, bool newLine) +{ + Q_D(Q3MainWindow); +#ifdef Q_WS_MAC + extern WindowPtr qt_mac_window_for(const QWidget*); //qwidget_mac.cpp + if(isWindow() && edge == Qt::DockTop) { + d->createWinId(); + ChangeWindowAttributes(qt_mac_window_for(this), kWindowToolbarButtonAttribute, 0); + } +#endif + moveDockWindow(dockWindow, edge); + dockWindow->setNewLine(newLine); + if (!d->dockWindows.contains(dockWindow)) { + d->dockWindows.append(dockWindow); + connect(dockWindow, SIGNAL(placeChanged(Q3DockWindow::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 Q3MainWindow::addDockWindow(Q3DockWindow * dockWindow, const QString &label, + Qt::Dock edge, bool newLine) +{ + addDockWindow(dockWindow, edge, newLine); +#ifndef QT_NO_TOOLBAR + Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(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 Q3MainWindow::moveDockWindow(Q3DockWindow * dockWindow, Qt::Dock edge) +{ + Q_D(Q3MainWindow); + Qt::Orientation oo = dockWindow->orientation(); + switch (edge) { + case Qt::DockTop: + if (dockWindow->area() != d->topDock) + dockWindow->removeFromDock(false); + d->topDock->moveDockWindow(dockWindow); + emit dockWindowPositionChanged(dockWindow); + break; + case Qt::DockBottom: + if (dockWindow->area() != d->bottomDock) + dockWindow->removeFromDock(false); + d->bottomDock->moveDockWindow(dockWindow); + emit dockWindowPositionChanged(dockWindow); + break; + case Qt::DockRight: + if (dockWindow->area() != d->rightDock) + dockWindow->removeFromDock(false); + d->rightDock->moveDockWindow(dockWindow); + emit dockWindowPositionChanged(dockWindow); + break; + case Qt::DockLeft: + if (dockWindow->area() != d->leftDock) + dockWindow->removeFromDock(false); + d->leftDock->moveDockWindow(dockWindow); + emit dockWindowPositionChanged(dockWindow); + break; + case Qt::DockTornOff: + dockWindow->undock(); + break; + case Qt::DockMinimized: + dockWindow->undock(d->hideDock); + break; + case Qt::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 Q3MainWindow::moveDockWindow(Q3DockWindow * dockWindow, Qt::Dock edge, bool nl, int index, int extraOffset) +{ + Q_D(Q3MainWindow); + Qt::Orientation oo = dockWindow->orientation(); + + dockWindow->setNewLine(nl); + dockWindow->setOffset(extraOffset); + switch (edge) { + case Qt::DockTop: + if (dockWindow->area() != d->topDock) + dockWindow->removeFromDock(false); + d->topDock->moveDockWindow(dockWindow, index); + break; + case Qt::DockBottom: + if (dockWindow->area() != d->bottomDock) + dockWindow->removeFromDock(false); + d->bottomDock->moveDockWindow(dockWindow, index); + break; + case Qt::DockRight: + if (dockWindow->area() != d->rightDock) + dockWindow->removeFromDock(false); + d->rightDock->moveDockWindow(dockWindow, index); + break; + case Qt::DockLeft: + if (dockWindow->area() != d->leftDock) + dockWindow->removeFromDock(false); + d->leftDock->moveDockWindow(dockWindow, index); + break; + case Qt::DockTornOff: + dockWindow->undock(); + break; + case Qt::DockMinimized: + dockWindow->undock(d->hideDock); + break; + case Qt::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 Q3MainWindow::removeDockWindow(Q3DockWindow * dockWindow) +{ + Q_D(Q3MainWindow); + +#ifdef Q_WS_MAC + extern WindowPtr qt_mac_window_for(const QWidget*); //qwidget_mac.cpp + if(isWindow() && dockWindow->area() == topDock() && !dockWindows(Qt::DockTop).count()) + ChangeWindowAttributes(qt_mac_window_for(this), 0, kWindowToolbarButtonAttribute); +#endif + + dockWindow->hide(); + d->dockWindows.removeAll(dockWindow); + disconnect(dockWindow, SIGNAL(placeChanged(Q3DockWindow::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 Q3MainWindow::setUpLayout() +{ + Q_D(Q3MainWindow); +#ifndef QT_NO_MENUBAR + if (!d->mb) { + // slightly evil hack here. reconsider this + QObjectList l = queryList("QMenuBar", 0, false, false); + if (l.size()) + d->mb = menuBar(); + } +#endif + if (!d->sb) { + // as above. + QObjectList l = queryList("QStatusBar", 0, false, false); + if (l.size()) + d->sb = statusBar(); + } + + if (!d->tll) { + d->tll = new QBoxLayout(this, QBoxLayout::Down); + d->tll->setResizeMode(minimumSize().isNull() ? QLayout::Minimum : QLayout::FreeResize); + d->mwl = new Q3MainWindowLayout(this); + } else { + d->tll->setMenuBar(0); + QLayoutItem *item; + while ((item = d->tll->takeAt(0))) { + if (item != d->mwl) + delete item; + } + } + +#ifndef QT_NO_MENUBAR + if (d->mb && d->mb->isVisibleTo(this)) { + d->tll->setMenuBar(d->mb); + if (style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, 0, this)) + d->tll->addSpacing(d->movable ? 1 : 2); + } +#endif + + d->tll->addWidget(d->hideDock); + if(d->topDock->parentWidget() == this) + d->tll->addWidget(d->topDock); + + Q3MainWindowLayout *mwl = d->mwl; + d->tll->addItem(mwl); + 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); + + 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 Q3MainWindow::setVisible(bool visible) +{ + Q_D(Q3MainWindow); + if (visible) { + if (!d->tll) + setUpLayout(); + + // show all floating dock windows not explicitly hidden + if (!isVisible()) { + for (int i = 0; i < d->dockWindows.size(); ++i) { + Q3DockWindow *dw = d->dockWindows.at(i); + if (dw->isWindow() && !dw->isVisible() && !dw->testAttribute(Qt::WA_WState_Hidden)) { + reinterpret_cast<Q3MainWindow *>(dw)->setAttribute(Qt::WA_WState_Hidden); + dw->show(); + } + } + } + } else if (isVisible()) { + for (int i = 0; i < d->dockWindows.size(); ++i) { + Q3DockWindow *dw = d->dockWindows.at(i); + if (dw->isWindow() && dw->isVisible()) { + dw->hide(); // implicit hide, so clear forcehide + reinterpret_cast<Q3MainWindow *>(dw)->setAttribute(Qt::WA_WState_Hidden, false); + } + } + } + QWidget::setVisible(visible); +} + + +/*! \reimp */ +QSize Q3MainWindow::sizeHint() const +{ + Q3MainWindow* that = (Q3MainWindow*) this; + // Workaround: because d->tll get's deleted in + // totalSizeHint->polish->sendPostedEvents->childEvent->triggerLayout + QApplication::sendPostedEvents(that, QEvent::ChildInserted); + if (!that->d_func()->tll) + that->setUpLayout(); + return that->d_func()->tll->totalSizeHint(); +} + +/*! \reimp */ +QSize Q3MainWindow::minimumSizeHint() const +{ + Q_D(const Q3MainWindow); + if (!d->tll) { + Q3MainWindow* that = (Q3MainWindow*) 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 Q3MainWindow::setCentralWidget(QWidget * w) +{ + Q_D(Q3MainWindow); + if (d->mc) + d->mc->removeEventFilter(this); + d->mc = w; + if (d->mc) + d->mc->installEventFilter(this); + triggerLayout(); +} + + +/*! + Returns a pointer to the main window's central widget. + + The central widget is surrounded by the left, top, right and + bottom dock areas. The menu bar is above the top dock area. + + \sa setCentralWidget() +*/ + +QWidget * Q3MainWindow::centralWidget() const +{ + return d_func()->mc; +} + + +/*! \reimp */ + +void Q3MainWindow::paintEvent(QPaintEvent *) +{ + Q_D(Q3MainWindow); + if (d->mb && + style()->styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, 0, this)) { + QPainter p(this); + int y = d->mb->height() + 1; + QStyleOption opt(0, QStyleOption::SO_Default); + opt.rect.setRect(0, y, width(), 1); + opt.palette = palette(); + opt.state = QStyle::State_Sunken; + style()->drawPrimitive(QStyle::PE_Q3Separator, &opt, &p, this); + } +} + + +bool Q3MainWindow::dockMainWindow(QObject *dock) const +{ + while (dock) { + if (dock->parent() && + dock->parent() == const_cast<Q3MainWindow*>(this)) + return true; + if (qobject_cast<Q3MainWindow*>(dock->parent())) + return false; + dock = dock->parent(); + } + return false; +} + +/*! + \reimp +*/ + +bool Q3MainWindow::eventFilter(QObject* o, QEvent *e) +{ + Q_D(Q3MainWindow); + if (e->type() == QEvent::Show && o == this) { + if (!d->tll) + setUpLayout(); + d->tll->activate(); + } else if (e->type() == QEvent::ContextMenu && d->dockMenu && + ((qobject_cast<Q3DockArea*>(o) && dockMainWindow(o)) || o == d->hideDock || o == d->mb)) { + if (showDockMenu(((QMouseEvent*)e)->globalPos())) { + ((QContextMenuEvent*)e)->accept(); + return true; + } + } + + return QWidget::eventFilter(o, e); +} + + +/*! + Monitors events, received in \a e, to ensure the layout is updated. +*/ +void Q3MainWindow::childEvent(QChildEvent* e) +{ + Q_D(Q3MainWindow); + if (e->type() == QEvent::ChildRemoved) { + if (e->child() == 0 || + !e->child()->isWidgetType() || + ((QWidget*)e->child())->isWindow()) { + // 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 (qobject_cast<Q3DockWindow*>(e->child())) { + removeDockWindow((Q3DockWindow *)(e->child())); + d->appropriate.remove((Q3DockWindow*)e->child()); + triggerLayout(); + } + } else if (e->type() == QEvent::ChildInserted && !d->sb) { + d->sb = qobject_cast<QStatusBar*>(e->child()); + if (d->sb) { + if (d->tll) { + if (!d->tll->findWidget(d->sb)) + d->tll->addWidget(d->sb); + } else { + triggerLayout(); + } + } + } +} + +/*! + \reimp +*/ + +bool Q3MainWindow::event(QEvent * e) +{ + Q_D(Q3MainWindow); +#ifndef QT_NO_STATUSTIP + if (e->type() == QEvent::StatusTip) { + if (d->sb) { + d->sb->showMessage(static_cast<QStatusTipEvent*>(e)->tip()); + return true; + } + } +#endif + if (e->type() == QEvent::ToolBarChange) { + // Keep compatibility with the Qt 3 main window, use the real main window + // or reimplement if you want proper handling. + int deltaH = 0; + Q3DockArea *area = topDock(); + if (area->width() >= area->height()) { + deltaH = area->sizeHint().height(); + if (!area->isVisible()) { + area->show(); + } else { + area->hide(); + deltaH = -deltaH; + } + } + + if (deltaH) { + QApplication::sendPostedEvents(this, QEvent::LayoutRequest); + resize(width(), height() + deltaH); + } + return true; + } + if (e->type() == QEvent::ChildRemoved && ((QChildEvent*)e)->child() == d->mc) { + d->mc->removeEventFilter(this); + d->mc = 0; + d->mwl->setCentralWidget(0); + } + + if (e->type() == QEvent::MenubarUpdated) { + QMenubarUpdatedEvent * const event = static_cast<QMenubarUpdatedEvent *>(e); + if (event->menuBar() && event->menuBar()->parent() == this) { + triggerLayout(); + update(); + } + } + return QWidget::event(e); +} + + +/*! + \property Q3MainWindow::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 Q3MainWindow::usesBigPixmaps() const +{ + return d_func()->ubp; +} + +void Q3MainWindow::setUsesBigPixmaps(bool enable) +{ + Q_D(Q3MainWindow); + if (enable == (bool)d->ubp) + return; + + d->ubp = enable; + emit pixmapSizeChanged(enable); + + QObjectList l = queryList("QLayout"); + for (int i = 0; i < l.size(); ++i) + static_cast<QLayout *>(l.at(i))->activate(); +} + +/*! + \property Q3MainWindow::usesTextLabel + \brief whether text labels for toolbar buttons are enabled + + If disabled (the default), the tool buttons will not use text + labels. If enabled, text labels will be used. + + Tool buttons and other widgets that wish to respond to this + setting are responsible for reading the correct state on startup, + and for connecting to the main window's widget's + usesTextLabelChanged() signal. + + \sa QToolButton::setUsesTextLabel() +*/ + +bool Q3MainWindow::usesTextLabel() const +{ + return d_func()->utl; +} + + +void Q3MainWindow::setUsesTextLabel(bool enable) +{ + Q_D(Q3MainWindow); + if (enable == (bool)d->utl) + return; + + d->utl = enable; + emit usesTextLabelChanged(enable); + + QObjectList l = queryList("QLayout"); + for (int i = 0; i < l.size(); ++i) + static_cast<QLayout *>(l.at(i))->activate(); + triggerLayout(false); +} + + +/*! + \fn void Q3MainWindow::pixmapSizeChanged(bool b) + + This signal is emitted whenever the setUsesBigPixmaps() is called + with a value different to the current setting. The new value is + passed in \a b. All widgets that should respond to such changes, + e.g. toolbar buttons, must connect to this signal. +*/ + +/*! + \fn void Q3MainWindow::usesTextLabelChanged(bool b) + + This signal is emitted whenever the setUsesTextLabel() is called + with a value different to the current setting. The new value is + passed in \a b. All widgets that should respond to such changes, + e.g. toolbar buttons, must connect to this signal. +*/ + +/*! + \fn void Q3MainWindow::dockWindowPositionChanged(Q3DockWindow *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 Q3MainWindow::setRightJustification(bool enable) +{ + Q_D(Q3MainWindow); + if (enable == (bool)d->justify) + return; + d->justify = enable; + triggerLayout(true); +} + + +/*! + \property Q3MainWindow::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 Q3DockWindow::setVerticalStretchable(), Q3DockWindow::setHorizontalStretchable() +*/ + +bool Q3MainWindow::rightJustification() const +{ + return d_func()->justify; +} + +/*! \internal + */ + +void Q3MainWindow::triggerLayout(bool deleteLayout) +{ + Q_D(Q3MainWindow); + if (deleteLayout || !d->tll) + setUpLayout(); + QApplication::postEvent(this, new QEvent(QEvent::LayoutHint)); +} + +/*! + Enters 'What's This?' mode and returns immediately. + + This is the same as QWhatsThis::enterWhatsThisMode(), but + implemented as a main window object's slot. This way it can easily + be used for popup menus, for example: + + \snippet doc/src/snippets/code/src_qt3support_widgets_q3mainwindow.cpp 4 + + \sa Q3WhatsThis::enterWhatsThisMode() +*/ +void Q3MainWindow::whatsThis() +{ +#ifndef QT_NO_WHATSTHIS + QWhatsThis::enterWhatsThisMode(); +#endif +} + +/*! + 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<<(). +*/ + +bool Q3MainWindow::getLocation(Q3DockWindow *dw, Qt::Dock &dock, int &index, bool &nl, int &extraOffset) const +{ + Q_D(const Q3MainWindow); + dock = Qt::DockTornOff; + if (d->topDock->hasDockWindow(dw, &index)) + dock = Qt::DockTop; + else if (d->bottomDock->hasDockWindow(dw, &index)) + dock = Qt::DockBottom; + else if (d->leftDock->hasDockWindow(dw, &index)) + dock = Qt::DockLeft; + else if (d->rightDock->hasDockWindow(dw, &index)) + dock = Qt::DockRight; + else if (dw->parentWidget() == d->hideDock) { + index = 0; + dock = Qt::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() +*/ + +QList<Q3ToolBar *> Q3MainWindow::toolBars(Qt::Dock dock) const +{ + QList<Q3DockWindow *> lst = dockWindows(dock); + QList<Q3ToolBar *> tbl; + for (int i = 0; i < lst.size(); ++i) { + Q3ToolBar *tb = qobject_cast<Q3ToolBar *>(lst.at(i)); + 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 Qt::DockTornOff dock area may contain closed dock + windows but these are returned along with the visible dock + windows. +*/ + +QList<Q3DockWindow *> Q3MainWindow::dockWindows(Qt::Dock dock) const +{ + Q_D(const Q3MainWindow); + QList<Q3DockWindow *> lst; + switch (dock) { + case Qt::DockTop: + return d->topDock->dockWindowList(); + case Qt::DockBottom: + return d->bottomDock->dockWindowList(); + case Qt::DockLeft: + return d->leftDock->dockWindowList(); + case Qt::DockRight: + return d->rightDock->dockWindowList(); + case Qt::DockTornOff: { + for (int i = 0; i < d->dockWindows.size(); ++i) { + Q3DockWindow *w = d->dockWindows.at(i); + if (!w->area() && w->place() == Q3DockWindow::OutsideDock) + lst.append(w); + } + } + return lst; + case Qt::DockMinimized: { + QObjectList childList = d->hideDock->children(); + for (int i = 0; i < childList.size(); ++i) { + Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(childList.at(i)); + if (dw) + 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). +*/ + +QList<Q3DockWindow *> Q3MainWindow::dockWindows() const +{ + return d_func()->dockWindows; +} + +void Q3MainWindow::setDockWindowsMovable(bool enable) +{ + Q_D(Q3MainWindow); + d->movable = enable; + QObjectList l = queryList("Q3DockWindow"); + for (int i = 0; i < l.size(); ++i) + static_cast<Q3DockWindow*>(l.at(i))->setMovingEnabled(enable); +} + +/*! + \property Q3MainWindow::dockWindowsMovable + \brief whether the dock windows are movable + + If true (the default), the user will be able to move movable dock + windows from one Q3MainWindow 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). Movable + dock windows can also be moved within Q3MainWindow 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 Q3MainWindow::dockWindowsMovable() const +{ + return d_func()->movable; +} + +void Q3MainWindow::setOpaqueMoving(bool b) +{ + Q_D(Q3MainWindow); + d->opaque = b; + QObjectList l = queryList("Q3DockWindow"); + for (int i = 0; i < l.size(); ++i) + static_cast<Q3DockWindow*>(l.at(i))->setOpaqueMoving(b); +} + +/*! + \property Q3MainWindow::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 Q3MainWindow::opaqueMoving() const +{ + return d_func()->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 Q3MainWindow::lineUpDockWindows(bool keepNewLines) +{ + Q_D(const Q3MainWindow); + 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 Q3MainWindow::isDockMenuEnabled() const +{ + return d_func()->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 Q3MainWindow::setDockMenuEnabled(bool b) +{ + d_func()->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). You can reimplement this function if you wish to + customize the behavior. + + 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. +*/ + +Q3PopupMenu *Q3MainWindow::createDockWindowMenu(DockWindows dockWindows) const +{ + Q_D(const Q3MainWindow); + QObjectList l = queryList("Q3DockWindow"); + if (l.isEmpty()) + return 0; + + Q3PopupMenu *menu = new Q3PopupMenu((Q3MainWindow*)this); + menu->setObjectName(QLatin1String("qt_customize_menu")); + d->dockWindowModes.replace( menu, dockWindows ); + menu->setCheckable(true); + 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. +*/ + +void Q3MainWindow::menuAboutToShow() +{ + Q_D(Q3MainWindow); + Q3PopupMenu *menu = (Q3PopupMenu*)sender(); + menu->clear(); + + DockWindows dockWindows; + { + QMap<Q3PopupMenu*, DockWindows>::Iterator it = d->dockWindowModes.find( menu ); + if ( it == d->dockWindowModes.end() ) + return; + dockWindows = (*it); + } + + QObjectList l = queryList("Q3DockWindow"); + bool empty = true; + if (!l.isEmpty()) { + if (dockWindows == AllDockWindows || dockWindows == NoToolBars) { + for (int i = 0; i < l.size(); ++i) { + Q3DockWindow *dw = (Q3DockWindow*)l.at(i); + if (!appropriate(dw) || qobject_cast<Q3ToolBar*>(dw) || !dockMainWindow(dw)) + continue; + QString label = dw->windowTitle(); + if (!label.isEmpty()) { + QAction *act = menu->addAction(label); + act->setCheckable(true); + act->setChecked(dw->isVisible()); + QObject::connect(act, SIGNAL(triggered()), dw, SLOT(toggleVisible())); + empty = false; + } + } + } + if (!empty) { + menu->addSeparator(); + empty = true; + } +#ifndef QT_NO_TOOLBAR + if (dockWindows == AllDockWindows || dockWindows == OnlyToolBars) { + for (int i = 0; i < l.size(); ++i) { + Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(l.at(i)); + if (!tb || !appropriate(tb) || !dockMainWindow(tb)) + continue; + QString label = tb->label(); + if (!label.isEmpty()) { + QAction *act = menu->addAction(label); + act->setCheckable(true); + act->setChecked(tb->isVisible()); + QObject::connect(act, SIGNAL(triggered()), tb, SLOT(toggleVisible())); + empty = false; + } + } + } +#endif + } + if (!empty) { + menu->addSeparator(); + empty = true; + } + + if (dockWindowsMovable()) + menu->addAction(tr("Line up"), this, SLOT(doLineUp())); + if (isCustomizable()) + menu->addAction(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. + + 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. +*/ + +bool Q3MainWindow::showDockMenu(const QPoint &globalPos) +{ + Q_D(Q3MainWindow); + if (!d->dockMenu) + return false; + + if(Q3PopupMenu *ret = createDockWindowMenu()) { + ret->exec(globalPos); + delete ret; + return true; + } + return false; +} + +void Q3MainWindow::slotPlaceChanged() +{ + QObject* obj = (QObject*)sender(); + Q3DockWindow *dw = qobject_cast<Q3DockWindow*>(obj); + if (dw) + emit dockWindowPositionChanged(dw); +#ifndef QT_NO_TOOLBAR + Q3ToolBar *tb = qobject_cast<Q3ToolBar*>(obj); + if (tb) + emit toolBarPositionChanged(tb); +#endif +} + +/*! + \internal + For internal use of Q3DockWindow only. + */ + +Q3DockArea *Q3MainWindow::dockingArea(const QPoint &p) +{ + Q_D(Q3MainWindow); + 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 Q3MainWindow::hasDockWindow(Q3DockWindow *dw) +{ + return d_func()->dockWindows.contains(dw); +} + +/*! + Returns the \c Left dock area + + \sa rightDock() topDock() bottomDock() +*/ + +Q3DockArea *Q3MainWindow::leftDock() const +{ + return d_func()->leftDock; +} + +/*! + Returns the \c Right dock area + + \sa leftDock() topDock() bottomDock() +*/ + +Q3DockArea *Q3MainWindow::rightDock() const +{ + return d_func()->rightDock; +} + +/*! + Returns the \c Top dock area + + \sa bottomDock() leftDock() rightDock() +*/ + +Q3DockArea *Q3MainWindow::topDock() const +{ + return d_func()->topDock; +} + +/*! + Returns a pointer the \c Bottom dock area + + \sa topDock() leftDock() rightDock() +*/ + +Q3DockArea *Q3MainWindow::bottomDock() const +{ + return d_func()->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 Q3MainWindow::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 Q3MainWindow::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 Q3MainWindow::appropriate(Q3DockWindow *dw) const +{ + Q_D(const Q3MainWindow); + QMap<Q3DockWindow*, 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 Q3MainWindow::setAppropriate(Q3DockWindow *dw, bool a) +{ + d_func()->appropriate.insert(dw, a); +} + +#ifndef QT_NO_TEXTSTREAM +static void saveDockArea(QTextStream &ts, Q3DockArea *a) +{ + QList<Q3DockWindow *> l = a->dockWindowList(); + for (int i = 0; i < l.size(); ++i) { + Q3DockWindow *dw = l.at(i); + ts << QString(dw->windowTitle()); + ts << ","; + } + ts << endl; + ts << *a; +} + +/*! + \relates Q3MainWindow + + Writes the layout (sizes and positions) of the dock windows in the + dock areas of the Q3MainWindow \a mainWindow, including \c + Minimized and \c TornOff dock windows, to the text stream \a ts. + + This can be used, for example, in conjunction with QSettings to + save the user's layout when the \a mainWindow receives a + close event. + + \sa QWidget::closeEvent() +*/ + +QTextStream &operator<<(QTextStream &ts, const Q3MainWindow &mainWindow) +{ + QList<Q3DockWindow *> l = mainWindow.dockWindows(Qt::DockMinimized); + for (int i = 0; i < l.size(); ++i) { + Q3DockWindow *dw = l.at(i); + ts << dw->windowTitle(); + ts << ","; + } + ts << endl; + + l = mainWindow.dockWindows(Qt::DockTornOff); + for (int i = 0; i < l.size(); ++i) { + Q3DockWindow *dw = l.at(i); + ts << dw->windowTitle(); + ts << ","; + } + ts << endl; + for (int i = 0; i < l.size(); ++i) { + Q3DockWindow *dw = l.at(i); + ts << "[" << dw->windowTitle() << "," + << (int)dw->geometry().x() << "," + << (int)dw->geometry().y() << "," + << (int)dw->geometry().width() << "," + << (int)dw->geometry().height() << "," + << (int)dw->isVisible() << "]"; + } + ts << endl; + + saveDockArea(ts, mainWindow.topDock()); + saveDockArea(ts, mainWindow.bottomDock()); + saveDockArea(ts, mainWindow.rightDock()); + saveDockArea(ts, mainWindow.leftDock()); + return ts; +} + +static void loadDockArea(const QStringList &names, Q3DockArea *a, Qt::Dock dl, QList<Q3DockWindow *> &l, Q3MainWindow *mw, QTextStream &ts) +{ + for (QStringList::ConstIterator it = names.begin(); it != names.end(); ++it) { + for (int i = 0; i < l.size(); ++i) { + Q3DockWindow *dw = l.at(i); + if (dw->windowTitle() == *it) { + mw->addDockWindow(dw, dl); + break; + } + } + } + if (a) { + ts >> *a; + } else if (dl == Qt::DockTornOff) { + QString s = ts.readLine(); + enum State { Pre, Name, X, Y, Width, Height, Visible, Post }; + int state = Pre; + QString name, x, y, w, h, visible; + QChar c; + for (int i = 0; i < (int)s.length(); ++i) { + c = s[i]; + if (state == Pre && c == QLatin1Char('[')) { + state++; + continue; + } + if (c == QLatin1Char(',') && + (state == Name || state == X || state == Y || state == Width || state == Height)) { + state++; + continue; + } + if (state == Visible && c == QLatin1Char(']')) { + for (int i = 0; i < l.size(); ++i) { + Q3DockWindow *dw = l.at(i); + if (QString(dw->windowTitle()) == name) { + if (!qobject_cast<Q3ToolBar*>(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 = QLatin1String(""); + + 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 Q3MainWindow + + Reads the layout (sizes and positions) of the dock windows in the + dock areas of the Q3MainWindow \a mainWindow from the text stream, + \a ts, including \c Minimized and \c TornOff dock windows. + Restores the dock windows and dock areas to these sizes and + positions. The layout information must be in the format produced + by operator<<(). + + This can be used, for example, in conjunction with QSettings to + restore the user's layout. +*/ + +QTextStream &operator>>(QTextStream &ts, Q3MainWindow &mainWindow) +{ + QList<Q3DockWindow *> l = mainWindow.dockWindows(); + + QString s = ts.readLine(); + QStringList names = s.split(QLatin1Char(',')); + loadDockArea(names, 0, Qt::DockMinimized, l, &mainWindow, ts); + + s = ts.readLine(); + names = s.split(QLatin1Char(',')); + loadDockArea(names, 0, Qt::DockTornOff, l, &mainWindow, ts); + + int i = 0; + Q3DockArea *areas[] = { mainWindow.topDock(), mainWindow.bottomDock(), mainWindow.rightDock(), mainWindow.leftDock() }; + for (int dl = (int)Qt::DockTop; dl != (int)Qt::DockMinimized; ++dl, ++i) { + s = ts.readLine(); + names = s.split(QLatin1Char(',')); + loadDockArea(names, areas[i], (Qt::Dock)dl, l, &mainWindow, ts); + } + return ts; +} +#endif + +QT_END_NAMESPACE + +#include "q3mainwindow.moc" + +#endif diff --git a/src/qt3support/widgets/q3mainwindow.h b/src/qt3support/widgets/q3mainwindow.h new file mode 100644 index 0000000..d3a582d --- /dev/null +++ b/src/qt3support/widgets/q3mainwindow.h @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3MAINWINDOW_H +#define Q3MAINWINDOW_H + +#include <QtGui/qwidget.h> +#include <Qt3Support/q3toolbar.h> +#include <QtCore/qtextstream.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_MAINWINDOW + +class QMenuBar; +class QStatusBar; +class QToolTipGroup; +class Q3MainWindowPrivate; +class Q3MainWindowLayout; +class Q3PopupMenu; +class QHideDock; +template<class T> class QList; + +class Q_COMPAT_EXPORT Q3MainWindow: public QWidget +{ + Q_OBJECT + Q_DECLARE_PRIVATE(Q3MainWindow) + + 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: + Q3MainWindow(QWidget* parent=0, const char* name=0, Qt::WindowFlags f = Qt::WType_TopLevel); + ~Q3MainWindow(); + +#ifndef QT_NO_MENUBAR + QMenuBar * menuBar() const; +#endif + QStatusBar * statusBar() const; +#if 0 + QToolTipGroup * toolTipGroup() const; +#endif + + virtual void setCentralWidget(QWidget *); + QWidget * centralWidget() const; + + virtual void setDockEnabled(Qt::Dock dock, bool enable); + bool isDockEnabled(Qt::Dock dock) const; + bool isDockEnabled(Q3DockArea *area) const; + virtual void setDockEnabled(Q3DockWindow *tb, Qt::Dock dock, bool enable); + bool isDockEnabled(Q3DockWindow *tb, Qt::Dock dock) const; + bool isDockEnabled(Q3DockWindow *tb, Q3DockArea *area) const; + + virtual void addDockWindow(Q3DockWindow *, Qt::Dock = Qt::DockTop, bool newLine = false); + virtual void addDockWindow(Q3DockWindow *, const QString &label, + Qt::Dock = Qt::DockTop, bool newLine = false); + virtual void moveDockWindow(Q3DockWindow *, Qt::Dock = Qt::DockTop); + virtual void moveDockWindow(Q3DockWindow *, Qt::Dock, bool nl, int index, int extraOffset = -1); + virtual void removeDockWindow(Q3DockWindow *); + + void setVisible(bool); + QSize sizeHint() const; + QSize minimumSizeHint() const; + + bool rightJustification() const; + bool usesBigPixmaps() const; + bool usesTextLabel() const; + bool dockWindowsMovable() const; + bool opaqueMoving() const; + + bool eventFilter(QObject*, QEvent*); + + bool getLocation(Q3DockWindow *tb, Qt::Dock &dock, int &index, bool &nl, int &extraOffset) const; + + QList<Q3DockWindow *> dockWindows(Qt::Dock dock) const; + QList<Q3DockWindow *> dockWindows() const; + void lineUpDockWindows(bool keepNewLines = false); + + bool isDockMenuEnabled() const; + + // compatibility stuff + bool hasDockWindow(Q3DockWindow *dw); +#ifndef QT_NO_TOOLBAR + void addToolBar(Q3DockWindow *, Qt::Dock = Qt::DockTop, bool newLine = false); + void addToolBar(Q3DockWindow *, const QString &label, + Qt::Dock = Qt::DockTop, bool newLine = false); + void moveToolBar(Q3DockWindow *, Qt::Dock = Qt::DockTop); + void moveToolBar(Q3DockWindow *, Qt::Dock, bool nl, int index, int extraOffset = -1); + void removeToolBar(Q3DockWindow *); + + bool toolBarsMovable() const; + QList<Q3ToolBar *> toolBars(Qt::Dock dock) const; + void lineUpToolBars(bool keepNewLines = false); +#endif + + virtual Q3DockArea *dockingArea(const QPoint &p); + Q3DockArea *leftDock() const; + Q3DockArea *rightDock() const; + Q3DockArea *topDock() const; + Q3DockArea *bottomDock() const; + + virtual bool isCustomizable() const; + + bool appropriate(Q3DockWindow *dw) const; + + enum DockWindows { OnlyToolBars, NoToolBars, AllDockWindows }; + virtual Q3PopupMenu *createDockWindowMenu(DockWindows dockWindows = AllDockWindows) const; + +public Q_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(Q3DockWindow *dw, bool a); + virtual void customize(); + + // compatibility stuff + void setToolBarsMovable(bool); + +Q_SIGNALS: + void pixmapSizeChanged(bool); + void usesTextLabelChanged(bool); + void dockWindowPositionChanged(Q3DockWindow *); + +#ifndef QT_NO_TOOLBAR + // compatibility stuff + void toolBarPositionChanged(Q3ToolBar *); +#endif + +protected Q_SLOTS: + virtual void setUpLayout(); + virtual bool showDockMenu(const QPoint &globalPos); + void menuAboutToShow(); + +protected: + void paintEvent(QPaintEvent *); + void childEvent(QChildEvent *); + bool event(QEvent *); + +private Q_SLOTS: + void slotPlaceChanged(); + void doLineUp() { lineUpDockWindows(true); } + +private: + Q_DISABLE_COPY(Q3MainWindow) + + void triggerLayout(bool deleteLayout = true); + bool dockMainWindow(QObject *dock) const; + +#ifndef QT_NO_MENUBAR + virtual void setMenuBar(QMenuBar *); +#endif + virtual void setStatusBar(QStatusBar *); +#if 0 + virtual void setToolTipGroup(QToolTipGroup *); +#endif + + friend class Q3DockWindow; + friend class QMenuBarPrivate; + friend class QHideDock; + friend class Q3ToolBar; + friend class Q3MainWindowLayout; +}; + +#ifndef QT_NO_TOOLBAR +inline void Q3MainWindow::addToolBar(Q3DockWindow *w, Qt::ToolBarDock dock, bool newLine) +{ + addDockWindow(w, dock, newLine); +} + +inline void Q3MainWindow::addToolBar(Q3DockWindow *w, const QString &label, + Qt::ToolBarDock dock, bool newLine) +{ + addDockWindow(w, label, dock, newLine); +} + +inline void Q3MainWindow::moveToolBar(Q3DockWindow *w, Qt::ToolBarDock dock) +{ + moveDockWindow(w, dock); +} + +inline void Q3MainWindow::moveToolBar(Q3DockWindow *w, Qt::ToolBarDock dock, bool nl, int index, int extraOffset) +{ + moveDockWindow(w, dock, nl, index, extraOffset); +} + +inline void Q3MainWindow::removeToolBar(Q3DockWindow *w) +{ + removeDockWindow(w); +} + +inline bool Q3MainWindow::toolBarsMovable() const +{ + return dockWindowsMovable(); +} + +inline void Q3MainWindow::lineUpToolBars(bool keepNewLines) +{ + lineUpDockWindows(keepNewLines); +} + +inline void Q3MainWindow::setToolBarsMovable(bool b) +{ + setDockWindowsMovable(b); +} +#endif + +#ifndef QT_NO_TEXTSTREAM +Q_COMPAT_EXPORT QTextStream &operator<<(QTextStream &, const Q3MainWindow &); +Q_COMPAT_EXPORT QTextStream &operator>>(QTextStream &, Q3MainWindow &); +#endif + +#endif // QT_NO_MAINWINDOW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3MAINWINDOW_H diff --git a/src/qt3support/widgets/q3mainwindow_p.h b/src/qt3support/widgets/q3mainwindow_p.h new file mode 100644 index 0000000..31bea4f --- /dev/null +++ b/src/qt3support/widgets/q3mainwindow_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3MAINWINDOW_P_H +#define Q3MAINWINDOW_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qwidget_p.h> + +QT_BEGIN_NAMESPACE + +class Q3MainWindowLayout; + +class Q3MainWindowPrivate : public QWidgetPrivate +{ + Q_DECLARE_PUBLIC(Q3MainWindow) +public: + Q3MainWindowPrivate() + : mb(0), sb(0), ttg(0), mc(0), tll(0), mwl(0), ubp(false), utl(false), + justify(false), movable(true), opaque(false), dockMenu(true) + { + docks.insert(Qt::DockTop, true); + docks.insert(Qt::DockBottom, true); + docks.insert(Qt::DockLeft, true); + docks.insert(Qt::DockRight, true); + docks.insert(Qt::DockMinimized, false); + docks.insert(Qt::DockTornOff, true); + } + + ~Q3MainWindowPrivate() + { + } + +#ifndef QT_NO_MENUBAR + mutable QMenuBar * mb; +#else + mutable QWidget * mb; +#endif + QStatusBar * sb; + QToolTipGroup * ttg; + + QWidget * mc; + + QBoxLayout * tll; + Q3MainWindowLayout * mwl; + + uint ubp :1; + uint utl :1; + uint justify :1; + uint movable :1; + uint opaque :1; + uint dockMenu :1; + + Q3DockArea *topDock, *bottomDock, *leftDock, *rightDock; + + QList<Q3DockWindow *> dockWindows; + QMap<Qt::Dock, bool> docks; + QStringList disabledDocks; + QHideDock *hideDock; + + QPointer<Q3PopupMenu> rmbMenu, tbMenu, dwMenu; + QMap<Q3DockWindow*, bool> appropriate; + mutable QMap<Q3PopupMenu*, Q3MainWindow::DockWindows> dockWindowModes; +}; + +QT_END_NAMESPACE + +#endif // Q3MAINWINDOW_P_H diff --git a/src/qt3support/widgets/q3popupmenu.cpp b/src/qt3support/widgets/q3popupmenu.cpp new file mode 100644 index 0000000..7f890b5 --- /dev/null +++ b/src/qt3support/widgets/q3popupmenu.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "q3popupmenu.h" + +QT_BEGIN_NAMESPACE + +/*! + \fn void Q3PopupMenu::setFrameRect(QRect) + \internal +*/ + +/*! + \fn QRect Q3PopupMenu::frameRect() const + \internal +*/ +/*! + \enum Q3PopupMenu::DummyFrame + \internal + + \value Box + \value Sunken + \value Plain + \value Raised + \value MShadow + \value NoFrame + \value Panel + \value StyledPanel + \value HLine + \value VLine + \value GroupBoxPanel + \value WinPanel + \value ToolBarPanel + \value MenuBarPanel + \value PopupPanel + \value LineEditPanel + \value TabWidgetPanel + \value MShape +*/ + +/*! + \fn void Q3PopupMenu::setFrameShadow(DummyFrame) + \internal +*/ + +/*! + \fn DummyFrame Q3PopupMenu::frameShadow() const + \internal +*/ + +/*! + \fn void Q3PopupMenu::setFrameShape(DummyFrame) + \internal +*/ + +/*! + \fn DummyFrame Q3PopupMenu::frameShape() const + \internal +*/ + +/*! + \fn void Q3PopupMenu::setFrameStyle(int) + \internal +*/ + +/*! + \fn int Q3PopupMenu::frameStyle() const + \internal +*/ + +/*! + \fn int Q3PopupMenu::frameWidth() const + \internal +*/ + +/*! + \fn void Q3PopupMenu::setLineWidth(int) + \internal +*/ + +/*! + \fn int Q3PopupMenu::lineWidth() const + \internal +*/ + +/*! + \fn void Q3PopupMenu::setMargin(int margin) + \since 4.2 + + Sets the width of the margin around the contents of the widget to \a margin. + + This function uses QWidget::setContentsMargins() to set the margin. + \sa margin(), QWidget::setContentsMargins() +*/ + +/*! + \fn int Q3PopupMenu::margin() const + \since 4.2 + + Returns the with of the the margin around the contents of the widget. + + This function uses QWidget::getContentsMargins() to get the margin. + \sa setMargin(), QWidget::getContentsMargins() +*/ + +/*! + \fn void Q3PopupMenu::setMidLineWidth(int) + \internal +*/ + +/*! + \fn int Q3PopupMenu::midLineWidth() const + \internal +*/ + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3popupmenu.h b/src/qt3support/widgets/q3popupmenu.h new file mode 100644 index 0000000..baf5494 --- /dev/null +++ b/src/qt3support/widgets/q3popupmenu.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3POPUPMENU_H +#define Q3POPUPMENU_H + +#include <QtGui/qmenu.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class Q_COMPAT_EXPORT Q3PopupMenu : public QMenu +{ + Q_OBJECT +public: + inline Q3PopupMenu(QWidget *parent = 0, const char * =0) : QMenu(parent) + { } + + inline int exec() { return findIdForAction(QMenu::exec()); } + inline int exec(const QPoint & pos, int indexAtPoint = 0) { + return findIdForAction(QMenu::exec(pos, actions().value(indexAtPoint))); + } + + void setFrameRect(QRect) {} + QRect frameRect() const { return QRect(); } + enum DummyFrame { Box, Sunken, Plain, Raised, MShadow, NoFrame, Panel, StyledPanel, + HLine, VLine, GroupBoxPanel, WinPanel, ToolBarPanel, MenuBarPanel, + PopupPanel, LineEditPanel, TabWidgetPanel, MShape }; + void setFrameShadow(DummyFrame) {} + DummyFrame frameShadow() const { return Plain; } + void setFrameShape(DummyFrame) {} + DummyFrame frameShape() const { return NoFrame; } + void setFrameStyle(int) {} + int frameStyle() const { return 0; } + int frameWidth() const { return 0; } + void setLineWidth(int) {} + int lineWidth() const { return 0; } + void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); } + int margin() const + { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; } + void setMidLineWidth(int) {} + int midLineWidth() const { return 0; } + +private: + Q_DISABLE_COPY(Q3PopupMenu) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPOPUPMENU_H diff --git a/src/qt3support/widgets/q3progressbar.cpp b/src/qt3support/widgets/q3progressbar.cpp new file mode 100644 index 0000000..caae460 --- /dev/null +++ b/src/qt3support/widgets/q3progressbar.cpp @@ -0,0 +1,464 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3progressbar.h" +#ifndef QT_NO_PROGRESSBAR +#include "qpainter.h" +#include "qdrawutil.h" +#include "qpixmap.h" +#include "qstyle.h" +#include "qstyleoption.h" +#ifndef QT_NO_ACCESSIBILITY +#include "qaccessible.h" +#endif +#include "qevent.h" +#include <limits.h> + +QT_BEGIN_NAMESPACE + +/*! + \class Q3ProgressBar + \brief The Q3ProgressBar widget provides a horizontal progress bar. + + \compat + + A progress bar is used to give the user an indication of the + progress of an operation and to reassure them that the application + is still running. + + The progress bar uses the concept of \e steps; you give it the + total number of steps and the number of steps completed so far and + it will display the percentage of steps that have been completed. + You can specify the total number of steps in the constructor or + later with setTotalSteps(). The current number of steps is set + with setProgress(). The progress bar can be rewound to the + beginning with reset(). + + If the total is given as 0 the progress bar shows a busy indicator + instead of a percentage of steps. This is useful, for example, + when using QFtp or QHttp to download items when they are unable to + determine the size of the item being downloaded. + + \sa QProgressDialog + + \inlineimage qprogbar-m.png Screenshot in Motif style + \inlineimage qprogbar-w.png Screenshot in Windows style + + \sa QProgressDialog +*/ + + +/*! \obsolete + Constructs a progress bar. + + The total number of steps is set to 100 by default. + + The \a parent, \a name and widget flags, \a f, are passed on to + the QFrame::QFrame() constructor. + + \sa setTotalSteps() +*/ + +Q3ProgressBar::Q3ProgressBar(QWidget *parent, const char *name, Qt::WindowFlags f) + : QFrame(parent, f), + total_steps(100), + progress_val(-1), + percentage(-1), + center_indicator(true), + percentage_visible(true), + d(0) +{ + setObjectName(QLatin1String(name)); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + initFrame(); +} + + +/*! \obsolete + Constructs a progress bar. + + The \a totalSteps is the total number of steps that need to be + completed for the operation which this progress bar represents. + For example, if the operation is to examine 50 files, this value + would be 50. Before examining the first file, call setProgress(0); + call setProgress(50) after examining the last file. + + The \a parent, \a name and widget flags, \a f, are passed to the + QFrame::QFrame() constructor. + + \sa setTotalSteps(), setProgress() +*/ + +Q3ProgressBar::Q3ProgressBar(int totalSteps, QWidget *parent, const char *name, Qt::WindowFlags f) + : QFrame(parent, f), + total_steps(totalSteps), + progress_val(-1), + percentage(-1), + center_indicator(true), + percentage_visible(true), + d(0) +{ + setObjectName(QLatin1String(name)); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + initFrame(); +} +#endif + +/*! + Constructs a progress bar. + + The total number of steps is set to 100 by default. + + The \a parent, and widget flags, \a f, are passed on to + the QFrame::QFrame() constructor. + + \sa setTotalSteps() +*/ + +Q3ProgressBar::Q3ProgressBar(QWidget *parent, Qt::WindowFlags f) + : QFrame(parent, f), + total_steps(100), + progress_val(-1), + percentage(-1), + center_indicator(true), + percentage_visible(true), + d(0) +{ + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + initFrame(); +} + + +/*! + Constructs a progress bar. + + The \a totalSteps is the total number of steps that need to be + completed for the operation which this progress bar represents. + For example, if the operation is to examine 50 files, this value + would be 50. Before examining the first file, call setProgress(0); + call setProgress(50) after examining the last file. + + The \a parent, and widget flags, \a f, are passed to the + QFrame::QFrame() constructor. + + \sa setTotalSteps(), setProgress() +*/ + +Q3ProgressBar::Q3ProgressBar(int totalSteps, QWidget *parent, Qt::WindowFlags f) + : QFrame(parent, f), + total_steps(totalSteps), + progress_val(-1), + percentage(-1), + center_indicator(true), + percentage_visible(true), + d(0) +{ + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + initFrame(); +} + + +/*! + Reset the progress bar. The progress bar "rewinds" and shows no + progress. +*/ + +void Q3ProgressBar::reset() +{ + progress_val = -1; + percentage = -1; + setIndicator(progress_str, progress_val, total_steps); + repaint(); +} + + +/*! + \property Q3ProgressBar::totalSteps + \brief The total number of steps. + + If totalSteps is 0, the progress bar will display a busy + indicator. +*/ + +void Q3ProgressBar::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(); +} + + +/*! + \property Q3ProgressBar::progress + \brief The current amount of progress + + This property is -1 if progress counting has not started. +*/ + +void Q3ProgressBar::setProgress(int progress) +{ + if (progress == progress_val || + progress < 0 || ((progress > total_steps) && total_steps)) + return; + + int old_progress_val = progress_val; + progress_val = progress; + + if (setIndicator(progress_str, progress_val, total_steps) + || ( total_steps == 0 || (width() * progress_val / total_steps != width() * old_progress_val / total_steps ))) + repaint(); + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::ValueChanged); +#endif +} + +/*! + \overload + + Sets the amount of progress to \a progress and the total number of + steps to \a totalSteps. + + \sa setTotalSteps() +*/ + +void Q3ProgressBar::setProgress(int progress, int totalSteps) +{ + if (total_steps != totalSteps) + setTotalSteps(totalSteps); + setProgress(progress); +} + +/*! + \property Q3ProgressBar::progressString + \brief the amount of progress as a string + + This property is an empty string if progress counting has not started. +*/ + +static QStyleOptionProgressBar getStyleOption(const Q3ProgressBar *pb) +{ + QStyleOptionProgressBar opt; + opt.init(pb); + opt.rect = pb->contentsRect(); + opt.minimum = 0; + opt.maximum = pb->totalSteps(); + opt.progress = pb->progress(); + if (pb->centerIndicator()) + opt.textAlignment = Qt::AlignCenter; + else + opt.textAlignment = Qt::AlignAuto; + opt.textVisible = pb->percentageVisible(); + opt.text = pb->progressString(); + return opt; +} + +/*! + \reimp +*/ +QSize Q3ProgressBar::sizeHint() const +{ + ensurePolished(); + QFontMetrics fm = fontMetrics(); + QStyleOptionProgressBar opt = getStyleOption(this); + int cw = style()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &opt, this); + return style()->sizeFromContents(QStyle::CT_ProgressBar, &opt, + QSize(cw * 7 + fm.width(QLatin1Char('0')) * 4, fm.height() + 8), this); +} + +/*! + \reimp +*/ +QSize Q3ProgressBar::minimumSizeHint() const +{ + return sizeHint(); +} + +/*! + \property Q3ProgressBar::centerIndicator + \brief whether the indicator string should be centered +*/ + +void Q3ProgressBar::setCenterIndicator(bool on) +{ + if (on == center_indicator) + return; + center_indicator = on; + repaint(); +} + +/*! + \property Q3ProgressBar::percentageVisible + \brief whether the current progress value is displayed + + The default is true. + + \sa centerIndicator +*/ +void Q3ProgressBar::setPercentageVisible(bool on) +{ + if (on == percentage_visible) + return; + percentage_visible = on; + repaint(); +} + +/*! + \reimp +*/ +void Q3ProgressBar::setVisible(bool visible) +{ + if (visible) + setIndicator(progress_str, progress_val, total_steps); + QFrame::setVisible(visible); +} + +void Q3ProgressBar::initFrame() +{ + setFrameStyle(QFrame::NoFrame); +} + +/*! + \reimp +*/ +void Q3ProgressBar::changeEvent(QEvent *ev) +{ + if(ev->type() == QEvent::StyleChange) + initFrame(); + QFrame::changeEvent(ev); +} + + +/*! + 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 Q3ProgressBar::setIndicator(QString & indicator, int progress, + int totalSteps) +{ + if (!totalSteps) + return false; + if (progress < 0) { + indicator = QString::fromLatin1(""); + return true; + } else { + // Get the values down to something usable. + if (totalSteps > INT_MAX/1000) { + progress /= 1000; + totalSteps /= 1000; + } + + int np = progress * 100 / totalSteps; + if (np != percentage) { + percentage = np; + indicator.sprintf("%d%%", np); + return true; + } else { + return false; + } + } +} + + +/*! + \reimp +*/ +void Q3ProgressBar::paintEvent(QPaintEvent *) +{ + QPainter paint(this); + QPainter *p = &paint; + drawFrame(p); + + QStyleOptionProgressBar opt = getStyleOption(this); + opt.rect = style()->subElementRect(QStyle::SE_ProgressBarGroove, &opt, this); + + style()->drawControl(QStyle::CE_ProgressBarGroove, &opt, p, this); + opt.rect = contentsRect(); + opt.rect = style()->subElementRect(QStyle::SE_ProgressBarContents, &opt, this); + style()->drawControl(QStyle::CE_ProgressBarContents, &opt, p, this); + + if (percentageVisible()) { + opt.rect = contentsRect(); + opt.rect = style()->subElementRect(QStyle::SE_ProgressBarLabel, &opt, this); + style()->drawControl(QStyle::CE_ProgressBarLabel, &opt, p, this); + } +} + +/*! + \fn void Q3ProgressBar::setMargin(int margin) + \since 4.2 + + Sets the width of the margin around the contents of the widget to \a margin. + + This function uses QWidget::setContentsMargins() to set the margin. + \sa margin(), QWidget::setContentsMargins() +*/ + +/*! + \fn int Q3ProgressBar::margin() const + \since 4.2 + + Returns the with of the the margin around the contents of the widget. + + This function uses QWidget::getContentsMargins() to get the margin. + \sa setMargin(), QWidget::getContentsMargins() +*/ + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3progressbar.h b/src/qt3support/widgets/q3progressbar.h new file mode 100644 index 0000000..fdf7d9a --- /dev/null +++ b/src/qt3support/widgets/q3progressbar.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3PROGRESSBAR_H +#define Q3PROGRESSBAR_H + +#include <QtGui/qframe.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_PROGRESSBAR + +class Q3ProgressBarPrivate; + +class Q_COMPAT_EXPORT Q3ProgressBar : public QFrame +{ + Q_OBJECT + Q_PROPERTY(int totalSteps READ totalSteps WRITE setTotalSteps) + Q_PROPERTY(int progress READ progress WRITE setProgress) + Q_PROPERTY(QString progressString READ progressString) + Q_PROPERTY(bool centerIndicator READ centerIndicator WRITE setCenterIndicator) + Q_PROPERTY(bool percentageVisible READ percentageVisible WRITE setPercentageVisible) + +public: + Q3ProgressBar(QWidget *parent, const char *name, Qt::WindowFlags f=0); + Q3ProgressBar(int totalSteps, QWidget *parent, const char *name, + Qt::WindowFlags f=0); + Q3ProgressBar(QWidget *parent = 0, Qt::WindowFlags f = 0); + Q3ProgressBar(int totalSteps, QWidget *parent = 0, Qt::WindowFlags f=0); + + int totalSteps() const; + int progress() const; + const QString &progressString() const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + void setCenterIndicator(bool on); + bool centerIndicator() const; + + bool percentageVisible() const; + void setPercentageVisible(bool); + + void setVisible(bool visible); + + void setMargin(int margin) { setContentsMargins(margin, margin, margin, margin); } + int margin() const + { int margin; int dummy; getContentsMargins(&margin, &dummy, &dummy, &dummy); return margin; } + +public Q_SLOTS: + void reset(); + virtual void setTotalSteps(int totalSteps); + virtual void setProgress(int progress); + void setProgress(int progress, int totalSteps); + +protected: + void paintEvent(QPaintEvent *); + virtual bool setIndicator(QString &progress_str, int progress, int totalSteps); + void changeEvent(QEvent *); + +private: + Q_DISABLE_COPY(Q3ProgressBar) + + int total_steps; + int progress_val; + int percentage; + QString progress_str; + bool center_indicator : 1; + bool percentage_visible : 1; + Q3ProgressBarPrivate *d; + void initFrame(); +}; + + +inline int Q3ProgressBar::totalSteps() const +{ + return total_steps; +} + +inline int Q3ProgressBar::progress() const +{ + return progress_val; +} + +inline const QString &Q3ProgressBar::progressString() const +{ + return progress_str; +} + +inline bool Q3ProgressBar::centerIndicator() const +{ + return center_indicator; +} + +inline bool Q3ProgressBar::percentageVisible() const +{ + return percentage_visible; +} + +#endif // QT_NO_PROGRESSBAR + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3PROGRESSBAR_H diff --git a/src/qt3support/widgets/q3rangecontrol.cpp b/src/qt3support/widgets/q3rangecontrol.cpp new file mode 100644 index 0000000..1373f28 --- /dev/null +++ b/src/qt3support/widgets/q3rangecontrol.cpp @@ -0,0 +1,550 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3rangecontrol.h" +#ifndef QT_NO_RANGECONTROL +#include "qglobal.h" +#include <limits.h> + +QT_BEGIN_NAMESPACE + +/*! + \class Q3RangeControl + \brief The Q3RangeControl class provides an integer value within a range. + + \compat + + Although originally designed for the QScrollBar widget, the + Q3RangeControl can also be used in conjunction with other widgets + such as QSlider and QSpinBox. Here are the five main concepts in + the class: + + \list 1 + + \i \e{Current value} The bounded integer that + Q3RangeControl 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 + Q3RangeControl 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 + Q3RangeControl 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(). + + Q3RangeControl provides three virtual functions that are well + suited for updating the on-screen representation of range controls + and emitting signals: valueChange(), rangeChange() and + stepChange(). + + Q3RangeControl 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 Q3RangeControl 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 Q3RangeControl because Q3RangeControl is + not derived from QWidget. +*/ + + +/*! + Constructs a range control with a minimum value of 0, maximum + value of 99, line step of 1, page step of 10 and initial value 0. +*/ + +Q3RangeControl::Q3RangeControl() +{ + 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()). +*/ + +Q3RangeControl::Q3RangeControl(int minValue, int maxValue, + int lineStep, int pageStep, + int value) +{ + minVal = minValue; + maxVal = maxValue; + line = QABS(lineStep); + page = QABS(pageStep); + prevVal = minVal - 1; + val = bound(value); + d = 0; +} + +/*! + Destroys the range control +*/ + +Q3RangeControl::~Q3RangeControl() +{ +} + + +/*! + \fn int Q3RangeControl::value() const + + Returns the current range control value. This is guaranteed to be + within the range [minValue(), maxValue()]. + + \sa setValue() prevValue() +*/ + +/*! + \fn int Q3RangeControl::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 Q3RangeControl::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 Q3RangeControl::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 Q3RangeControl::addPage() +{ + setValue(value() + pageStep()); +} + +/*! + Equivalent to \c{setValue(value() - pageStep())}. + + If the value is changed, then valueChange() is called. + + \sa addPage() subtractLine() setValue() +*/ + +void Q3RangeControl::subtractPage() +{ + setValue(value() - pageStep()); +} + +/*! + Equivalent to \c{setValue(value() + lineStep())}. + + If the value is changed, then valueChange() is called. + + \sa subtractLine() addPage() setValue() +*/ + +void Q3RangeControl::addLine() +{ + setValue(value() + lineStep()); +} + +/*! + Equivalent to \c{setValue(value() - lineStep())}. + + If the value is changed, then valueChange() is called. + + \sa addLine() subtractPage() setValue() +*/ + +void Q3RangeControl::subtractLine() +{ + setValue(value() - lineStep()); +} + + +/*! + \fn int Q3RangeControl::minValue() const + + Returns the minimum value of the range. + + \sa setMinValue() setRange() maxValue() +*/ + +/*! + \fn int Q3RangeControl::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 Q3RangeControl::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 Q3RangeControl::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 Q3RangeControl::setRange(int minValue, int maxValue) +{ + if (minValue > maxValue) { + qWarning("Q3RangeControl::setRange: minValue %d > maxValue %d", + minValue, maxValue); + 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 Q3RangeControl::lineStep() const + + Returns the line step. + + \sa setSteps() pageStep() +*/ + +/*! + \fn int Q3RangeControl::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 Q3RangeControl::setSteps(int lineStep, int pageStep) +{ + if (lineStep != line || pageStep != page) { + line = QABS(lineStep); + page = QABS(pageStep); + stepChange(); + } +} + + +/*! + This virtual function is called whenever the range control value + changes. You can reimplement it if you want to be notified when + the value changes. The default implementation does nothing. + + Note that this method is called after the value has changed. The + previous value can be retrieved using prevValue(). + + \sa setValue(), addPage(), subtractPage(), addLine(), + subtractLine() rangeChange(), stepChange() +*/ + +void Q3RangeControl::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 Q3RangeControl::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 Q3RangeControl::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 Q3RangeControl itself. + + \sa setValue() value() minValue() maxValue() +*/ + +int Q3RangeControl::bound(int v) const +{ + if (v < minVal) + return minVal; + if (v > maxVal) + return maxVal; + return v; +} + + +/*! + Converts \a logical_val to a pixel position. minValue() maps to 0, + maxValue() maps to \a span and other values are distributed evenly + in-between. + + This function can handle the entire integer range without + overflow, providing \a span is \<= 4096. + + Calling this method is useful when actually drawing a range + control such as a QScrollBar on-screen. + + \sa valueFromPosition() +*/ + +int Q3RangeControl::positionFromValue(int logical_val, int span) const +{ + if (span <= 0 || logical_val < minValue() || maxValue() <= minValue()) + return 0; + if (logical_val > maxValue()) + return span; + + uint range = maxValue() - minValue(); + uint p = logical_val - minValue(); + + if (range > (uint)INT_MAX/4096) { + const int scale = 4096*2; + return ((p/scale) * span) / (range/scale); + // ### the above line is probably not 100% correct + // ### but fixing it isn't worth the extreme pain... + } else if (range > (uint)span) { + return (2*p*span + range) / (2*range); + } else { + uint div = span / range; + uint mod = span % range; + return p*div + (2*p*mod + range) / (2*range); + } + //equiv. to (p*span)/range + 0.5 + // no overflow because of this implicit assumption: + // span <= 4096 +} + + +/*! + Converts the pixel position \a pos to a value. 0 maps to + minValue(), \a span maps to maxValue() and other values are + distributed evenly in-between. + + This function can handle the entire integer range without + overflow. + + Calling this method is useful if you actually implemented a range + control widget such as QScrollBar and want to handle mouse press + events. This function then maps screen coordinates to the logical + values. + + \sa positionFromValue() +*/ + +int Q3RangeControl::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) +} + +QT_END_NAMESPACE + +#endif // QT_NO_RANGECONTROL diff --git a/src/qt3support/widgets/q3rangecontrol.h b/src/qt3support/widgets/q3rangecontrol.h new file mode 100644 index 0000000..651ca7a --- /dev/null +++ b/src/qt3support/widgets/q3rangecontrol.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3RANGECONTROL_H +#define Q3RANGECONTROL_H + +#include <QtCore/qglobal.h> +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_RANGECONTROL + +class Q3RangeControlPrivate; + +class Q_COMPAT_EXPORT Q3RangeControl +{ +public: + Q3RangeControl(); + Q3RangeControl(int minValue, int maxValue, + int lineStep, int pageStep, int value); + virtual ~Q3RangeControl(); + + 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; + + Q3RangeControlPrivate * d; + +private: + Q_DISABLE_COPY(Q3RangeControl) +}; + + +inline int Q3RangeControl::value() const +{ return val; } + +inline int Q3RangeControl::prevValue() const +{ return prevVal; } + +inline int Q3RangeControl::minValue() const +{ return minVal; } + +inline int Q3RangeControl::maxValue() const +{ return maxVal; } + +inline int Q3RangeControl::lineStep() const +{ return line; } + +inline int Q3RangeControl::pageStep() const +{ return page; } + + +#endif // QT_NO_RANGECONTROL + +#ifndef QT_NO_SPINWIDGET + +class Q3SpinWidgetPrivate; +class Q_COMPAT_EXPORT Q3SpinWidget : public QWidget +{ + Q_OBJECT +public: + Q3SpinWidget(QWidget* parent=0, const char* name=0); + ~Q3SpinWidget(); + + void setEditWidget(QWidget * widget); + QWidget * editWidget(); + + QRect upRect() const; + QRect downRect() const; + + void setUpEnabled(bool on); + void setDownEnabled(bool on); + + bool isUpEnabled() const; + bool isDownEnabled() const; + + enum ButtonSymbols { UpDownArrows, PlusMinus }; + virtual void setButtonSymbols(ButtonSymbols bs); + ButtonSymbols buttonSymbols() const; + + void arrange(); + +Q_SIGNALS: + void stepUpPressed(); + void stepDownPressed(); + +public Q_SLOTS: + void stepUp(); + void stepDown(); + +protected: + void mousePressEvent(QMouseEvent *e); + void resizeEvent(QResizeEvent* ev); + void mouseReleaseEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); +#ifndef QT_NO_WHEELEVENT + void wheelEvent(QWheelEvent *); +#endif + void changeEvent(QEvent *); + void paintEvent(QPaintEvent *); + +private Q_SLOTS: + void timerDone(); + void timerDoneEx(); + +private: + Q3SpinWidgetPrivate * d; + + void updateDisplay(); + +private: + Q_DISABLE_COPY(Q3SpinWidget) +}; + +#endif // QT_NO_RANGECONTROL + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3RANGECONTROL_H diff --git a/src/qt3support/widgets/q3scrollview.cpp b/src/qt3support/widgets/q3scrollview.cpp new file mode 100644 index 0000000..91a9203 --- /dev/null +++ b/src/qt3support/widgets/q3scrollview.cpp @@ -0,0 +1,2803 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwidget.h" +#ifndef QT_NO_SCROLLVIEW +#include "qscrollbar.h" +#include "qpainter.h" +#include "qpixmap.h" +#include "qcursor.h" +#include "q3scrollview.h" +#include "q3ptrdict.h" +#include "qapplication.h" +#include "qtimer.h" +#include "qstyle.h" +#include "q3ptrlist.h" +#include "qevent.h" +#include "q3listview.h" +#ifdef Q_WS_MAC +# include "private/qt_mac_p.h" +#endif + +QT_BEGIN_NAMESPACE + +using namespace Qt; + +static const int coord_limit = 4000; +static const int autoscroll_margin = 16; +static const int initialScrollTime = 30; +static const int initialScrollAccel = 5; + +struct QSVChildRec { + QSVChildRec(QWidget* c, int xx, int yy) : + child(c), + x(xx), y(yy) + { + } + + void hideOrShow(Q3ScrollView* sv, QWidget* clipped_viewport); + void moveTo(Q3ScrollView* sv, int xx, int yy, QWidget* clipped_viewport) + { + if (x != xx || y != yy) { + x = xx; + y = yy; + hideOrShow(sv,clipped_viewport); + } + } + QWidget* child; + int x, y; +}; + +void QSVChildRec::hideOrShow(Q3ScrollView* sv, QWidget* clipped_viewport) +{ + if (clipped_viewport) { + if (x+child->width() < sv->contentsX()+clipped_viewport->x() + || x > sv->contentsX()+clipped_viewport->width() + || y+child->height() < sv->contentsY()+clipped_viewport->y() + || y > sv->contentsY()+clipped_viewport->height()) { + child->move(clipped_viewport->width(), + clipped_viewport->height()); + } else { + child->move(x-sv->contentsX()-clipped_viewport->x(), + y-sv->contentsY()-clipped_viewport->y()); + } + } else { + child->move(x-sv->contentsX(), y-sv->contentsY()); + } +} + +class QAbstractScrollAreaWidget : public QWidget +{ + Q_OBJECT + +public: + QAbstractScrollAreaWidget(Q3ScrollView* parent=0, const char* name=0, Qt::WindowFlags f = 0) + : QWidget(parent, name, f) + { + setAutoFillBackground(true); + } +}; + +class QClipperWidget : public QWidget +{ + Q_OBJECT + +public: + QClipperWidget(QWidget * parent=0, const char * name=0, Qt::WindowFlags f=0) + : QWidget (parent,name,f) {} +}; + +QT_BEGIN_INCLUDE_NAMESPACE +#include "q3scrollview.moc" +QT_END_INCLUDE_NAMESPACE + +class Q3ScrollViewData { +public: + Q3ScrollViewData(Q3ScrollView* parent, int vpwflags) : + hbar(new QScrollBar(Qt::Horizontal, parent, "qt_hbar")), + vbar(new QScrollBar(Qt::Vertical, parent, "qt_vbar")), + viewport(new QAbstractScrollAreaWidget(parent, "qt_viewport", QFlag(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(); + vMode = Q3ScrollView::Auto; + hMode = Q3ScrollView::Auto; + corner = 0; + vbar->setSteps(20, 1/*set later*/); + hbar->setSteps(20, 1/*set later*/); + policy = Q3ScrollView::Default; + signal_choke = false; + static_bg = false; + fake_scroll = false; + hbarPressed = false; + vbarPressed = false; + hbar->setLayoutDirection(Qt::LeftToRight); + } + ~Q3ScrollViewData(); + + QSVChildRec* rec(QWidget* w) { return childDict.find(w); } + QSVChildRec* ancestorRec(QWidget* w); + QSVChildRec* addChildRec(QWidget* w, int x, int y) + { + QSVChildRec *r = new QSVChildRec(w,x,y); + children.append(r); + childDict.insert(w, r); + return r; + } + void deleteChildRec(QSVChildRec* r) + { + childDict.remove(r->child); + children.removeRef(r); + delete r; + } + + void hideOrShowAll(Q3ScrollView* sv, bool isScroll = false); + void moveAllBy(int dx, int dy); + bool anyVisibleChildren(); + void autoMove(Q3ScrollView* sv); + void autoResize(Q3ScrollView* sv); + void autoResizeHint(Q3ScrollView* sv); + void viewportResized(int w, int h); + + QScrollBar* hbar; + QScrollBar* vbar; + bool hbarPressed; + bool vbarPressed; + QAbstractScrollAreaWidget* viewport; + QClipperWidget* clipped_viewport; + int flags; + Q3PtrList<QSVChildRec> children; + Q3PtrDict<QSVChildRec> childDict; + QWidget* corner; + int vx, vy, vwidth, vheight; // for drawContents-style usage + int l_marg, r_marg, t_marg, b_marg; + Q3ScrollView::ResizePolicy policy; + Q3ScrollView::ScrollBarMode vMode; + Q3ScrollView::ScrollBarMode hMode; +#ifndef QT_NO_DRAGANDDROP + QPoint cpDragStart; + QTimer autoscroll_timer; + int autoscroll_time; + int autoscroll_accel; + bool drag_autoscroll; +#endif + QTimer scrollbar_timer; + + uint static_bg : 1; + uint fake_scroll : 1; + + // This variable allows ensureVisible to move the contents then + // update both the sliders. Otherwise, updating the sliders would + // cause two image scrolls, creating ugly flashing. + // + uint signal_choke : 1; + + // This variables indicates in updateScrollBars() that we are + // in a resizeEvent() and thus don't want to flash scroll bars + uint inresize : 1; + uint use_cached_size_hint : 1; + QSize cachedSizeHint; + + inline int contentsX() const { return -vx; } + inline int contentsY() const { return -vy; } + inline int contentsWidth() const { return vwidth; } +}; + +inline Q3ScrollViewData::~Q3ScrollViewData() +{ + children.setAutoDelete(true); +} + +QSVChildRec* Q3ScrollViewData::ancestorRec(QWidget* w) +{ + if (clipped_viewport) { + while (w->parentWidget() != clipped_viewport) { + w = w->parentWidget(); + if (!w) return 0; + } + } else { + while (w->parentWidget() != viewport) { + w = w->parentWidget(); + if (!w) return 0; + } + } + return rec(w); +} + +void Q3ScrollViewData::hideOrShowAll(Q3ScrollView* 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->testAttribute(Qt::WA_StaticContents)) || static_bg) + clipped_viewport->update(); + } else { + // Re-center + int nx = (viewport->width() - clipped_viewport->width()) / 2; + int ny = (viewport->height() - clipped_viewport->height()) / 2; + clipped_viewport->move(nx,ny); + clipped_viewport->update(); + } + for (QSVChildRec *r = children.first(); r; r=children.next()) { + r->hideOrShow(sv, clipped_viewport); + } +} + +void Q3ScrollViewData::moveAllBy(int dx, int dy) +{ + if (clipped_viewport && !static_bg) { + clipped_viewport->move(clipped_viewport->x()+dx, + clipped_viewport->y()+dy); + } else { + for (QSVChildRec *r = children.first(); r; r=children.next()) { + r->child->move(r->child->x()+dx,r->child->y()+dy); + } + if (static_bg) + viewport->repaint(true); + } +} + +bool Q3ScrollViewData::anyVisibleChildren() +{ + for (QSVChildRec *r = children.first(); r; r=children.next()) { + if (r->child->isVisible()) return true; + } + return false; +} + +void Q3ScrollViewData::autoMove(Q3ScrollView* sv) +{ + if (policy == Q3ScrollView::AutoOne) { + QSVChildRec* r = children.first(); + if (r) + sv->setContentsPos(-r->child->x(),-r->child->y()); + } +} + +void Q3ScrollViewData::autoResize(Q3ScrollView* sv) +{ + if (policy == Q3ScrollView::AutoOne) { + QSVChildRec* r = children.first(); + if (r) + sv->resizeContents(r->child->width(),r->child->height()); + } +} + +void Q3ScrollViewData::autoResizeHint(Q3ScrollView* sv) +{ + if (policy == Q3ScrollView::AutoOne) { + QSVChildRec* r = children.first(); + if (r) { + QSize s = r->child->sizeHint(); + if (s.isValid()) + r->child->resize(s); + } + } else if (policy == Q3ScrollView::AutoOneFit) { + QSVChildRec* r = children.first(); + if (r) { + QSize sh = r->child->sizeHint(); + sh = sh.boundedTo(r->child->maximumSize()); + sv->resizeContents(sh.width(), sh.height()); + } + } +} + +void Q3ScrollViewData::viewportResized(int w, int h) +{ + if (policy == Q3ScrollView::AutoOneFit) { + QSVChildRec* r = children.first(); + if (r) { + QSize sh = r->child->sizeHint(); + sh = sh.boundedTo(r->child->maximumSize()); + r->child->resize(QMAX(w,sh.width()), QMAX(h,sh.height())); + } + + } +} + + +/*! + \class Q3ScrollView + \brief The Q3ScrollView widget provides a scrolling area with on-demand scroll bars. + + \compat + + The Q3ScrollView is a large canvas - potentially larger than the + coordinate system normally supported by the underlying window + system. This is important because it is quite easy to go beyond + these limitations (e.g. many web pages are more than 32000 pixels + high). Additionally, the Q3ScrollView can have QWidgets positioned + on it that scroll around with the drawn content. These sub-widgets + can also have positions outside the normal coordinate range (but + they are still limited in size). + + To provide content for the widget, inherit from Q3ScrollView, + 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 Q3ScrollView 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 Q3ScrollView (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 Q3ScrollView. The child should be a child of the + viewport() of the scrollview and be added with addChild(): + \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 0 + You can go on to add arbitrary child widgets to the single child + in the scrollview as you would with any widget: + \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 1 + + Here the Q3ScrollView has four children: the viewport(), the + verticalScrollBar(), the horizontalScrollBar() and a small + cornerWidget(). The viewport() has one child: the QWidget. The + QWidget has the three QLabel objects as child widgets. When the view + is scrolled, the QWidget 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 Q3ScrollView (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): + \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 2 + Here, the Q3ScrollView has the same four children: the viewport(), + the verticalScrollBar(), the horizontalScrollBar() and a small + cornerWidget(). The viewport() has the three QLabel objects as + child widgets. When the view is scrolled, the scrollview moves the + child widgets individually. + + \section1 Using a Very Big View with Many Widgets + + \img qscrollview-cl.png + + The final usage of Q3ScrollView (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(): + \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 3 + + Here, the Q3ScrollView 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 Q3ScrollView itself. So to turn mouse + tracking on, for example, use viewport()->setMouseTracking(true). + + To enable drag-and-drop, you would setAcceptDrops(true) on the + Q3ScrollView (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 + Q3ScrollView 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 Q3ScrollView 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 Q3ScrollView, some of the window flags apply + to the viewport() instead of being sent to the QWidget constructor + for the Q3ScrollView. + + \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 scroll bars with setCornerWidget(). You can get access + to the scroll bars 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 Q3ScrollView currently does not erase the background when + resized, i.e. you must always clear the background manually in + scrollview subclasses. This will change in a future version of Qt + and we recommend specifying the \c WNoAutoErase flag explicitly. +*/ + + +/*! + \enum Q3ScrollView::ResizePolicy + + This enum type is used to control a Q3ScrollView's reaction to + resize events. + + \value Default the Q3ScrollView selects one of the other settings + automatically when it has to. In this version of Qt, Q3ScrollView + 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 Q3ScrollView 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. +*/ + +Q3ScrollView::Q3ScrollView(QWidget *parent, const char *name, Qt::WindowFlags f) : + Q3Frame(parent, name, f & (~WStaticContents) & (~WNoAutoErase) & (~WResizeNoErase)) +{ + WindowFlags flags = WResizeNoErase | (f&WPaintClever) | (f&WRepaintNoErase) | (f&WStaticContents); + d = new Q3ScrollViewData(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(Q3Frame::StyledPanel | Q3Frame::Sunken); + setLineWidth(style()->pixelMetric(QStyle::PM_DefaultFrameWidth)); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); +} + + +/*! + Destroys the Q3ScrollView. Any children added with addChild() will + be deleted. +*/ +Q3ScrollView::~Q3ScrollView() +{ + // Be careful not to get all those useless events... + if (d->clipped_viewport) + d->clipped_viewport->removeEventFilter(this); + else + d->viewport->removeEventFilter(this); + + // order is important + // ~QWidget may cause a WM_ERASEBKGND on Windows + delete d->vbar; + d->vbar = 0; + delete d->hbar; + d->hbar = 0; + delete d->viewport; + d->viewport = 0; + delete d; + d = 0; +} + +/*! + \fn void Q3ScrollView::horizontalSliderPressed() + + This signal is emitted whenever the user presses the horizontal slider. +*/ +/*! + \fn void Q3ScrollView::horizontalSliderReleased() + + This signal is emitted whenever the user releases the horizontal slider. +*/ +/*! + \fn void Q3ScrollView::verticalSliderPressed() + + This signal is emitted whenever the user presses the vertical slider. +*/ +/*! + \fn void Q3ScrollView::verticalSliderReleased() + + This signal is emitted whenever the user releases the vertical slider. +*/ +void Q3ScrollView::hbarIsPressed() +{ + d->hbarPressed = true; + emit(horizontalSliderPressed()); +} + +void Q3ScrollView::hbarIsReleased() +{ + d->hbarPressed = false; + emit(horizontalSliderReleased()); +} + +/*! + Returns true if horizontal slider is pressed by user; otherwise returns false. +*/ +bool Q3ScrollView::isHorizontalSliderPressed() +{ + return d->hbarPressed; +} + +void Q3ScrollView::vbarIsPressed() +{ + d->vbarPressed = true; + emit(verticalSliderPressed()); +} + +void Q3ScrollView::vbarIsReleased() +{ + d->vbarPressed = false; + emit(verticalSliderReleased()); +} + +/*! + Returns true if vertical slider is pressed by user; otherwise returns false. +*/ +bool Q3ScrollView::isVerticalSliderPressed() +{ + return d->vbarPressed; +} + +/*! + \reimp +*/ +void Q3ScrollView::styleChange(QStyle& old) +{ + QWidget::styleChange(old); + updateScrollBars(); + d->cachedSizeHint = QSize(); +} + +/*! + \reimp +*/ +void Q3ScrollView::fontChange(const QFont &old) +{ + QWidget::fontChange(old); + updateScrollBars(); + d->cachedSizeHint = QSize(); +} + +void Q3ScrollView::hslide(int pos) +{ + if (!d->signal_choke) { + moveContents(-pos, -d->contentsY()); + QApplication::syncX(); + } +} + +void Q3ScrollView::vslide(int pos) +{ + if (!d->signal_choke) { + moveContents(-d->contentsX(), -pos); + QApplication::syncX(); + } +} + +/*! + Called when the horizontal scroll bar geometry changes. This is + provided as a protected function so that subclasses can do + interesting things such as providing extra buttons in some of the + space normally used by the scroll bars. + + The default implementation simply gives all the space to \a hbar. + The new geometry is given by \a x, \a y, \a w and \a h. + + \sa setVBarGeometry() +*/ +void Q3ScrollView::setHBarGeometry(QScrollBar& hbar, + int x, int y, int w, int h) +{ + hbar.setGeometry(x, y, w, h); +} + +/*! + Called when the vertical scroll bar geometry changes. This is + provided as a protected function so that subclasses can do + interesting things such as providing extra buttons in some of the + space normally used by the scroll bars. + + The default implementation simply gives all the space to \a vbar. + The new geometry is given by \a x, \a y, \a w and \a h. + + \sa setHBarGeometry() +*/ +void Q3ScrollView::setVBarGeometry(QScrollBar& vbar, + int x, int y, int w, int h) +{ + vbar.setGeometry(x, y, w, h); +} + + +/*! + Returns the viewport size for size (\a x, \a y). + + The viewport size depends on (\a x, \a y) (the size of the contents), + the size of this widget and the modes of the horizontal and + vertical scroll bars. + + This function permits widgets that can trade vertical and + horizontal space for each other to control scroll bar appearance + better. For example, a word processor or web browser can control + the width of the right margin accurately, whether or not there + needs to be a vertical scroll bar. +*/ + +QSize Q3ScrollView::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 scroll bar? + needh = w-lmarg-rmarg < x; + needv = h-tmarg-bmarg < y; + + // Do we intend to show the scroll bar? + 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 scroll bar 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 { + // Scroll bars not needed, only show scroll bar that are always on. + showh = d->hMode == AlwaysOn; + showv = d->vMode == AlwaysOn; + } + + return QSize(w-lmarg-rmarg - (showv ? vsbExt : 0), + h-tmarg-bmarg - (showh ? hsbExt : 0)); +} + + +/*! + Updates scroll bars: all possibilities are considered. You should + never need to call this in your code. +*/ +void Q3ScrollView::updateScrollBars() +{ + if(!horizontalScrollBar() && !verticalScrollBar()) + return; + + // I support this should use viewportSize()... but it needs + // so many of the temporary variables from viewportSize. hm. + int fw = frameWidth(); + int lmarg = fw+d->l_marg; + int rmarg = fw+d->r_marg; + int tmarg = fw+d->t_marg; + int bmarg = fw+d->b_marg; + + int w = width(); + int h = height(); + + int portw, porth; + + bool needh; + bool needv; + bool showh; + bool showv; + bool showc = false; + + int hsbExt = horizontalScrollBar()->sizeHint().height(); + int vsbExt = verticalScrollBar()->sizeHint().width(); + + QSize oldVisibleSize(visibleWidth(), visibleHeight()); + + if (d->policy != AutoOne || d->anyVisibleChildren()) { + // Do we definitely need the scroll bar? + needh = w-lmarg-rmarg < d->contentsWidth(); + if (d->inresize) + needh = !horizontalScrollBar()->isHidden(); + needv = h-tmarg-bmarg < contentsHeight(); + + // Do we intend to show the scroll bar? + if (d->hMode == AlwaysOn) + showh = true; + else if (d->hMode == AlwaysOff) + showh = false; + else + showh = needh; + + if (d->vMode == AlwaysOn) + showv = true; + else if (d->vMode == AlwaysOff) + showv = false; + else + showv = needv; + +#ifdef Q_WS_MAC + bool mac_need_scroll = false; + if(!parentWidget()) { + mac_need_scroll = true; + } else { + QWidget *tlw = window(); + QPoint tlw_br = QPoint(tlw->width(), tlw->height()), + my_br = qt_mac_posInWindow(this) + QPoint(w, h); + if(my_br.x() >= tlw_br.x() - 3 && my_br.y() >= tlw_br.y() - 3) + mac_need_scroll = true; + } + if(mac_need_scroll) { + WindowAttributes attr; + GetWindowAttributes((WindowPtr)handle(), &attr); + mac_need_scroll = (attr & kWindowResizableAttribute); + } + if(mac_need_scroll) { + showc = true; + if(d->vMode == Auto) + showv = true; + if(d->hMode == Auto) + showh = true; + } +#endif + + // Given other scroll bar 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 scroll bar 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 scroll bar, 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 scroll bars that we will show + if (needv) { + d->vbar->setRange(0, contentsHeight()-porth); + d->vbar->setSteps(Q3ScrollView::d->vbar->lineStep(), porth); + } else { + d->vbar->setRange(0, 0); + } + if (needh) { + d->hbar->setRange(0, QMAX(0, d->contentsWidth()-portw)); + d->hbar->setSteps(Q3ScrollView::d->hbar->lineStep(), portw); + } else { + d->hbar->setRange(0, 0); + } + + // Position the scroll bars, viewport and corner widget. + int bottom; + bool reverse = QApplication::reverseLayout(); + int xoffset = (reverse && (showv || cornerWidget())) ? vsbExt : 0; + int xpos = reverse ? 0 : w - vsbExt; + bool frameContentsOnly = + style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents); + + if(! frameContentsOnly) { + if (reverse) + xpos += fw; + else + xpos -= fw; + } + if (showh) { + int right = (showc || showv || cornerWidget()) ? w-vsbExt : w; + if (! frameContentsOnly) + setHBarGeometry(*d->hbar, fw + xoffset, h-hsbExt-fw, + right-fw-fw, hsbExt); + else + setHBarGeometry(*d->hbar, 0 + xoffset, h-hsbExt, right, + hsbExt); + bottom=h-hsbExt; + } else { + bottom=h; + } + if (showv) { + clipper()->setGeometry(lmarg + xoffset, tmarg, + w-vsbExt-lmarg-rmarg, + bottom-tmarg-bmarg); + d->viewportResized(w-vsbExt-lmarg-rmarg, bottom-tmarg-bmarg); + if (! frameContentsOnly) + changeFrameRect(QRect(0, 0, w, h)); + else + changeFrameRect(QRect(xoffset, 0, w-vsbExt, bottom)); + if (showc || cornerWidget()) { + if (! frameContentsOnly) + setVBarGeometry(*d->vbar, xpos, + fw, vsbExt, + h-hsbExt-fw-fw); + else + setVBarGeometry(*d->vbar, xpos, 0, + vsbExt, + h-hsbExt); + } + else { + if (! frameContentsOnly) + setVBarGeometry(*d->vbar, xpos, + fw, vsbExt, + bottom-fw-fw); + else + setVBarGeometry(*d->vbar, xpos, 0, + vsbExt, bottom); + } + } else { + if (! frameContentsOnly) + changeFrameRect(QRect(0, 0, w, h)); + else + changeFrameRect(QRect(0, 0, w, bottom)); + clipper()->setGeometry(lmarg, tmarg, + w-lmarg-rmarg, bottom-tmarg-bmarg); + d->viewportResized(w-lmarg-rmarg, bottom-tmarg-bmarg); + } + + QWidget *corner = d->corner; + if (d->corner) { + if (! frameContentsOnly) + corner->setGeometry(xpos, + h-hsbExt-fw, + vsbExt, + hsbExt); + else + corner->setGeometry(xpos, + h-hsbExt, + vsbExt, + hsbExt); + } + + d->signal_choke=sc; + + if (d->contentsX()+visibleWidth() > d->contentsWidth()) { + int x; +#if 0 + if (reverse) + x =QMIN(0,d->contentsWidth()-visibleWidth()); + else +#endif + x =QMAX(0,d->contentsWidth()-visibleWidth()); + d->hbar->setValue(x); + // Do it even if it is recursive + moveContents(-x, -d->contentsY()); + } + if (d->contentsY()+visibleHeight() > contentsHeight()) { + int y=QMAX(0,contentsHeight()-visibleHeight()); + d->vbar->setValue(y); + // Do it even if it is recursive + moveContents(-d->contentsX(), -y); + } + + // Finally, show the scroll bars + if (showh && (d->hbar->isHidden() || !d->hbar->isVisible())) + d->hbar->show(); + if (showv && (d->vbar->isHidden() || !d->vbar->isVisible())) + d->vbar->show(); + + d->signal_choke=true; + d->vbar->setValue(d->contentsY()); + d->hbar->setValue(d->contentsX()); + d->signal_choke=false; + + QSize newVisibleSize(visibleWidth(), visibleHeight()); + if (d->clipped_viewport && oldVisibleSize != newVisibleSize) { + QResizeEvent e(newVisibleSize, oldVisibleSize); + viewportResizeEvent(&e); + } +} + + +/*! + \reimp +*/ +void Q3ScrollView::setVisible(bool visible) +{ + if (visible && !isVisible()) { + QWidget::setVisible(visible); + updateScrollBars(); + d->hideOrShowAll(this); + } else { + QWidget::setVisible(visible); + } +} + +/*! + \internal + */ +void Q3ScrollView::resize(int w, int h) +{ + QWidget::resize(w, h); +} + +/*! + \internal +*/ +void Q3ScrollView::resize(const QSize& s) +{ + resize(s.width(), s.height()); +} + +/*! + \reimp +*/ +void Q3ScrollView::resizeEvent(QResizeEvent* event) +{ + Q3Frame::resizeEvent(event); + +#if 0 + if (QApplication::reverseLayout()) { + d->fake_scroll = true; + scrollBy(-event->size().width() + event->oldSize().width(), 0); + d->fake_scroll = false; + } +#endif + + bool inresize = d->inresize; + d->inresize = true; + updateScrollBars(); + d->inresize = inresize; + d->scrollbar_timer.start(0, true); + + d->hideOrShowAll(this); +} + + + +/*! + \reimp +*/ +void Q3ScrollView::mousePressEvent(QMouseEvent * e) +{ + e->ignore(); +} + +/*! + \reimp +*/ +void Q3ScrollView::mouseReleaseEvent(QMouseEvent *e) +{ + e->ignore(); +} + + +/*! + \reimp +*/ +void Q3ScrollView::mouseDoubleClickEvent(QMouseEvent *e) +{ + e->ignore(); +} + +/*! + \reimp +*/ +void Q3ScrollView::mouseMoveEvent(QMouseEvent *e) +{ + e->ignore(); +} + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void Q3ScrollView::wheelEvent(QWheelEvent *e) +{ + QWheelEvent ce(viewport()->mapFromGlobal(e->globalPos()), + e->globalPos(), e->delta(), e->state()); + viewportWheelEvent(&ce); + if (!ce.isAccepted()) { + if (e->orientation() == Horizontal && horizontalScrollBar()) + horizontalScrollBar()->event(e); + else if (e->orientation() == Vertical && verticalScrollBar()) + verticalScrollBar()->event(e); + } else { + e->accept(); + } +} +#endif + +/*! + \reimp +*/ +void Q3ScrollView::contextMenuEvent(QContextMenuEvent *e) +{ + if (e->reason() != QContextMenuEvent::Keyboard) { + e->ignore(); + return; + } + + QContextMenuEvent ce(e->reason(), viewport()->mapFromGlobal(e->globalPos()), + e->globalPos(), e->state()); + viewportContextMenuEvent(&ce); + if (ce.isAccepted()) + e->accept(); + else + e->ignore(); +} + +Q3ScrollView::ScrollBarMode Q3ScrollView::vScrollBarMode() const +{ + return d->vMode; +} + + +/*! + \enum Q3ScrollView::ScrollBarMode + + This enum type describes the various modes of Q3ScrollView's scroll + bars. + + \value Auto Q3ScrollView shows a scroll bar when the content is + too large to fit and not otherwise. This is the default. + + \value AlwaysOff Q3ScrollView never shows a scroll bar. + + \value AlwaysOn Q3ScrollView always shows a scroll bar. + + (The modes for the horizontal and vertical scroll bars are + independent.) +*/ + + +/*! + \property Q3ScrollView::vScrollBarMode + \brief the mode for the vertical scroll bar + + The default mode is Q3ScrollView::Auto. + + \sa hScrollBarMode +*/ +void Q3ScrollView::setVScrollBarMode(ScrollBarMode mode) +{ + if (d->vMode != mode) { + d->vMode = mode; + updateScrollBars(); + } +} + + +/*! + \property Q3ScrollView::hScrollBarMode + \brief the mode for the horizontal scroll bar + + The default mode is Q3ScrollView::Auto. + + \sa vScrollBarMode +*/ +Q3ScrollView::ScrollBarMode Q3ScrollView::hScrollBarMode() const +{ + return d->hMode; +} + +void Q3ScrollView::setHScrollBarMode(ScrollBarMode mode) +{ + if (d->hMode != mode) { + d->hMode = mode; + updateScrollBars(); + } +} + + +/*! + Returns the widget in the corner between the two scroll bars. + + By default, no corner widget is present. +*/ +QWidget* Q3ScrollView::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 Q3ScrollView 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 Q3ScrollView::setCornerWidget(QWidget* corner) +{ + QWidget* oldcorner = d->corner; + if (oldcorner != corner) { + if (oldcorner) oldcorner->hide(); + d->corner = corner; + if (corner) corner->setParent(this); + updateScrollBars(); + if (corner) corner->show(); + } +} + + +void Q3ScrollView::setResizePolicy(ResizePolicy r) +{ + d->policy = r; +} + +/*! + \property Q3ScrollView::resizePolicy + \brief the resize policy + + The default is \c Default. + + \sa ResizePolicy +*/ +Q3ScrollView::ResizePolicy Q3ScrollView::resizePolicy() const +{ + return d->policy; +} + +/*! + \internal +*/ +void Q3ScrollView::setEnabled(bool enable) +{ + Q3Frame::setEnabled(enable); +} + +/*! + Removes the \a child widget from the scrolled area. Note that this + happens automatically if the \a child is deleted. +*/ +void Q3ScrollView::removeChild(QWidget* child) +{ + if (!d || !child) // First check in case we are destructing + return; + + QSVChildRec *r = d->rec(child); + if (r) d->deleteChildRec(r); +} + +/*! + \internal +*/ +void Q3ScrollView::removeChild(QObject* child) +{ + Q3Frame::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 Q3ScrollView::addChild(QWidget* child, int x, int y) +{ + if (!child) { +#if defined(QT_CHECK_NULL) + qWarning("Q3ScrollView::addChild(): Cannot add null child"); +#endif + return; + } + child->polish(); + child->setBackgroundOrigin(WidgetOrigin); + + if (child->parentWidget() == viewport()) { + // May already be there + QSVChildRec *r = d->rec(child); + if (r) { + r->moveTo(this,x,y,d->clipped_viewport); + if (d->policy > Manual) { + d->autoResizeHint(this); + d->autoResize(this); // #### better to just deal with this one widget! + } + return; + } + } + + if (d->children.isEmpty() && d->policy != Manual) { + if (d->policy == Default) + setResizePolicy(AutoOne); + child->installEventFilter(this); + } else if (d->policy == AutoOne) { + child->removeEventFilter(this); //#### ????? + setResizePolicy(Manual); + } + if (child->parentWidget() != viewport()) { + child->reparent(viewport(), 0, QPoint(0,0), false); + } + d->addChildRec(child,x,y)->hideOrShow(this, d->clipped_viewport); + + if (d->policy > Manual) { + d->autoResizeHint(this); + d->autoResize(this); // #### better to just deal with this one widget! + } +} + +/*! + Repositions the \a child widget to (\a x, \a y). This function is + the same as addChild(). +*/ +void Q3ScrollView::moveChild(QWidget* child, int x, int y) +{ + addChild(child,x,y); +} + +/*! + Returns the X position of the given \a child widget. Use this + rather than QWidget::x() for widgets added to the view. + + This function returns 0 if \a child has not been added to the view. +*/ +int Q3ScrollView::childX(QWidget* child) +{ + QSVChildRec *r = d->rec(child); + return r ? r->x : 0; +} + +/*! + Returns the Y position of the given \a child widget. Use this + rather than QWidget::y() for widgets added to the view. + + This function returns 0 if \a child has not been added to the view. +*/ +int Q3ScrollView::childY(QWidget* child) +{ + QSVChildRec *r = d->rec(child); + return r ? r->y : 0; +} + +/*! \fn bool Q3ScrollView::childIsVisible(QWidget*) + \obsolete + + Returns true if \a child is visible. This is equivalent + to child->isVisible(). +*/ + +/*! \fn void Q3ScrollView::showChild(QWidget* child, bool y) + \obsolete + + Sets the visibility of \a child. Equivalent to + QWidget::show() or QWidget::hide(). +*/ + +/*! + This event filter ensures the scroll bars are updated when a + single contents widget is resized, shown, hidden or destroyed; it + passes mouse events to the Q3ScrollView. The event is in \a e and + the object is in \a obj. +*/ + +bool Q3ScrollView::eventFilter(QObject *obj, QEvent *e) +{ + bool disabled = !(qobject_cast<QWidget*>(obj)->isEnabled()); + if (!d) + return false; // we are destructing + if (obj == d->viewport || obj == d->clipped_viewport) { + switch (e->type()) { + /* Forward many events to viewport...() functions */ + case QEvent::Paint: + viewportPaintEvent((QPaintEvent*)e); + break; + case QEvent::Resize: + if (!d->clipped_viewport) + viewportResizeEvent((QResizeEvent *)e); + break; + case QEvent::MouseButtonPress: + if (disabled) + return false; + viewportMousePressEvent((QMouseEvent*)e); + if (((QMouseEvent*)e)->isAccepted()) + return true; + break; + case QEvent::MouseButtonRelease: + if (disabled) + return false; + viewportMouseReleaseEvent((QMouseEvent*)e); + if (((QMouseEvent*)e)->isAccepted()) + return true; + break; + case QEvent::MouseButtonDblClick: + if (disabled) + return false; + viewportMouseDoubleClickEvent((QMouseEvent*)e); + if (((QMouseEvent*)e)->isAccepted()) + return true; + break; + case QEvent::MouseMove: + if (disabled) + return false; + viewportMouseMoveEvent((QMouseEvent*)e); + if (((QMouseEvent*)e)->isAccepted()) + return true; + break; +#ifndef QT_NO_DRAGANDDROP + case QEvent::DragEnter: + if (disabled) + return false; + viewportDragEnterEvent((QDragEnterEvent*)e); + break; + case QEvent::DragMove: { + if (disabled) + return false; + if (d->drag_autoscroll) { + QPoint vp = ((QDragMoveEvent*) e)->pos(); + QRect inside_margin(autoscroll_margin, autoscroll_margin, + visibleWidth() - autoscroll_margin * 2, + visibleHeight() - autoscroll_margin * 2); + if (!inside_margin.contains(vp)) { + startDragAutoScroll(); + // Keep sending move events + ((QDragMoveEvent*)e)->accept(QRect(0,0,0,0)); + } + } + viewportDragMoveEvent((QDragMoveEvent*)e); + } break; + case QEvent::DragLeave: + if (disabled) + return false; + stopDragAutoScroll(); + viewportDragLeaveEvent((QDragLeaveEvent*)e); + break; + case QEvent::Drop: + if (disabled) + return false; + stopDragAutoScroll(); + viewportDropEvent((QDropEvent*)e); + break; +#endif // QT_NO_DRAGANDDROP +#ifndef QT_NO_WHEELEVENT + case QEvent::Wheel: + if (disabled) + return false; + break; +#endif + case QEvent::ContextMenu: + if (disabled) + return false; + viewportContextMenuEvent((QContextMenuEvent*)e); + if (((QContextMenuEvent*)e)->isAccepted()) + return true; + break; + case QEvent::ChildRemoved: + removeChild((QWidget*)((QChildEvent*)e)->child()); + break; + case QEvent::LayoutHint: + d->autoResizeHint(this); + break; + default: + break; + } + } else if (d && d->rec((QWidget*)obj)) { // must be a child + if (e->type() == QEvent::Resize) + d->autoResize(this); + else if (e->type() == QEvent::Move) + d->autoMove(this); + } + return Q3Frame::eventFilter(obj, e); // always continue with standard event processing +} + +/*! + This event handler is called whenever the Q3ScrollView receives a + mousePressEvent(): the press position in \a e is translated to be a point + on the contents. +*/ +void Q3ScrollView::contentsMousePressEvent(QMouseEvent* e) +{ + e->ignore(); +} + +/*! + This event handler is called whenever the Q3ScrollView receives a + mouseReleaseEvent(): the release position in \a e is translated to be a + point on the contents. +*/ +void Q3ScrollView::contentsMouseReleaseEvent(QMouseEvent* e) +{ + e->ignore(); +} + +/*! + This event handler is called whenever the Q3ScrollView 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 Q3ScrollView::contentsMouseDoubleClickEvent(QMouseEvent* e) +{ + contentsMousePressEvent(e); // try mouse press event +} + +/*! + This event handler is called whenever the Q3ScrollView receives a + mouseMoveEvent(): the mouse position in \a e is translated to be a point + on the contents. +*/ +void Q3ScrollView::contentsMouseMoveEvent(QMouseEvent* e) +{ + e->ignore(); +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + This event handler is called whenever the Q3ScrollView receives a + dragEnterEvent(): the drag position is translated to be a point + on the contents. + + The default implementation does nothing. The \a event parameter is + ignored. +*/ +void Q3ScrollView::contentsDragEnterEvent(QDragEnterEvent * /* event */) +{ +} + +/*! + This event handler is called whenever the Q3ScrollView receives a + dragMoveEvent(): the drag position is translated to be a point on + the contents. + + The default implementation does nothing. The \a event parameter is + ignored. +*/ +void Q3ScrollView::contentsDragMoveEvent(QDragMoveEvent * /* event */) +{ +} + +/*! + This event handler is called whenever the Q3ScrollView receives a + dragLeaveEvent(): the drag position is translated to be a point + on the contents. + + The default implementation does nothing. The \a event parameter is + ignored. +*/ +void Q3ScrollView::contentsDragLeaveEvent(QDragLeaveEvent * /* event */) +{ +} + +/*! + This event handler is called whenever the Q3ScrollView receives a + dropEvent(): the drop position is translated to be a point on the + contents. + + The default implementation does nothing. The \a event parameter is + ignored. +*/ + +void Q3ScrollView::contentsDropEvent(QDropEvent * /* event */) +{ +} + +#endif // QT_NO_DRAGANDDROP + +/*! + This event handler is called whenever the Q3ScrollView receives a + wheelEvent() in \a{e}: the mouse position is translated to be a + point on the contents. +*/ +#ifndef QT_NO_WHEELEVENT +void Q3ScrollView::contentsWheelEvent(QWheelEvent * e) +{ + e->ignore(); +} +#endif +/*! + This event handler is called whenever the Q3ScrollView receives a + contextMenuEvent() in \a{e}: the mouse position is translated to + be a point on the contents. +*/ +void Q3ScrollView::contentsContextMenuEvent(QContextMenuEvent *e) +{ + e->ignore(); +} + +/*! + This is a low-level painting routine that draws the viewport + contents. Reimplement this if drawContents() is too high-level + (for example, if you don't want to open a QPainter on the + viewport). The paint event is passed in \a pe. +*/ +void Q3ScrollView::viewportPaintEvent(QPaintEvent* pe) +{ + QWidget* vp = viewport(); + + QPainter p(vp); + QRect r = pe->rect(); + + if (d->clipped_viewport) { + QRect rr( + -d->clipped_viewport->x(), -d->clipped_viewport->y(), + d->viewport->width(), d->viewport->height() + ); + r &= rr; + if (r.isValid()) { + int ex = r.x() + d->clipped_viewport->x() + d->contentsX(); + int ey = r.y() + d->clipped_viewport->y() + d->contentsY(); + int ew = r.width(); + int eh = r.height(); + drawContentsOffset(&p, + d->contentsX()+d->clipped_viewport->x(), + d->contentsY()+d->clipped_viewport->y(), + ex, ey, ew, eh); + } + } else { + r &= d->viewport->rect(); + int ex = r.x() + d->contentsX(); + int ey = r.y() + d->contentsY(); + int ew = r.width(); + int eh = r.height(); + drawContentsOffset(&p, d->contentsX(), d->contentsY(), ex, ey, ew, eh); + } +} + + +/*! + To provide simple processing of events on the contents, this + function receives all resize events sent to the viewport. + + The default implementation does nothing. The \a event parameter is + ignored. + + \sa QWidget::resizeEvent() +*/ +void Q3ScrollView::viewportResizeEvent(QResizeEvent * /* event */) +{ +} + +/*! \internal + + To provide simple processing of events on the contents, this + function receives all mouse press events sent to the viewport, + translates the event and calls contentsMousePressEvent(). + + \sa contentsMousePressEvent(), QWidget::mousePressEvent() +*/ +void Q3ScrollView::viewportMousePressEvent(QMouseEvent* e) +{ + QMouseEvent ce(e->type(), viewportToContents(e->pos()), + e->globalPos(), e->button(), e->state()); + contentsMousePressEvent(&ce); + if (!ce.isAccepted()) + e->ignore(); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all mouse release events sent to the viewport, translates + the event and calls contentsMouseReleaseEvent(). + + \sa QWidget::mouseReleaseEvent() +*/ +void Q3ScrollView::viewportMouseReleaseEvent(QMouseEvent* e) +{ + QMouseEvent ce(e->type(), viewportToContents(e->pos()), + e->globalPos(), e->button(), e->state()); + contentsMouseReleaseEvent(&ce); + if (!ce.isAccepted()) + e->ignore(); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all mouse double click events sent to the viewport, + translates the event and calls contentsMouseDoubleClickEvent(). + + \sa QWidget::mouseDoubleClickEvent() +*/ +void Q3ScrollView::viewportMouseDoubleClickEvent(QMouseEvent* e) +{ + QMouseEvent ce(e->type(), viewportToContents(e->pos()), + e->globalPos(), e->button(), e->state()); + contentsMouseDoubleClickEvent(&ce); + if (!ce.isAccepted()) + e->ignore(); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all mouse move events sent to the viewport, translates the + event and calls contentsMouseMoveEvent(). + + \sa QWidget::mouseMoveEvent() +*/ +void Q3ScrollView::viewportMouseMoveEvent(QMouseEvent* e) +{ + QMouseEvent ce(e->type(), viewportToContents(e->pos()), + e->globalPos(), e->button(), e->state()); + contentsMouseMoveEvent(&ce); + if (!ce.isAccepted()) + e->ignore(); +} + +#ifndef QT_NO_DRAGANDDROP + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all drag enter events sent to the viewport, translates the + event and calls contentsDragEnterEvent(). + + \sa QWidget::dragEnterEvent() +*/ +void Q3ScrollView::viewportDragEnterEvent(QDragEnterEvent* e) +{ + e->setPoint(viewportToContents(e->pos())); + contentsDragEnterEvent(e); + e->setPoint(contentsToViewport(e->pos())); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all drag move events sent to the viewport, translates the + event and calls contentsDragMoveEvent(). + + \sa QWidget::dragMoveEvent() +*/ +void Q3ScrollView::viewportDragMoveEvent(QDragMoveEvent* e) +{ + e->setPoint(viewportToContents(e->pos())); + contentsDragMoveEvent(e); + e->setPoint(contentsToViewport(e->pos())); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all drag leave events sent to the viewport and calls + contentsDragLeaveEvent(). + + \sa QWidget::dragLeaveEvent() +*/ +void Q3ScrollView::viewportDragLeaveEvent(QDragLeaveEvent* e) +{ + contentsDragLeaveEvent(e); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all drop events sent to the viewport, translates the event + and calls contentsDropEvent(). + + \sa QWidget::dropEvent() +*/ +void Q3ScrollView::viewportDropEvent(QDropEvent* e) +{ + e->setPoint(viewportToContents(e->pos())); + contentsDropEvent(e); + e->setPoint(contentsToViewport(e->pos())); +} + +#endif // QT_NO_DRAGANDDROP + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all wheel events sent to the viewport, translates the + event and calls contentsWheelEvent(). + + \sa QWidget::wheelEvent() +*/ +#ifndef QT_NO_WHEELEVENT +void Q3ScrollView::viewportWheelEvent(QWheelEvent* e) +{ + /* + Different than standard mouse events, because wheel events might + be sent to the focus widget if the widget-under-mouse doesn't want + the event itself. + */ + QWheelEvent ce(viewportToContents(e->pos()), + e->globalPos(), e->delta(), e->state()); + 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 Q3ScrollView::viewportContextMenuEvent(QContextMenuEvent *e) +{ + QContextMenuEvent ce(e->reason(), viewportToContents(e->pos()), e->globalPos(), e->state()); + contentsContextMenuEvent(&ce); + if (ce.isAccepted()) + e->accept(); + else + e->ignore(); +} + +/*! + Returns the component horizontal scroll bar. It is made available + to allow accelerators, autoscrolling, etc. + + It should not be used for other purposes. + + This function never returns 0. +*/ +QScrollBar* Q3ScrollView::horizontalScrollBar() const +{ + return d->hbar; +} + +/*! + Returns the component vertical scroll bar. It is made available to + allow accelerators, autoscrolling, etc. + + It should not be used for other purposes. + + This function never returns 0. +*/ +QScrollBar* Q3ScrollView::verticalScrollBar() const { + return d->vbar; +} + + +/*! + Scrolls the content so that the point (\a x, \a y) is visible with at + least 50-pixel margins (if possible, otherwise centered). +*/ +void Q3ScrollView::ensureVisible(int x, int y) +{ + ensureVisible(x, y, 50, 50); +} + +/*! + \overload + + Scrolls the content so that the point (\a x, \a y) is visible with at + least the \a xmargin and \a ymargin margins (if possible, + otherwise centered). +*/ +void Q3ScrollView::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, \a y) is in the top-left + corner. +*/ +void Q3ScrollView::setContentsPos(int x, int y) +{ +#if 0 + // bounds checking... + if (QApplication::reverseLayout()) + if (x > d->contentsWidth() - visibleWidth()) x = d->contentsWidth() - visibleWidth(); + else +#endif + if (x < 0) x = 0; + if (y < 0) y = 0; + // Choke signal handling while we update BOTH sliders. + d->signal_choke=true; + moveContents(-x, -y); + d->vbar->setValue(y); + d->hbar->setValue(x); + d->signal_choke=false; +} + +/*! + Scrolls the content by \a dx to the left and \a dy upwards. +*/ +void Q3ScrollView::scrollBy(int dx, int dy) +{ + setContentsPos(QMAX(d->contentsX()+dx, 0), QMAX(d->contentsY()+dy, 0)); +} + +/*! + Scrolls the content so that the point (\a x, \a y) is in the center + of visible area. +*/ +void Q3ScrollView::center(int x, int y) +{ + ensureVisible(x, y, 32000, 32000); +} + +/*! + \overload + + Scrolls the content so that the point (\a x, \a 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 Q3ScrollView::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 Q3ScrollView::contentsMoving(int x, int y) + + This signal is emitted just before the contents are moved to + position (\a x, \a y). + + \sa contentsX(), contentsY() +*/ + +/*! + Moves the contents by (\a x, \a y). +*/ +void Q3ScrollView::moveContents(int x, int y) +{ + if (-x+visibleWidth() > d->contentsWidth()) +#if 0 + if(QApplication::reverseLayout()) + x=QMAX(0,-d->contentsWidth()+visibleWidth()); + else +#endif + x=QMIN(0,-d->contentsWidth()+visibleWidth()); + if (-y+visibleHeight() > contentsHeight()) + y=QMIN(0,-contentsHeight()+visibleHeight()); + + int dx = x - d->vx; + int dy = y - d->vy; + + if (!dx && !dy) + return; // Nothing to do + + emit contentsMoving(-x, -y); + + d->vx = x; + d->vy = y; + + if (d->clipped_viewport || d->static_bg) { + // Cheap move (usually) + d->moveAllBy(dx,dy); + } else if (/*dx && dy ||*/ + (QABS(dy) * 5 > visibleHeight() * 4) || + (QABS(dx) * 5 > visibleWidth() * 4) + ) + { + // Big move + if (viewport()->updatesEnabled()) + viewport()->update(); + d->moveAllBy(dx,dy); + } else if (!d->fake_scroll || d->contentsWidth() > visibleWidth()) { + // Small move + clipper()->scroll(dx,dy); + } + d->hideOrShowAll(this, true); +} + +/*! + \property Q3ScrollView::contentsX + \brief the X coordinate of the contents that are at the left edge of + the viewport. +*/ +int Q3ScrollView::contentsX() const +{ + return d->contentsX(); +} + +/*! + \property Q3ScrollView::contentsY + \brief the Y coordinate of the contents that are at the top edge of + the viewport. +*/ +int Q3ScrollView::contentsY() const +{ + return d->contentsY(); +} + +/*! + \property Q3ScrollView::contentsWidth + \brief the width of the contents area +*/ +int Q3ScrollView::contentsWidth() const +{ + return d->contentsWidth(); +} + +/*! + \property Q3ScrollView::contentsHeight + \brief the height of the contents area +*/ +int Q3ScrollView::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 Q3ScrollView::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 Q3ScrollView::updateContents(int x, int y, int w, int h) +{ + if (!isVisible() || !updatesEnabled()) + return; + + QWidget* vp = viewport(); + + // Translate + x -= d->contentsX(); + y -= d->contentsY(); + + 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 Q3ScrollView::updateContents(const QRect& r) +{ + updateContents(r.x(), r.y(), r.width(), r.height()); +} + +/*! + \overload +*/ +void Q3ScrollView::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 Q3ScrollView::repaintContents(const QRect& r, bool erase) +{ + repaintContents(r.x(), r.y(), r.width(), r.height(), erase); +} + + +/*! + \overload + + Repaints the contents. If \a erase is true the background is + cleared using the background color. +*/ +void Q3ScrollView::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 Q3ScrollView::repaintContents(int x, int y, int w, int h, bool /*erase*/) +{ + if (!isVisible() || !updatesEnabled()) + return; + + QWidget* vp = viewport(); + + // Translate logical to clipper() + x -= d->contentsX(); + y -= d->contentsY(); + + 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->update(x, y, w, h); +} + + +/*! + For backward-compatibility only. It is easier to use + drawContents(QPainter*,int,int,int,int). + + The default implementation translates the painter appropriately + and calls drawContents(QPainter*,int,int,int,int). See + drawContents() for an explanation of the parameters \a p, \a + offsetx, \a offsety, \a clipx, \a clipy, \a clipw and \a cliph. +*/ +void Q3ScrollView::drawContentsOffset(QPainter* p, int offsetx, int offsety, int clipx, int clipy, int clipw, int cliph) +{ + p->translate(-offsetx,-offsety); + drawContents(p, clipx, clipy, clipw, cliph); +} + +/*! + \fn void Q3ScrollView::drawContents(QPainter* p, int clipx, int clipy, int clipw, int cliph) + + Reimplement this function if you are viewing a drawing area rather + than a widget. + + The function should draw the rectangle (\a clipx, \a clipy, \a + clipw, \a cliph) of the contents using painter \a p. The clip + rectangle is in the scrollview's coordinates. + + For example: + \snippet doc/src/snippets/code/src_qt3support_widgets_q3scrollview.cpp 4 + + The clip rectangle and translation of the painter \a p is already + set appropriately. +*/ +void Q3ScrollView::drawContents(QPainter*, int, int, int, int) +{ +} + + +/*! + \reimp +*/ +void Q3ScrollView::frameChanged() +{ + // slight ugle-hack - the listview header needs readjusting when + // changing the frame + if (Q3ListView *lv = qobject_cast<Q3ListView *>(this)) + lv->triggerUpdate(); + Q3Frame::frameChanged(); + updateScrollBars(); +} + + +/*! + Returns the viewport widget of the scrollview. This is the widget + containing the contents widget or which is the drawing area. +*/ +QWidget* Q3ScrollView::viewport() const +{ + if (d->clipped_viewport) + return d->clipped_viewport; + return d->viewport; +} + +/*! + Returns the clipper widget. Contents in the scrollview are + ultimately clipped to be inside the clipper widget. + + You should not need to use this function. + + \sa visibleWidth(), visibleHeight() +*/ +QWidget* Q3ScrollView::clipper() const +{ + return d->viewport; +} + +/*! + \property Q3ScrollView::visibleWidth + \brief the horizontal amount of the content that is visible +*/ +int Q3ScrollView::visibleWidth() const +{ + return clipper()->width(); +} + +/*! + \property Q3ScrollView::visibleHeight + \brief the vertical amount of the content that is visible +*/ +int Q3ScrollView::visibleHeight() const +{ + return clipper()->height(); +} + + +void Q3ScrollView::changeFrameRect(const QRect& r) +{ + QRect oldr = frameRect(); + if (oldr != r) { + QRect cr = contentsRect(); + QRegion fr(frameRect()); + fr = fr.subtracted(contentsRect()); + setFrameRect(r); + if (isVisible()) { + cr = cr.intersected(contentsRect()); + fr = fr.united(frameRect()); + fr = fr.subtracted(cr); + if (!fr.isEmpty()) + update(fr); + } + } +} + + +/*! + 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 Q3ScrollView::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 Q3ScrollView::leftMargin() const +{ + return d->l_marg; +} + + +/*! + Returns the top margin. + + \sa setMargins() +*/ +int Q3ScrollView::topMargin() const +{ + return d->t_marg; +} + + +/*! + Returns the right margin. + + \sa setMargins() +*/ +int Q3ScrollView::rightMargin() const +{ + return d->r_marg; +} + + +/*! + Returns the bottom margin. + + \sa setMargins() +*/ +int Q3ScrollView::bottomMargin() const +{ + return d->b_marg; +} + +/*! + \reimp +*/ +bool Q3ScrollView::focusNextPrevChild(bool next) +{ + // Makes sure that the new focus widget is on-screen, if + // necessary by scrolling the scroll view. + bool retval = Q3Frame::focusNextPrevChild(next); + if (retval) { + QWidget *w = window()->focusWidget(); + if (isAncestorOf(w)) { + QSVChildRec *r = d->ancestorRec(w); + if (r && (r->child == w || w->isVisibleTo(r->child))) { + QPoint cp = r->child->mapToGlobal(QPoint(0, 0)); + QPoint cr = w->mapToGlobal(QPoint(0, 0)) - cp; + ensureVisible(r->x + cr.x() + w->width()/2, r->y + cr.y() + w->height()/2, + w->width()/2, w->height()/2); + } + } + } + return retval; +} + + + +/*! + 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. +*/ +void Q3ScrollView::enableClipper(bool y) +{ + if (!d->clipped_viewport == !y) + return; + if (d->children.count()) + qFatal("May only call Q3ScrollView::enableClipper() before adding widgets"); + if (y) { + d->clipped_viewport = new QClipperWidget(clipper(), "qt_clipped_viewport", QFlag(d->flags)); + d->clipped_viewport->setGeometry(-coord_limit/2,-coord_limit/2, + coord_limit,coord_limit); + d->clipped_viewport->setBackgroundMode(d->viewport->backgroundMode()); + d->viewport->setBackgroundMode(NoBackground); // no exposures for this + d->viewport->removeEventFilter(this); + d->clipped_viewport->installEventFilter(this); + d->clipped_viewport->show(); + } else { + delete d->clipped_viewport; + d->clipped_viewport = 0; + } +} + +/*! + Sets the scrollview to have a static background if \a y is true, + or a scrolling background if \a y is false. By default, the + background is scrolling. + + Be aware that this mode is quite slow, as a full repaint of the + visible area has to be triggered on every contents move. + + \sa hasStaticBackground() +*/ +void Q3ScrollView::setStaticBackground(bool y) +{ + d->static_bg = y; +} + +/*! + Returns true if Q3ScrollView uses a static background; otherwise + returns false. + + \sa setStaticBackground() +*/ +bool Q3ScrollView::hasStaticBackground() const +{ + return d->static_bg; +} + +/*! + \overload + + Returns the point \a p translated to a point on the viewport() + widget. +*/ +QPoint Q3ScrollView::contentsToViewport(const QPoint& p) const +{ + if (d->clipped_viewport) { + return QPoint(p.x() - d->contentsX() - d->clipped_viewport->x(), + p.y() - d->contentsY() - d->clipped_viewport->y()); + } else { + return QPoint(p.x() - d->contentsX(), + p.y() - d->contentsY()); + } +} + +/*! + \overload + + Returns the point on the viewport \a vp translated to a point in + the contents. +*/ +QPoint Q3ScrollView::viewportToContents(const QPoint& vp) const +{ + if (d->clipped_viewport) { + return QPoint(vp.x() + d->contentsX() + d->clipped_viewport->x(), + vp.y() + d->contentsY() + d->clipped_viewport->y()); + } else { + return QPoint(vp.x() + d->contentsX(), + vp.y() + d->contentsY()); + } +} + + +/*! + Translates a point (\a x, \a y) in the contents to a point (\a vx, + \a vy) on the viewport() widget. +*/ +void Q3ScrollView::contentsToViewport(int x, int y, int& vx, int& vy) const +{ + const QPoint v = contentsToViewport(QPoint(x,y)); + vx = v.x(); + vy = v.y(); +} + +/*! + Translates a point (\a vx, \a vy) on the viewport() widget to a + point (\a x, \a y) in the contents. +*/ +void Q3ScrollView::viewportToContents(int vx, int vy, int& x, int& y) const +{ + const QPoint c = viewportToContents(QPoint(vx,vy)); + x = c.x(); + y = c.y(); +} + +/*! + \reimp +*/ +QSize Q3ScrollView::sizeHint() const +{ + if (d->use_cached_size_hint && d->cachedSizeHint.isValid()) + return d->cachedSizeHint; + + constPolish(); + int f = 2 * frameWidth(); + int h = fontMetrics().height(); + QSize sz(f, f); + if (d->policy > Manual) { + QSVChildRec *r = d->children.first(); + if (r) { + QSize cs = r->child->sizeHint(); + if (cs.isValid()) + sz += cs.boundedTo(r->child->maximumSize()); + else + sz += r->child->size(); + } + } else { + sz += QSize(d->contentsWidth(), contentsHeight()); + } + if (d->vMode == AlwaysOn) + sz.setWidth(sz.width() + d->vbar->sizeHint().width()); + if (d->hMode == AlwaysOn) + sz.setHeight(sz.height() + d->hbar->sizeHint().height()); + return sz.expandedTo(QSize(12 * h, 8 * h)) + .boundedTo(QSize(36 * h, 24 * h)); +} + + +/*! + \reimp +*/ +QSize Q3ScrollView::minimumSizeHint() const +{ + int h = fontMetrics().height(); + if (h < 10) + h = 10; + int f = 2 * frameWidth(); + return QSize((6 * h) + f, (4 * h) + f); +} + + +/*! + \reimp + + (Implemented to get rid of a compiler warning.) +*/ +void Q3ScrollView::drawContents(QPainter *) +{ +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + \internal +*/ +void Q3ScrollView::startDragAutoScroll() +{ + if (!d->autoscroll_timer.isActive()) { + d->autoscroll_time = initialScrollTime; + d->autoscroll_accel = initialScrollAccel; + d->autoscroll_timer.start(d->autoscroll_time); + } +} + + +/*! + \internal +*/ +void Q3ScrollView::stopDragAutoScroll() +{ + d->autoscroll_timer.stop(); +} + + +/*! + \internal +*/ +void Q3ScrollView::doDragAutoScroll() +{ + QPoint p = d->viewport->mapFromGlobal(QCursor::pos()); + + if (d->autoscroll_accel-- <= 0 && d->autoscroll_time) { + d->autoscroll_accel = initialScrollAccel; + d->autoscroll_time--; + d->autoscroll_timer.start(d->autoscroll_time); + } + int l = QMAX(1, (initialScrollTime- d->autoscroll_time)); + + int dx = 0, dy = 0; + if (p.y() < autoscroll_margin) { + dy = -l; + } else if (p.y() > visibleHeight() - autoscroll_margin) { + dy = +l; + } + if (p.x() < autoscroll_margin) { + dx = -l; + } else if (p.x() > visibleWidth() - autoscroll_margin) { + dx = +l; + } + if (dx || dy) { + scrollBy(dx,dy); + } else { + stopDragAutoScroll(); + } +} + + +/*! + \property Q3ScrollView::dragAutoScroll + \brief whether autoscrolling in drag move events is enabled + + If this property is set to true (the default), the Q3ScrollView + 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. +*/ + +void Q3ScrollView::setDragAutoScroll(bool b) +{ + d->drag_autoscroll = b; +} + +bool Q3ScrollView::dragAutoScroll() const +{ + return d->drag_autoscroll; +} + +#endif // QT_NO_DRAGANDDROP + +/*!\internal + */ +void Q3ScrollView::setCachedSizeHint(const QSize &sh) const +{ + if (isVisible() && !d->cachedSizeHint.isValid()) + d->cachedSizeHint = sh; +} + +/*!\internal + */ +void Q3ScrollView::disableSizeHintCaching() +{ + d->use_cached_size_hint = false; +} + +/*!\internal + */ +QSize Q3ScrollView::cachedSizeHint() const +{ + return d->use_cached_size_hint ? d->cachedSizeHint : QSize(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_SCROLLVIEW diff --git a/src/qt3support/widgets/q3scrollview.h b/src/qt3support/widgets/q3scrollview.h new file mode 100644 index 0000000..14c2720 --- /dev/null +++ b/src/qt3support/widgets/q3scrollview.h @@ -0,0 +1,253 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3SCROLLVIEW_H +#define Q3SCROLLVIEW_H + +#include <Qt3Support/q3frame.h> +#include <QtGui/qscrollbar.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class Q3ScrollViewData; + +class Q_COMPAT_EXPORT Q3ScrollView : public Q3Frame +{ + 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 ) + Q_PROPERTY( bool dragAutoScroll READ dragAutoScroll WRITE setDragAutoScroll ) + +public: + Q3ScrollView(QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0); + ~Q3ScrollView(); + + enum ResizePolicy { Default, Manual, AutoOne, AutoOneFit }; + virtual void setResizePolicy( ResizePolicy ); + ResizePolicy resizePolicy() const; + + void styleChange( QStyle & ); + void removeChild(QWidget* child); + virtual void addChild( QWidget* child, int x=0, int y=0 ); + virtual void moveChild( QWidget* child, int x, int y ); + int childX(QWidget* child); + int childY(QWidget* child); + bool childIsVisible(QWidget* child) { return child->isVisible(); } // obsolete functions + void showChild(QWidget* child, bool yes=true) { child->setVisible(yes); } + + enum ScrollBarMode { Auto, AlwaysOff, AlwaysOn }; + + ScrollBarMode vScrollBarMode() const; + virtual void setVScrollBarMode( ScrollBarMode ); + + ScrollBarMode hScrollBarMode() const; + virtual void setHScrollBarMode( ScrollBarMode ); + + QWidget* cornerWidget() const; + virtual void setCornerWidget(QWidget*); + + // ### 4.0: Consider providing a factory function for scrollbars + // (e.g. make the two following functions virtual) + QScrollBar* horizontalScrollBar() const; + QScrollBar* verticalScrollBar() const; + QWidget* viewport() const; + QWidget* clipper() const; + + int visibleWidth() const; + int visibleHeight() const; + + int contentsWidth() const; + int contentsHeight() const; + int contentsX() const; + int contentsY() const; + + void resize( int w, int h ); + void resize( const QSize& ); + void setVisible(bool visible); + + void updateContents( int x, int y, int w, int h ); + void updateContents( const QRect& r ); + void updateContents(); + void repaintContents( int x, int y, int w, int h, bool erase=true ); + void repaintContents( const QRect& r, bool erase=true ); + void repaintContents( bool erase=true ); + void contentsToViewport( int x, int y, int& vx, int& vy ) const; + void viewportToContents( int vx, int vy, int& x, int& y ) const; + QPoint contentsToViewport( const QPoint& ) const; + QPoint viewportToContents( const QPoint& ) const; + void enableClipper( bool y ); + + void setStaticBackground( bool y ); + bool hasStaticBackground() const; + + QSize viewportSize( int, int ) const; + QSize sizeHint() const; + QSize minimumSizeHint() const; + + void removeChild(QObject* child); + + bool isHorizontalSliderPressed(); + bool isVerticalSliderPressed(); + + virtual void setDragAutoScroll( bool b ); + bool dragAutoScroll() const; + +Q_SIGNALS: + void contentsMoving(int x, int y); + void horizontalSliderPressed(); + void horizontalSliderReleased(); + void verticalSliderPressed(); + void verticalSliderReleased(); + +public Q_SLOTS: + virtual void resizeContents( int w, int h ); + void scrollBy( int dx, int dy ); + virtual void setContentsPos( int x, int y ); + void ensureVisible(int x, int y); + void ensureVisible(int x, int y, int xmargin, int ymargin); + void center(int x, int y); + void center(int x, int y, float xmargin, float ymargin); + + void updateScrollBars(); // ### virtual in 4.0 + void setEnabled( bool enable ); + +protected: + virtual void drawContents(QPainter*, int cx, int cy, int cw, int ch); + virtual void drawContentsOffset(QPainter*, int ox, int oy, + int cx, int cy, int cw, int ch); + + + virtual void contentsMousePressEvent( QMouseEvent* ); + virtual void contentsMouseReleaseEvent( QMouseEvent* ); + virtual void contentsMouseDoubleClickEvent( QMouseEvent* ); + virtual void contentsMouseMoveEvent( QMouseEvent* ); + virtual void contentsDragEnterEvent( QDragEnterEvent * ); + virtual void contentsDragMoveEvent( QDragMoveEvent * ); + virtual void contentsDragLeaveEvent( QDragLeaveEvent * ); + virtual void contentsDropEvent( QDropEvent * ); + virtual void contentsWheelEvent( QWheelEvent * ); + virtual void contentsContextMenuEvent( QContextMenuEvent * ); + + + virtual void viewportPaintEvent( QPaintEvent* ); + virtual void viewportResizeEvent( QResizeEvent* ); + virtual void viewportMousePressEvent( QMouseEvent* ); + virtual void viewportMouseReleaseEvent( QMouseEvent* ); + virtual void viewportMouseDoubleClickEvent( QMouseEvent* ); + virtual void viewportMouseMoveEvent( QMouseEvent* ); + virtual void viewportDragEnterEvent( QDragEnterEvent * ); + virtual void viewportDragMoveEvent( QDragMoveEvent * ); + virtual void viewportDragLeaveEvent( QDragLeaveEvent * ); + virtual void viewportDropEvent( QDropEvent * ); + virtual void viewportWheelEvent( QWheelEvent * ); + virtual void viewportContextMenuEvent( QContextMenuEvent * ); + + void frameChanged(); + +public: + virtual void setMargins(int left, int top, int right, int bottom); + int leftMargin() const; + int topMargin() const; + int rightMargin() const; + int bottomMargin() const; +protected: + + bool focusNextPrevChild( bool next ); + + virtual void setHBarGeometry(QScrollBar& hbar, int x, int y, int w, int h); + virtual void setVBarGeometry(QScrollBar& vbar, int x, int y, int w, int h); + + void resizeEvent(QResizeEvent*); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void wheelEvent( QWheelEvent * ); + void contextMenuEvent( QContextMenuEvent * ); + bool eventFilter( QObject *, QEvent *e ); + + void setCachedSizeHint( const QSize &sh ) const; + QSize cachedSizeHint() const; + void fontChange( const QFont & ); + +private: + void drawContents( QPainter* ); + void moveContents(int x, int y); + + Q3ScrollViewData* d; + +private Q_SLOTS: + void hslide(int); + void vslide(int); + void hbarIsPressed(); + void hbarIsReleased(); + void vbarIsPressed(); + void vbarIsReleased(); + void doDragAutoScroll(); + void startDragAutoScroll(); + void stopDragAutoScroll(); + +private: // Disabled copy constructor and operator= + Q_DISABLE_COPY(Q3ScrollView) + void changeFrameRect(const QRect&); + +public: + void disableSizeHintCaching(); + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3SCROLLVIEW_H diff --git a/src/qt3support/widgets/q3spinwidget.cpp b/src/qt3support/widgets/q3spinwidget.cpp new file mode 100644 index 0000000..8a6152b --- /dev/null +++ b/src/qt3support/widgets/q3spinwidget.cpp @@ -0,0 +1,473 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3rangecontrol.h" + +#ifndef QT_NO_SPINWIDGET + +#include "qabstractspinbox.h" +#include "qevent.h" +#include "qpainter.h" +#include "qrect.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtimer.h" + +QT_BEGIN_NAMESPACE + +class Q3SpinWidgetPrivate +{ +public: + Q3SpinWidgetPrivate() + : upEnabled(true), + downEnabled(true), + theButton(0), + buttonDown(0), + timerUp(0), + bsyms(Q3SpinWidget::UpDownArrows), + ed (0) {} + uint upEnabled :1; + uint downEnabled :1; + uint theButton :2; + uint buttonDown :2; + uint timerUp : 1; + QRect up; + QRect down; + QTimer auRepTimer; + Q3SpinWidget::ButtonSymbols bsyms; + QWidget *ed; + void startTimer(int msec) { auRepTimer.start(msec, true); } + void startTimer(bool up, int msec) { timerUp = up; startTimer(msec); } + void stopTimer() { auRepTimer.stop(); } +}; + +/*! + \class Q3SpinWidget + \brief The Q3SpinWidget class is an internal range control related class. + + \internal + + Constructs an empty range control widget with parent \a parent + called \a name. + +*/ + +Q3SpinWidget::Q3SpinWidget(QWidget* parent, const char* name) + : QWidget(parent, name) +{ + d = new Q3SpinWidgetPrivate(); + connect(&d->auRepTimer, SIGNAL(timeout()), this, SLOT(timerDone())); + setFocusPolicy(Qt::StrongFocus); + + arrange(); + updateDisplay(); +} + + +/*! Destroys the object and frees any allocated resources. + +*/ + +Q3SpinWidget::~Q3SpinWidget() +{ + delete d; +} + +/*! */ +QWidget * Q3SpinWidget::editWidget() +{ + return d->ed; +} + +/*! + Sets the editing widget to \a w. +*/ +void Q3SpinWidget::setEditWidget(QWidget * w) +{ + if (w) { + if (w->parentWidget() != this) + w->setParent(this); + setFocusProxy(w); + } + d->ed = w; + arrange(); + updateDisplay(); +} + +/*! \reimp + +*/ + +void Q3SpinWidget::mousePressEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) { + d->stopTimer(); + d->buttonDown = 0; + d->theButton = 0; + repaint(d->down.united(d->up)); + 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.united(d->up)); + } else if (d->buttonDown & 1) { + repaint(d->down); + stepDown(); + d->startTimer(false, 300); + } else if (d->buttonDown & 2) { + repaint(d->up); + stepUp(); + d->startTimer(true, 300); + } + } + + if (!oldButtonDown && !d->buttonDown) + e->ignore(); + +} + +static QStyleOptionSpinBox getStyleOption(const Q3SpinWidget *spin) +{ + QStyleOptionSpinBox opt; + opt.init(spin); + opt.frame = true; + opt.subControls = 0; + opt.buttonSymbols = (QAbstractSpinBox::ButtonSymbols)spin->buttonSymbols(); + opt.stepEnabled = 0; + if (spin->isUpEnabled()) + opt.stepEnabled |= QAbstractSpinBox::StepUpEnabled; + if (spin->isDownEnabled()) + opt.stepEnabled |= QAbstractSpinBox::StepDownEnabled; + opt.activeSubControls = 0; + return opt; +} + +/*! + +*/ + +void Q3SpinWidget::arrange() +{ + QStyleOptionSpinBox opt = getStyleOption(this); + d->up = style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxUp, this); + d->down = style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxDown, this); + if (d->ed) { + QRect r = style()->subControlRect(QStyle::CC_SpinBox, &opt, + QStyle::SC_SpinBoxEditField, this); + d->ed->setGeometry(r); + } +} + +/*! + +*/ + +void Q3SpinWidget::stepUp() +{ + emit stepUpPressed(); +} + +void Q3SpinWidget::resizeEvent(QResizeEvent*) +{ + arrange(); +} + +/*! + +*/ + +void Q3SpinWidget::stepDown() +{ + emit stepDownPressed(); +} + + +void Q3SpinWidget::timerDone() +{ + // we use a double timer to make it possible for users to do + // something with 0-timer on valueChanged. + QTimer::singleShot(1, this, SLOT(timerDoneEx())); +} + +void Q3SpinWidget::timerDoneEx() +{ + if (!d->buttonDown) + return; + if (d->timerUp) + stepUp(); + else + stepDown(); + d->startTimer(100); +} + + +/*! + The event is passed in \a e. +*/ + +void Q3SpinWidget::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) + return; + + uint oldButtonDown = d->theButton; + d->theButton = 0; + if (oldButtonDown != d->theButton) { + if (oldButtonDown & 1) + repaint(d->down); + else if (oldButtonDown & 2) + repaint(d->up); + } + d->stopTimer(); + d->buttonDown = 0; + + if (!oldButtonDown && !d->buttonDown) + e->ignore(); +} + + +/*! + The event is passed in \a e. +*/ + +void Q3SpinWidget::mouseMoveEvent(QMouseEvent *e) +{ + if (!(e->state() & Qt::LeftButton)) + return; + + uint oldButtonDown = d->theButton; + if (oldButtonDown & 1 && !d->down.contains(e->pos())) { + d->stopTimer(); + d->theButton = 0; + repaint(d->down); + } else if (oldButtonDown & 2 && !d->up.contains(e->pos())) { + d->stopTimer(); + d->theButton = 0; + repaint(d->up); + } else if (!oldButtonDown && d->up.contains(e->pos()) && d->buttonDown & 2) { + d->startTimer(500); + d->theButton = 2; + repaint(d->up); + } else if (!oldButtonDown && d->down.contains(e->pos()) && d->buttonDown & 1) { + d->startTimer(500); + d->theButton = 1; + repaint(d->down); + } + + if (!oldButtonDown && !d->buttonDown) + e->ignore(); +} + + +/*! + The event is passed in \a e. +*/ +#ifndef QT_NO_WHEELEVENT +void Q3SpinWidget::wheelEvent(QWheelEvent *e) +{ + e->accept(); + static float offset = 0; + static Q3SpinWidget* offset_owner = 0; + if (offset_owner != this) { + offset_owner = this; + offset = 0; + } + offset += -e->delta()/120; + if (QABS(offset) < 1) + return; + int ioff = int(offset); + int i; + for(i=0; i < QABS(ioff); i++) + offset > 0 ? stepDown() : stepUp(); + offset -= ioff; +} +#endif + +/*! + +*/ +void Q3SpinWidget::paintEvent(QPaintEvent *) +{ + QPainter p(this); + QStyleOptionSpinBox opt = getStyleOption(this); + + if (d->theButton & 1) + opt.activeSubControls = QStyle::SC_SpinBoxDown; + else if (d->theButton & 2) + opt.activeSubControls = QStyle::SC_SpinBoxUp; + else + opt.activeSubControls = QStyle::SC_None; + opt.rect = style()->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxFrame, this); + opt.subControls = QStyle::SC_All; + style()->drawComplexControl(QStyle::CC_SpinBox, &opt, &p, this); +} + + +// ### What does the QEvent passed in contain? It used to be the previous style. +/*! + The previous style is passed in \a ev. +*/ +void Q3SpinWidget::changeEvent(QEvent *ev) +{ + if(ev->type() == QEvent::StyleChange) { + arrange(); + } else if(ev->type() == QEvent::ActivationChange) { + if (!isActiveWindow() && d->buttonDown) { //was active, but lost focus + d->stopTimer(); + d->buttonDown = 0; + d->theButton = 0; + } + } else if(ev->type() == QEvent::EnabledChange) { + d->upEnabled = isEnabled(); + d->downEnabled = isEnabled(); + updateDisplay(); + } + QWidget::changeEvent(ev); +} + +/*! +*/ + +QRect Q3SpinWidget::upRect() const +{ + return d->up; +} + +/*! +*/ + +QRect Q3SpinWidget::downRect() const +{ + return d->down; +} + +/*! +*/ + +void Q3SpinWidget::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(); +} + +/*! + Sets up-enabled to \a on. +*/ + +void Q3SpinWidget::setUpEnabled(bool on) +{ + if ((bool)d->upEnabled != on) { + d->upEnabled = on; + updateDisplay(); + } +} + +/*! +*/ + +bool Q3SpinWidget::isUpEnabled() const +{ + return d->upEnabled; +} + +/*! + Sets down-enabled to \a on. +*/ + +void Q3SpinWidget::setDownEnabled(bool on) +{ + if ((bool)d->downEnabled != on) { + d->downEnabled = on; + updateDisplay(); + } +} + +/*! +*/ + +bool Q3SpinWidget::isDownEnabled() const +{ + return d->downEnabled; +} + +/*! + Sets the button symbol to \a bs. +*/ + +void Q3SpinWidget::setButtonSymbols(ButtonSymbols bs) +{ + d->bsyms = bs; +} + +/*! +*/ + +Q3SpinWidget::ButtonSymbols Q3SpinWidget::buttonSymbols() const +{ + return d->bsyms; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qt3support/widgets/q3titlebar.cpp b/src/qt3support/widgets/q3titlebar.cpp new file mode 100644 index 0000000..df53364 --- /dev/null +++ b/src/qt3support/widgets/q3titlebar.cpp @@ -0,0 +1,645 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" + +#ifndef QT_NO_TITLEBAR + +#include "qapplication.h" +#include "qcursor.h" +#include "qdatetime.h" +#include "qevent.h" +#include "qimage.h" +#include "qpainter.h" +#include "qiodevice.h" +#include "qpixmap.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtimer.h" +#include "qtooltip.h" +#include "qdebug.h" +#if defined(Q_WS_WIN) +#include "qt_windows.h" +#endif + +#include "private/qapplication_p.h" +#include "private/q3titlebar_p.h" +#include "private/qwidget_p.h" + +QT_BEGIN_NAMESPACE + +class Q3TitleBarPrivate : public QWidgetPrivate +{ + Q_DECLARE_PUBLIC(Q3TitleBar) +public: + Q3TitleBarPrivate() + : toolTip(0), act(0), window(0), movable(1), pressed(0), autoraise(0), inevent(0) + { + } + + Qt::WindowFlags flags; + QStyle::SubControl buttonDown; + QPoint moveOffset; + QToolTip *toolTip; + bool act :1; + QWidget* window; + bool movable :1; + bool pressed :1; + bool autoraise :1; + bool inevent : 1; + + int titleBarState() const; + QStyleOptionTitleBar getStyleOption() const; + void readColors(); +}; + +inline int Q3TitleBarPrivate::titleBarState() const +{ + uint state = window ? window->windowState() : static_cast<Qt::WindowStates>(Qt::WindowNoState); + state |= uint(act ? QStyle::State_Active : QStyle::State_None); + return (int)state; +} + +QStyleOptionTitleBar Q3TitleBarPrivate::getStyleOption() const +{ + Q_Q(const Q3TitleBar); + QStyleOptionTitleBar opt; + opt.init(q); + opt.text = q->windowTitle(); + //################ + QIcon icon = q->windowIcon(); + QSize s = icon.actualSize(QSize(64, 64)); + opt.icon = icon.pixmap(s); + opt.subControls = QStyle::SC_All; + opt.activeSubControls = QStyle::SC_None; + opt.titleBarState = titleBarState(); + opt.titleBarFlags = flags; + return opt; +} + +Q3TitleBar::Q3TitleBar(QWidget *w, QWidget *parent, Qt::WindowFlags f) + : QWidget(*new Q3TitleBarPrivate, parent, Qt::WStyle_Customize | Qt::WStyle_NoBorder) +{ + Q_D(Q3TitleBar); + if (f == 0 && w) + f = w->windowFlags(); + d->flags = f; + d->window = w; + d->buttonDown = QStyle::SC_None; + d->act = 0; + if (w) { + if (w->minimumSize() == w->maximumSize()) + d->flags &= ~Qt::WindowMaximizeButtonHint; + setWindowTitle(w->windowTitle()); + } + + d->readColors(); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + setMouseTracking(true); + setAutoRaise(style()->styleHint(QStyle::SH_TitleBar_AutoRaise, 0, this)); +} + +void Q3TitleBar::setFakeWindowFlags(Qt::WindowFlags f) +{ + Q_D(Q3TitleBar); + d->flags = f; +} + +Qt::WindowFlags Q3TitleBar::fakeWindowFlags() const +{ + Q_D(const Q3TitleBar); + return d->flags; +} + +Q3TitleBar::~Q3TitleBar() +{ +} + +QStyleOptionTitleBar Q3TitleBar::getStyleOption() const +{ + return d_func()->getStyleOption(); +} + +#ifdef Q_WS_WIN +static inline QRgb colorref2qrgb(COLORREF col) +{ + return qRgb(GetRValue(col),GetGValue(col),GetBValue(col)); +} +#endif + +void Q3TitleBarPrivate::readColors() +{ + Q_Q(Q3TitleBar); + QPalette pal = q->palette(); + + bool colorsInitialized = false; + +#ifdef Q_WS_WIN // ask system properties on windows +#ifndef SPI_GETGRADIENTCAPTIONS +#define SPI_GETGRADIENTCAPTIONS 0x1008 +#endif +#ifndef COLOR_GRADIENTACTIVECAPTION +#define COLOR_GRADIENTACTIVECAPTION 27 +#endif +#ifndef COLOR_GRADIENTINACTIVECAPTION +#define COLOR_GRADIENTINACTIVECAPTION 28 +#endif + if (QApplication::desktopSettingsAware()) { + pal.setColor(QPalette::Active, QPalette::Highlight, colorref2qrgb(GetSysColor(COLOR_ACTIVECAPTION))); + pal.setColor(QPalette::Inactive, QPalette::Highlight, colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTION))); + pal.setColor(QPalette::Active, QPalette::HighlightedText, colorref2qrgb(GetSysColor(COLOR_CAPTIONTEXT))); + pal.setColor(QPalette::Inactive, QPalette::HighlightedText, colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTIONTEXT))); + if (QSysInfo::WindowsVersion != QSysInfo::WV_95 && QSysInfo::WindowsVersion != QSysInfo::WV_NT) { + colorsInitialized = true; + BOOL gradient; + QT_WA({ + SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &gradient, 0); + } , { + SystemParametersInfoA(SPI_GETGRADIENTCAPTIONS, 0, &gradient, 0); + }); + if (gradient) { + pal.setColor(QPalette::Active, QPalette::Base, colorref2qrgb(GetSysColor(COLOR_GRADIENTACTIVECAPTION))); + pal.setColor(QPalette::Inactive, QPalette::Base, colorref2qrgb(GetSysColor(COLOR_GRADIENTINACTIVECAPTION))); + } else { + pal.setColor(QPalette::Active, QPalette::Base, pal.color(QPalette::Active, QPalette::Highlight)); + pal.setColor(QPalette::Inactive, QPalette::Base, pal.color(QPalette::Inactive, QPalette::Highlight)); + } + } + } +#endif // Q_WS_WIN + if (!colorsInitialized) { + pal.setColor(QPalette::Active, QPalette::Highlight, + pal.color(QPalette::Active, QPalette::Highlight)); + pal.setColor(QPalette::Active, QPalette::Base, + pal.color(QPalette::Active, QPalette::Highlight)); + pal.setColor(QPalette::Inactive, QPalette::Highlight, + pal.color(QPalette::Inactive, QPalette::Dark)); + pal.setColor(QPalette::Inactive, QPalette::Base, + pal.color(QPalette::Inactive, QPalette::Dark)); + pal.setColor(QPalette::Inactive, QPalette::HighlightedText, + pal.color(QPalette::Inactive, QPalette::Window)); + } + + q->setPalette(pal); + q->setActive(act); +} + +void Q3TitleBar::changeEvent(QEvent *ev) +{ + if(ev->type() == QEvent::ModifiedChange) + update(); + QWidget::changeEvent(ev); +} + +void Q3TitleBar::mousePressEvent(QMouseEvent *e) +{ + Q_D(Q3TitleBar); + if (!d->act) + emit doActivate(); + if (e->button() == Qt::LeftButton) { + d->pressed = true; + QStyleOptionTitleBar opt = d->getStyleOption(); + QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt, + e->pos(), this); + switch (ctrl) { + case QStyle::SC_TitleBarSysMenu: + if (d->flags & Qt::WindowSystemMenuHint) { + d->buttonDown = QStyle::SC_None; + static QTime *t = 0; + static Q3TitleBar *tc = 0; + if (!t) + t = new QTime; + if (tc != this || t->elapsed() > QApplication::doubleClickInterval()) { + emit showOperationMenu(); + t->start(); + tc = this; + } else { + tc = 0; + emit doClose(); + return; + } + } + break; + + case QStyle::SC_TitleBarShadeButton: + case QStyle::SC_TitleBarUnshadeButton: + if (d->flags & Qt::WindowShadeButtonHint) + d->buttonDown = ctrl; + break; + + case QStyle::SC_TitleBarNormalButton: + if (d->flags & Qt::WindowMinMaxButtonsHint) + d->buttonDown = ctrl; + break; + + case QStyle::SC_TitleBarMinButton: + if (d->flags & Qt::WindowMinimizeButtonHint) + d->buttonDown = ctrl; + break; + + case QStyle::SC_TitleBarMaxButton: + if (d->flags & Qt::WindowMaximizeButtonHint) + d->buttonDown = ctrl; + break; + + case QStyle::SC_TitleBarCloseButton: + if (d->flags & Qt::WindowSystemMenuHint) + d->buttonDown = ctrl; + break; + + case QStyle::SC_TitleBarLabel: + d->buttonDown = ctrl; + d->moveOffset = mapToParent(e->pos()); + break; + + default: + break; + } + repaint(); + } else { + d->pressed = false; + } +} + +void Q3TitleBar::contextMenuEvent(QContextMenuEvent *e) +{ + Q_D(Q3TitleBar); + QStyleOptionTitleBar opt = d->getStyleOption(); + QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt, e->pos(), + this); + if(ctrl == QStyle::SC_TitleBarLabel || ctrl == QStyle::SC_TitleBarSysMenu) { + e->accept(); + emit popupOperationMenu(e->globalPos()); + } else { + e->ignore(); + } +} + +void Q3TitleBar::mouseReleaseEvent(QMouseEvent *e) +{ + Q_D(Q3TitleBar); + if (e->button() == Qt::LeftButton && d->pressed) { + e->accept(); + QStyleOptionTitleBar opt = d->getStyleOption(); + QStyle::SubControl ctrl = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt, + e->pos(), this); + d->pressed = false; + if (ctrl == d->buttonDown) { + d->buttonDown = QStyle::SC_None; + repaint(); + switch(ctrl) { + case QStyle::SC_TitleBarShadeButton: + case QStyle::SC_TitleBarUnshadeButton: + if(d->flags & Qt::WindowShadeButtonHint) + emit doShade(); + break; + + case QStyle::SC_TitleBarNormalButton: + if(d->flags & Qt::WindowMaximizeButtonHint) + emit doNormal(); + break; + + case QStyle::SC_TitleBarMinButton: + if(d->flags & Qt::WindowMinimizeButtonHint) { + if (d->window && d->window->isMinimized()) + emit doNormal(); + else + emit doMinimize(); + } + break; + + case QStyle::SC_TitleBarMaxButton: + if(d->flags & Qt::WindowMaximizeButtonHint) { + if(d->window && d->window->isMaximized()) + emit doNormal(); + else + emit doMaximize(); + } + break; + + case QStyle::SC_TitleBarCloseButton: + if(d->flags & Qt::WindowSystemMenuHint) { + d->buttonDown = QStyle::SC_None; + repaint(); + emit doClose(); + return; + } + break; + + default: + break; + } + } + } else { + e->ignore(); + } +} + +void Q3TitleBar::mouseMoveEvent(QMouseEvent *e) +{ + Q_D(Q3TitleBar); + e->accept(); + switch (d->buttonDown) { + case QStyle::SC_None: + if(autoRaise()) + repaint(); + break; + case QStyle::SC_TitleBarSysMenu: + break; + case QStyle::SC_TitleBarShadeButton: + case QStyle::SC_TitleBarUnshadeButton: + case QStyle::SC_TitleBarNormalButton: + case QStyle::SC_TitleBarMinButton: + case QStyle::SC_TitleBarMaxButton: + case QStyle::SC_TitleBarCloseButton: + { + QStyle::SubControl last_ctrl = d->buttonDown; + QStyleOptionTitleBar opt = d->getStyleOption(); + d->buttonDown = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt, e->pos(), this); + if (d->buttonDown != last_ctrl) + d->buttonDown = QStyle::SC_None; + repaint(); + d->buttonDown = last_ctrl; + } + break; + + case QStyle::SC_TitleBarLabel: + if (d->buttonDown == QStyle::SC_TitleBarLabel && d->movable && d->pressed) { + if ((d->moveOffset - mapToParent(e->pos())).manhattanLength() >= 4) { + QPoint p = mapFromGlobal(e->globalPos()); + + QWidget *parent = d->window ? d->window->parentWidget() : 0; + if(parent && parent->inherits("Q3WorkspaceChild")) { + QWidget *workspace = parent->parentWidget(); + 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(); + } + } + + QPoint pp = p - d->moveOffset; + if (!parentWidget()->isMaximized()) + parentWidget()->move(pp); + } + } else { + QStyle::SubControl last_ctrl = d->buttonDown; + d->buttonDown = QStyle::SC_None; + if(d->buttonDown != last_ctrl) + repaint(); + } + break; + default: + break; + } +} + +void Q3TitleBar::resizeEvent(QResizeEvent *r) +{ + QWidget::resizeEvent(r); + cutText(); +} + +bool Q3TitleBar::isTool() const +{ + return (d_func()->flags & Qt::WindowType_Mask) == Qt::Tool; +} + +void Q3TitleBar::paintEvent(QPaintEvent *) +{ + Q_D(Q3TitleBar); + QStyleOptionTitleBar opt = d->getStyleOption(); + opt.subControls = QStyle::SC_TitleBarLabel; + opt.activeSubControls = d->buttonDown; + if (d->flags & Qt::WindowSystemMenuHint) { + opt.subControls |= QStyle::SC_TitleBarSysMenu | QStyle::SC_TitleBarCloseButton; + if (d->window && (d->flags & Qt::WindowShadeButtonHint)) { + if (d->window->isMinimized()) + opt.subControls |= QStyle::SC_TitleBarUnshadeButton; + else + opt.subControls |= QStyle::SC_TitleBarShadeButton; + } + if (d->window && (d->flags & Qt::WindowMinMaxButtonsHint)) { + if(d->window && d->window->isMinimized()) + opt.subControls |= QStyle::SC_TitleBarNormalButton; + else + opt.subControls |= QStyle::SC_TitleBarMinButton; + } + if (d->window && (d->flags & Qt::WindowMaximizeButtonHint) && !d->window->isMaximized()) + opt.subControls |= QStyle::SC_TitleBarMaxButton; + } + QStyle::SubControl under_mouse = QStyle::SC_None; + + if (underMouse()) { + under_mouse = style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt, + mapFromGlobal(QCursor::pos()), this); + opt.activeSubControls |= under_mouse; + if (d->pressed) + opt.state |= QStyle::State_Sunken; + else if(autoRaise()) + opt.state |= QStyle::State_MouseOver; + } + + opt.palette.setCurrentColorGroup(usesActiveColor() ? QPalette::Active : QPalette::Inactive); + + QPainter p(this); + if (!windowTitle().isEmpty()) + opt.titleBarFlags |= Qt::WindowTitleHint; + style()->drawComplexControl(QStyle::CC_TitleBar, &opt, &p, this); +} + +void Q3TitleBar::mouseDoubleClickEvent(QMouseEvent *e) +{ + Q_D(Q3TitleBar); + if (e->button() != Qt::LeftButton) { + e->ignore(); + return; + } + e->accept(); + QStyleOptionTitleBar opt = d->getStyleOption(); + switch (style()->hitTestComplexControl(QStyle::CC_TitleBar, &opt, e->pos(), this)) { + case QStyle::SC_TitleBarLabel: + emit doubleClicked(); + break; + + case QStyle::SC_TitleBarSysMenu: + if (d->flags & Qt::WStyle_SysMenu) + emit doClose(); + break; + + default: + break; + } +} + +void Q3TitleBar::cutText() +{ + Q_D(Q3TitleBar); + QFontMetrics fm(font()); + QStyleOptionTitleBar opt = d->getStyleOption(); + int maxw = style()->subControlRect(QStyle::CC_TitleBar, &opt, QStyle::SC_TitleBarLabel, + this).width(); + if (!d->window) + return; + + QString txt = d->window->windowTitle(); + if (style()->styleHint(QStyle::SH_TitleBar_ModifyNotification, 0, this) && d->window + && d->window->isWindowModified()) + txt += QLatin1String(" *"); + + QString cuttext = txt; + if (fm.width(txt + QLatin1Char('m')) > maxw) { + int i = txt.length(); + int dotlength = fm.width(QLatin1String("...")); + while (i>0 && fm.width(txt.left(i)) + dotlength > maxw) + i--; + if(i != (int)txt.length()) + cuttext = txt.left(i) + QLatin1String("..."); + } + + setWindowTitle(cuttext); +} + + +void Q3TitleBar::leaveEvent(QEvent *) +{ + if(autoRaise() && !d_func()->pressed) + repaint(); +} + +void Q3TitleBar::enterEvent(QEvent *) +{ + if(autoRaise() && !d_func()->pressed) + repaint(); + QEvent e(QEvent::Leave); + QApplication::sendEvent(parentWidget(), &e); +} + +void Q3TitleBar::setActive(bool active) +{ + Q_D(Q3TitleBar); + if (d->act == active) + return ; + + d->act = active; + update(); +} + +bool Q3TitleBar::isActive() const +{ + return d_func()->act; +} + +bool Q3TitleBar::usesActiveColor() const +{ + return (isActive() && isActiveWindow()) || + (!window() && QWidget::window()->isActiveWindow()); +} + +QWidget *Q3TitleBar::window() const +{ + return d_func()->window; +} + +bool Q3TitleBar::event(QEvent *e) +{ + Q_D(Q3TitleBar); + if (d->inevent) + return QWidget::event(e); + d->inevent = true; + if (e->type() == QEvent::ApplicationPaletteChange) { + d->readColors(); + return true; + } else if (e->type() == QEvent::WindowActivate) { + setActive(d->act); + } else if (e->type() == QEvent::WindowDeactivate) { + bool wasActive = d->act; + setActive(false); + d->act = wasActive; + } else if (e->type() == QEvent::WindowIconChange) { + update(); + } else if (e->type() == QEvent::WindowTitleChange) { + cutText(); + update(); + } + + d->inevent = false; + return QWidget::event(e); +} + +void Q3TitleBar::setMovable(bool b) +{ + d_func()->movable = b; +} + +bool Q3TitleBar::isMovable() const +{ + return d_func()->movable; +} + +void Q3TitleBar::setAutoRaise(bool b) +{ + d_func()->autoraise = b; +} + +bool Q3TitleBar::autoRaise() const +{ + return d_func()->autoraise; +} + +QSize Q3TitleBar::sizeHint() const +{ + ensurePolished(); + QStyleOptionTitleBar opt = d_func()->getStyleOption(); + QRect menur = style()->subControlRect(QStyle::CC_TitleBar, &opt, + QStyle::SC_TitleBarSysMenu, this); + return QSize(menur.width(), style()->pixelMetric(QStyle::PM_TitleBarHeight, &opt, this)); +} + +QT_END_NAMESPACE + +#endif //QT_NO_TITLEBAR diff --git a/src/qt3support/widgets/q3titlebar_p.h b/src/qt3support/widgets/q3titlebar_p.h new file mode 100644 index 0000000..fc52f34 --- /dev/null +++ b/src/qt3support/widgets/q3titlebar_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3TITLEBAR_P_H +#define Q3TITLEBAR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qwidget.h" +#include "qstyleoption.h" + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_TITLEBAR) + +class QToolTip; +class Q3TitleBarPrivate; +class QPixmap; + +class Q_COMPAT_EXPORT Q3TitleBar : public QWidget +{ + Q_OBJECT + Q_DECLARE_PRIVATE(Q3TitleBar) + Q_PROPERTY(bool autoRaise READ autoRaise WRITE setAutoRaise) + Q_PROPERTY(bool movable READ isMovable WRITE setMovable) + +public: + Q3TitleBar (QWidget *w, QWidget *parent, Qt::WindowFlags f = 0); + ~Q3TitleBar(); + + bool isActive() const; + bool usesActiveColor() const; + + bool isMovable() const; + void setMovable(bool); + + bool autoRaise() const; + void setAutoRaise(bool); + + QWidget *window() const; + bool isTool() const; + + QSize sizeHint() const; + QStyleOptionTitleBar getStyleOption() const; + + // These flags are fake, stored in QTitleBarPrivate. They are not set as the widget's window flags. + void setFakeWindowFlags(Qt::WindowFlags f); + Qt::WindowFlags fakeWindowFlags() const; + +public Q_SLOTS: + void setActive(bool); + +Q_SIGNALS: + void doActivate(); + void doNormal(); + void doClose(); + void doMaximize(); + void doMinimize(); + void doShade(); + void showOperationMenu(); + void popupOperationMenu(const QPoint&); + void doubleClicked(); + +protected: + bool event(QEvent *); + void resizeEvent(QResizeEvent *); + void contextMenuEvent(QContextMenuEvent *); + void changeEvent(QEvent *); + void mousePressEvent(QMouseEvent *); + void mouseDoubleClickEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + void paintEvent(QPaintEvent *p); + + virtual void cutText(); + +private: + Q_DISABLE_COPY(Q3TitleBar) +}; + +#endif + +QT_END_NAMESPACE + +#endif //Q3TITLEBAR_P_H diff --git a/src/qt3support/widgets/q3toolbar.cpp b/src/qt3support/widgets/q3toolbar.cpp new file mode 100644 index 0000000..dc0a9fd --- /dev/null +++ b/src/qt3support/widgets/q3toolbar.cpp @@ -0,0 +1,840 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3toolbar.h" +#ifndef QT_NO_TOOLBAR + +#include "q3mainwindow.h" +#include "qapplication.h" +#include "q3combobox.h" +#include "qcursor.h" +#include "qdesktopwidget.h" +#include "qdrawutil.h" +#include "qevent.h" +#include "qframe.h" +#include "qlayout.h" +#include "qmap.h" +#include "qpainter.h" +#include "q3popupmenu.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qtimer.h" +#include "qtoolbutton.h" +#include "qtooltip.h" + +QT_BEGIN_NAMESPACE + +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 Q3ToolBarExtensionWidget; + +class Q3ToolBarPrivate +{ +public: + Q3ToolBarPrivate() : moving(false), checkingExtension(false) { + } + + bool moving; + bool checkingExtension; + Q3ToolBarExtensionWidget *extension; + Q3PopupMenu *extensionPopup; + + QMap<QAction *, QWidget *> actions; +}; + + +class Q3ToolBarSeparator : public QWidget +{ + Q_OBJECT +public: + Q3ToolBarSeparator(Qt::Orientation, Q3ToolBar *parent, const char* name=0); + + QSize sizeHint() const; + Qt::Orientation orientation() const { return orient; } +public slots: + void setOrientation(Qt::Orientation); +protected: + void changeEvent(QEvent *); + void paintEvent(QPaintEvent *); + +private: + Qt::Orientation orient; +}; + +class Q3ToolBarExtensionWidget : public QWidget +{ + Q_OBJECT + +public: + Q3ToolBarExtensionWidget(QWidget *w); + void setOrientation(Qt::Orientation o); + QToolButton *button() const { return tb; } + +protected: + void resizeEvent(QResizeEvent *e) { + QWidget::resizeEvent(e); + layOut(); + } + +private: + void layOut(); + QToolButton *tb; + Qt::Orientation orient; + +}; + +Q3ToolBarExtensionWidget::Q3ToolBarExtensionWidget(QWidget *w) + : QWidget(w, "qt_dockwidget_internal") +{ + tb = new QToolButton(this, "qt_toolbar_ext_button"); + tb->setAutoRaise(true); + setOrientation(Qt::Horizontal); + setAutoFillBackground(true); +} + +void Q3ToolBarExtensionWidget::setOrientation(Qt::Orientation o) +{ + orient = o; + if (orient == Qt::Horizontal) + tb->setIcon(QPixmap((const char **)arrow_h_xpm)); + else + tb->setIcon(QPixmap((const char **)arrow_v_xpm)); + layOut(); +} + +void Q3ToolBarExtensionWidget::layOut() +{ + tb->setGeometry(2, 2, width() - 4, height() - 4); +} + +Q3ToolBarSeparator::Q3ToolBarSeparator(Qt::Orientation o , Q3ToolBar *parent, + const char* name) + : QWidget(parent, name) +{ + connect(parent, SIGNAL(orientationChanged(Qt::Orientation)), + this, SLOT(setOrientation(Qt::Orientation))); + setOrientation(o); + setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); +} + + + +void Q3ToolBarSeparator::setOrientation(Qt::Orientation o) +{ + orient = o; +} + +void Q3ToolBarSeparator::changeEvent(QEvent *ev) +{ + if(ev->type() == QEvent::StyleChange) + setOrientation(orient); + QWidget::changeEvent(ev); +} + +static QStyleOption getStyleOption(const Q3ToolBarSeparator *tbs) +{ + QStyleOption opt(0); + opt.rect = tbs->rect(); + opt.palette = tbs->palette(); + if (tbs->orientation() == Qt::Horizontal) + opt.state = QStyle::State_Horizontal; + else + opt.state = QStyle::State_None; + return opt; +} + +QSize Q3ToolBarSeparator::sizeHint() const +{ + QStyleOption opt = getStyleOption(this); + int extent = style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, &opt, this); + if (orient == Qt::Horizontal) + return QSize(extent, 0); + else + return QSize(0, extent); +} + +void Q3ToolBarSeparator::paintEvent(QPaintEvent *) +{ + QPainter p(this); + QStyleOption opt = getStyleOption(this); + style()->drawPrimitive(QStyle::PE_Q3DockWindowSeparator, &opt, &p, this); +} + +QT_BEGIN_INCLUDE_NAMESPACE +#include "q3toolbar.moc" +QT_END_INCLUDE_NAMESPACE + + +/*! + \class Q3ToolBar + \brief The Q3ToolBar class provides a movable panel containing + widgets such as tool buttons. + + \compat + + A toolbar is a panel that contains a set of controls, usually + represented by small icons. It's purpose is to provide quick + access to frequently used commands or options. Within a + Q3MainWindow the user can drag toolbars within and between the + \link Q3DockArea dock areas\endlink. Toolbars can also be dragged + out of any dock area to float freely as top-level windows. + + Q3ToolBar is a specialization of QDockWindow, and so provides all + the functionality of a QDockWindow. + + To use Q3ToolBar you simply create a Q3ToolBar as a child of a + Q3MainWindow, create a number of QToolButton widgets (or other + widgets) in left to right (or top to bottom) order and call + addSeparator() when you want a separator. When a toolbar is + floated the caption used is the label given in the constructor + call. This can be changed with setLabel(). + + You may use most widgets within a toolbar, with QToolButton and + QComboBox being the most common. But note that the toolbar's + actions must be \l {Q3Action}s. + + If you create a new widget on an already visible Q3ToolBar, this + widget will automatically become visible without needing a show() + call. (This differs from every other Qt widget container. We + recommend calling show() anyway since we hope to fix this anomaly + in a future release.) + + Q3ToolBars, like QDockWindows, are located in \l{Q3DockArea}s or + float as top-level windows. Q3MainWindow provides four Q3DockAreas + (top, left, right and bottom). When you create a new toolbar (as + in the example above) as a child of a Q3MainWindow the toolbar will + be added to the top dock area. You can move it to another dock + area (or float it) by calling Q3MainWindow::moveDockWindow(). Dock + areas lay out their windows in lines. + + If the main window is resized so that the area occupied by the + toolbar is too small to show all its widgets a little arrow button + (which looks like a right-pointing chevron, '»') will appear + at the right or bottom of the toolbar depending on its + orientation. Clicking this button pops up a menu that shows the + 'overflowing' items. QToolButtons are represented in the menu using + their textLabel property, other QAbstractButton subclasses are represented + using their text property, and QComboBoxes are represented as submenus, + with the caption text being used in the submenu item. + + Usually a toolbar will get precisely the space it needs. However, + with setHorizontalStretchable(), setVerticalStretchable() or + setStretchableWidget() you can tell the main window to expand the + toolbar to fill all available space in the specified orientation. + + The toolbar arranges its buttons either horizontally or vertically + (see orientation() for details). Generally, Q3DockArea will set the + orientation correctly for you, but you can set it yourself with + setOrientation() and track any changes by connecting to the + orientationChanged() signal. + + You can use the clear() method to remove all items from a toolbar. + + \img qdockwindow.png Toolbar (dock window) + \caption A floating QToolbar (dock window) + + \sa QToolButton Q3MainWindow +*/ + +/*! + 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. +*/ + +Q3ToolBar::Q3ToolBar(const QString &label, + Q3MainWindow * parent, Qt::ToolBarDock dock, + bool newLine, const char * name) + : Q3DockWindow(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 Q3MainWindow::addDockWindow(). \a name and + the widget flags \a f are passed on to the Q3DockWindow constructor. + + Use this constructor if you want to create torn-off (undocked, + floating) toolbars or toolbars in the \link QStatusBar status + bar\endlink. +*/ + +Q3ToolBar::Q3ToolBar(const QString &label, Q3MainWindow * mainWindow, + QWidget * parent, bool newLine, const char * name, + Qt::WindowFlags f) + : Q3DockWindow(InDock, parent, name, f, true) +{ + mw = mainWindow; + init(); + + setParent(parent); + + if (mainWindow) + mainWindow->addToolBar(this, label, Qt::DockUnmanaged, newLine); +} + + +/*! + \overload + + Constructs an empty toolbar called \a name, with parent \a parent, + in its \a parent's top dock area, without any label and without + requiring a newline. +*/ + +Q3ToolBar::Q3ToolBar(Q3MainWindow * parent, const char * name) + : Q3DockWindow(InDock, parent, name, 0, true) +{ + mw = parent; + init(); + + if (parent) + parent->addToolBar(this, QString(), Qt::DockTop); +} + +/*! + \internal + + Common initialization code. Requires that \c mw and \c o are set. + Does not call Q3MainWindow::addDockWindow(). +*/ +void Q3ToolBar::init() +{ + d = new Q3ToolBarPrivate; + d->extension = 0; + d->extensionPopup = 0; + sw = 0; + + setBackgroundRole(QPalette::Button); + setFocusPolicy(Qt::NoFocus); + setFrameStyle(QFrame::ToolBarPanel | QFrame::Raised); +} + +/*! + Destructor. +*/ + +Q3ToolBar::~Q3ToolBar() +{ + delete d; +} + +/*! + \reimp +*/ + +void Q3ToolBar::setOrientation(Qt::Orientation o) +{ + Q3DockWindow::setOrientation(o); + if (d->extension) + d->extension->setOrientation(o); + QObjectList childList = children(); + for (int i = 0; i < childList.size(); ++i) { + Q3ToolBarSeparator* w = qobject_cast<Q3ToolBarSeparator*>(childList.at(i)); + if (w) + w->setOrientation(o); + } +} + +/*! + Adds a separator to the right/bottom of the toolbar. +*/ + +void Q3ToolBar::addSeparator() +{ + (void) new Q3ToolBarSeparator(orientation(), this, "toolbar separator"); +} + +/*! + \reimp +*/ + +void Q3ToolBar::styleChange(QStyle &oldStyle) +{ + Q3DockWindow::styleChange(oldStyle); +} + + +/*! + \reimp +*/ +void Q3ToolBar::setVisible(bool visible) +{ + Q3DockWindow::setVisible(visible); + if (mw) + mw->triggerLayout(false); + if (visible) + checkForExtension(size()); +} + +/*! + Returns a pointer to the Q3MainWindow which manages this toolbar. +*/ + +Q3MainWindow * Q3ToolBar::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 Q3MainWindow + 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 Q3MainWindow::setRightJustification(), setVerticalStretchable(), + setHorizontalStretchable() +*/ + +void Q3ToolBar::setStretchableWidget(QWidget * w) +{ + sw = w; + boxLayout()->setStretchFactor(w, 1); + + if (!isHorizontalStretchable() && !isVerticalStretchable()) { + if (orientation() == Qt::Horizontal) + setHorizontalStretchable(true); + else + setVerticalStretchable(true); + } +} + + +/*! + \reimp +*/ + +bool Q3ToolBar::event(QEvent * e) +{ + bool r = Q3DockWindow::event(e); + // After the event filters have dealt with it, do our stuff. + if (e->type() == QEvent::ChildInserted) { + QObject * child = ((QChildEvent*)e)->child(); + if (child && child->isWidgetType() && !((QWidget*)child)->isWindow() + && child->parent() == this + && QLatin1String("qt_dockwidget_internal") != child->objectName()) { + boxLayout()->addWidget((QWidget*)child); + QLayoutItem *item = boxLayout()->itemAt(boxLayout()->indexOf((QWidget*)child)); + if (QToolButton *button = qobject_cast<QToolButton*>(child)) { + item->setAlignment(Qt::AlignHCenter); + button->setFocusPolicy(Qt::NoFocus); + if (mw) { + QObject::connect(mw, SIGNAL(pixmapSizeChanged(bool)), + button, SLOT(setUsesBigPixmap(bool))); + button->setUsesBigPixmap(mw->usesBigPixmaps()); + QObject::connect(mw, SIGNAL(usesTextLabelChanged(bool)), + child, SLOT(setUsesTextLabel(bool))); + button->setUsesTextLabel(mw->usesTextLabel()); + } + button->setAutoRaise(true); + } + if (isVisible()) { + // toolbar compatibility: we auto show widgets that + // are not explicitly hidden + if (((QWidget*)child)->testAttribute(Qt::WA_WState_Hidden) + && !((QWidget*)child)->testAttribute(Qt::WA_WState_ExplicitShowHide)) + ((QWidget*)child)->show(); + checkForExtension(size()); + } + } + if (child && child->isWidgetType() && ((QWidget*)child) == sw) + boxLayout()->setStretchFactor((QWidget*)child, 1); + } else if (e->type() == QEvent::Show) { + layout()->activate(); + } else if (e->type() == QEvent::LayoutHint && place() == OutsideDock) { + adjustSize(); + } + return r; +} + + +/*! + \property Q3ToolBar::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 Q3ToolBar::setLabel(const QString & label) +{ + l = label; + setWindowTitle(l); +} + +QString Q3ToolBar::label() const +{ + return l; +} + + +/*! + Deletes all the toolbar's child widgets. +*/ + +void Q3ToolBar::clear() +{ + QObjectList childList = children(); + d->extension = 0; + d->extensionPopup = 0; //they will both be destroyed by the following code + for (int i = 0; i < childList.size(); ++i) { + QObject *obj = childList.at(i); + if (obj->isWidgetType() && QLatin1String("qt_dockwidget_internal") != obj->objectName()) + delete obj; + } +} + +/*! + \internal +*/ + +QSize Q3ToolBar::minimumSize() const +{ + if (orientation() == Qt::Horizontal) + return QSize(0, Q3DockWindow::minimumSize().height()); + return QSize(Q3DockWindow::minimumSize().width(), 0); +} + +/*! + \reimp +*/ + +QSize Q3ToolBar::minimumSizeHint() const +{ + if (orientation() == Qt::Horizontal) + return QSize(0, Q3DockWindow::minimumSizeHint().height()); + return QSize(Q3DockWindow::minimumSizeHint().width(), 0); +} + +void Q3ToolBar::createPopup() +{ + if (!d->extensionPopup) { + d->extensionPopup = new Q3PopupMenu(this, "qt_dockwidget_internal"); + connect(d->extensionPopup, SIGNAL(aboutToShow()), this, SLOT(createPopup())); + } + + if (!d->extension) { + d->extension = new Q3ToolBarExtensionWidget(this); + d->extension->setOrientation(orientation()); + d->extension->button()->setPopup(d->extensionPopup); + d->extension->button()->setPopupMode(QToolButton::InstantPopup); + } + + d->extensionPopup->clear(); + + // delete submenus + QObjectList popups = d->extensionPopup->queryList("Q3PopupMenu", 0, false, true); + while (!popups.isEmpty()) + delete popups.takeFirst(); + + QObjectList childlist = queryList("QWidget", 0, false, true); + bool hide = false; + bool doHide = false; + int id; + for (int i = 0; i < childlist.size(); ++i) { + QObject *obj = childlist.at(i); + if (!obj->isWidgetType() || obj == d->extension->button() || obj == d->extensionPopup + || QLatin1String("qt_dockwidget_internal") == obj->objectName()) { + continue; + } + int j = 2; + QWidget *w = (QWidget*)obj; + if (qobject_cast<Q3ComboBox*>(w)) + j = 1; + hide = false; + + const int padding = 4; // extra pixels added by the layout hierarchy + QPoint p(mapTo(this, w->geometry().bottomRight())); + if (orientation() == Qt::Horizontal) { + if ((p.x() > (doHide ? width() - d->extension->width() / j - padding : width() - padding)) + || (p.x() > parentWidget()->width() - d->extension->width())) + hide = true; + } else { + if ((p.y() > (doHide ? height()- d->extension->height() / j - padding : height() - padding)) + || (p.y() > parentWidget()->height() - d->extension->height())) + hide = true; + } + if (hide && w->isVisible()) { + doHide = true; + if (qobject_cast<QToolButton*>(w)) { + QToolButton *b = (QToolButton*)w; + QString s = b->textLabel(); + if (s.isEmpty()) + s = b->text(); + if (b->popup() && b->popupDelay() == 0) + id = d->extensionPopup->insertItem(b->iconSet(), s, b->popup()); + else + id = d->extensionPopup->insertItem(b->iconSet(), s, b, SLOT(click())) ; + if (b->isToggleButton()) + d->extensionPopup->setItemChecked(id, b->isOn()); + if (!b->isEnabled()) + d->extensionPopup->setItemEnabled(id, false); + } else if (qobject_cast<QAbstractButton*>(w)) { + QAbstractButton *b = (QAbstractButton*)w; + QString s = b->text(); + if (s.isEmpty()) + s = QLatin1String(""); + if (b->pixmap()) + id = d->extensionPopup->insertItem(*b->pixmap(), s, b, SLOT(click())); + else + id = d->extensionPopup->insertItem(s, b, SLOT(click())); + if (b->isToggleButton()) + d->extensionPopup->setItemChecked(id, b->isOn()); + if (!b->isEnabled()) + d->extensionPopup->setItemEnabled(id, false); +#ifndef QT_NO_COMBOBOX + } else if (qobject_cast<Q3ComboBox*>(w)) { + Q3ComboBox *c = (Q3ComboBox*)w; + if (c->count() != 0) { + QString s = c->windowTitle(); + if (s.isEmpty()) + s = c->currentText(); + int maxItems = 0; + Q3PopupMenu *cp = new Q3PopupMenu(d->extensionPopup); + cp->setEnabled(c->isEnabled()); + d->extensionPopup->insertItem(s, cp); + connect(cp, SIGNAL(activated(int)), c, SLOT(internalActivate(int))); + for (int i = 0; i < c->count(); ++i) { + QString tmp = c->text(i); + cp->insertItem(tmp, i); + if (c->currentText() == tmp) + cp->setItemChecked(i, true); + if (!maxItems) { + if (cp->actions().count() == 10) { + int h = cp->sizeHint().height(); + maxItems = QApplication::desktop()->height() * 10 / h; + } + } else if (cp->actions().count() >= maxItems - 1) { + Q3PopupMenu* sp = new Q3PopupMenu(d->extensionPopup); + cp->insertItem(tr("More..."), sp); + cp = sp; + connect(cp, SIGNAL(activated(int)), c, SLOT(internalActivate(int))); + } + } + } +#endif //QT_NO_COMBOBOX + } + } + } +} + +/*! + \reimp +*/ + +void Q3ToolBar::resizeEvent(QResizeEvent *e) +{ + Q3DockWindow::resizeEvent(e); + checkForExtension(e->size()); +} + +/*! + \internal + + This function is called when an action is triggered. The relevant + information is passed in the event \a e. +*/ +void Q3ToolBar::actionEvent(QActionEvent *e) +{ + if (e->type() == QEvent::ActionAdded) { + QAction *a = e->action(); + QWidget *w; + if (a->isSeparator()) { + w = new Q3ToolBarSeparator(orientation(), this, "toolbar separator"); + } else { + QToolButton* btn = new QToolButton(this); + btn->setDefaultAction(a); + w = btn; + } + d->actions.insert(a, w); + } else if (e->type() == QEvent::ActionRemoved) { + QAction *a = e->action(); + delete d->actions.take(a); + } +} + + +void Q3ToolBar::checkForExtension(const QSize &sz) +{ + if (!isVisible()) + return; + + if (d->checkingExtension) + return; + d->checkingExtension = true; + + bool tooSmall; + if (orientation() == Qt::Horizontal) + tooSmall = sz.width() < sizeHint().width(); + else + tooSmall = sz.height() < sizeHint().height(); + + if (tooSmall) { + createPopup(); + if (d->extensionPopup->actions().count()) { + // parentWidget()->width() used since the Q3ToolBar width + // will never be less than minimumSize() + if (orientation() == Qt::Horizontal) + d->extension->setGeometry((parentWidget() ? parentWidget()->width() : width()) - 20, + 1, 20, height() - 2); + else + d->extension->setGeometry(1, (parentWidget() ? parentWidget()->height() : 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; + } + d->checkingExtension = false; +} + + +/*! + \internal +*/ + +void Q3ToolBar::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. +*/ + +QT_END_NAMESPACE + +#endif diff --git a/src/qt3support/widgets/q3toolbar.h b/src/qt3support/widgets/q3toolbar.h new file mode 100644 index 0000000..a1fc471 --- /dev/null +++ b/src/qt3support/widgets/q3toolbar.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3TOOLBAR_H +#define Q3TOOLBAR_H + +#include <Qt3Support/q3dockwindow.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_TOOLBAR + +class Q3MainWindow; +class Q3ToolBarPrivate; + +class Q_COMPAT_EXPORT Q3ToolBar: public Q3DockWindow +{ + Q_OBJECT + Q_PROPERTY(QString label READ label WRITE setLabel) + +public: + Q3ToolBar(const QString &label, + Q3MainWindow *, Qt::ToolBarDock = Qt::DockTop, + bool newLine = false, const char* name=0); + Q3ToolBar(const QString &label, Q3MainWindow *, QWidget *, + bool newLine = false, const char* name=0, Qt::WindowFlags f = 0); + Q3ToolBar(Q3MainWindow* parent=0, const char* name=0); + ~Q3ToolBar(); + + void addSeparator(); + + void setVisible(bool visible); + + Q3MainWindow * mainWindow() const; + + virtual void setStretchableWidget(QWidget *); + + bool event(QEvent * e); + + virtual void setLabel(const QString &); + QString label() const; + + virtual void clear(); + + QSize minimumSize() const; + QSize minimumSizeHint() const; + + void setOrientation(Qt::Orientation o); + void setMinimumSize(int minw, int minh); + +protected: + void resizeEvent(QResizeEvent *e); + void styleChange(QStyle &); + void actionEvent(QActionEvent *); + +private Q_SLOTS: + void createPopup(); + +private: + void init(); + void checkForExtension(const QSize &sz); + Q3ToolBarPrivate * d; + Q3MainWindow * mw; + QWidget * sw; + QString l; + + friend class Q3MainWindow; + friend class Q3DockAreaLayout; + +private: + Q_DISABLE_COPY(Q3ToolBar) +}; + +#endif // QT_NO_TOOLBAR + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3TOOLBAR_H diff --git a/src/qt3support/widgets/q3vbox.cpp b/src/qt3support/widgets/q3vbox.cpp new file mode 100644 index 0000000..7a72051 --- /dev/null +++ b/src/qt3support/widgets/q3vbox.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "q3vbox.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Q3VBox + \brief The Q3VBox widget provides vertical geometry management of + its child widgets. + + \compat + + All its child widgets will be placed vertically and sized + according to their sizeHint()s. + + \img qvbox-m.png Q3VBox + + \sa Q3HBox +*/ + + +/*! + Constructs a vbox widget called \a name with parent \a parent and + widget flags \a f. + */ +Q3VBox::Q3VBox( QWidget *parent, const char *name, Qt::WindowFlags f ) + :Q3HBox( false, parent, name, f ) +{ +} + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3vbox.h b/src/qt3support/widgets/q3vbox.h new file mode 100644 index 0000000..c3b334c --- /dev/null +++ b/src/qt3support/widgets/q3vbox.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3VBOX_H +#define Q3VBOX_H + +#include <Qt3Support/q3hbox.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class Q_COMPAT_EXPORT Q3VBox : public Q3HBox +{ + Q_OBJECT +public: + Q3VBox(QWidget* parent=0, const char* name=0, Qt::WindowFlags f=0); + +private: + Q_DISABLE_COPY(Q3VBox) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3VBOX_H diff --git a/src/qt3support/widgets/q3vgroupbox.cpp b/src/qt3support/widgets/q3vgroupbox.cpp new file mode 100644 index 0000000..b7c58a7 --- /dev/null +++ b/src/qt3support/widgets/q3vgroupbox.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3vgroupbox.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Q3VGroupBox + + \brief The Q3VGroupBox widget organizes widgets in a group with one + vertical column. + + \compat + + Q3VGroupBox is a convenience class that offers a thin layer on top + of Q3GroupBox. Think of it as a Q3VBox that offers a frame with a + title. + + \sa Q3HGroupBox +*/ + +/*! + Constructs a horizontal group box with no title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ +Q3VGroupBox::Q3VGroupBox( QWidget *parent, const char *name ) + : Q3GroupBox( 1, Qt::Horizontal /* sic! */, parent, name ) +{ +} + +/*! + Constructs a horizontal group box with the title \a title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +Q3VGroupBox::Q3VGroupBox( const QString &title, QWidget *parent, + const char *name ) + : Q3GroupBox( 1, Qt::Horizontal /* sic! */, title, parent, name ) +{ +} + +/*! + Destroys the horizontal group box, deleting its child widgets. +*/ +Q3VGroupBox::~Q3VGroupBox() +{ +} + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3vgroupbox.h b/src/qt3support/widgets/q3vgroupbox.h new file mode 100644 index 0000000..a0ec403 --- /dev/null +++ b/src/qt3support/widgets/q3vgroupbox.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3VGROUPBOX_H +#define Q3VGROUPBOX_H + +#include <Qt3Support/q3groupbox.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class Q_COMPAT_EXPORT Q3VGroupBox : public Q3GroupBox +{ + Q_OBJECT +public: + Q3VGroupBox( QWidget* parent=0, const char* name=0 ); + Q3VGroupBox( const QString &title, QWidget* parent=0, const char* name=0 ); + ~Q3VGroupBox(); + +private: + Q_DISABLE_COPY(Q3VGroupBox) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3VGROUPBOX_H diff --git a/src/qt3support/widgets/q3whatsthis.cpp b/src/qt3support/widgets/q3whatsthis.cpp new file mode 100644 index 0000000..6037be7 --- /dev/null +++ b/src/qt3support/widgets/q3whatsthis.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3whatsthis.h" +#ifndef QT_NO_WHATSTHIS +#include "qapplication.h" +#include "qwidget.h" +#include "qevent.h" + +QT_BEGIN_NAMESPACE + +/*! \class Q3WhatsThis + \compat +*/ + +/*! + Constructs a new "What's This?" object for \a widget. +*/ +Q3WhatsThis::Q3WhatsThis(QWidget *widget) + : QObject(widget) +{ + if (widget) + widget->installEventFilter(this); +} + +/*! + Destroys the "What's This?" object. +*/ +Q3WhatsThis::~Q3WhatsThis() +{ +} + +/*! + \internal + + Handles "What's This?" events. +*/ +bool Q3WhatsThis::eventFilter(QObject *o, QEvent *e) +{ + if (o != parent() || !o->isWidgetType()) + return false; + + if (e->type() == QEvent::WhatsThis) { + QString s = text(static_cast<QHelpEvent*>(e)->pos()); + if (!s.isEmpty()) + QWhatsThis::showText(static_cast<QHelpEvent*>(e)->globalPos(), s, static_cast<QWidget*>(o)); + } else if (e->type() == QEvent::QueryWhatsThis) { + QString s = text(static_cast<QHelpEvent*>(e)->pos()); + if (s.isEmpty()) + return false; + } else if (e->type() == QEvent::WhatsThisClicked) { + QString href = static_cast<QWhatsThisClickedEvent*>(e)->href(); + if (clicked(href)) + QWhatsThis::hideText(); + } else { + return false; + } + return true; +} + +/*! + This virtual function returns the text for position \a pos in the + widget that this "What's This?" object documents. If there is no + "What's This?" text for the position, an empty string is returned. + + The default implementation returns an empty string. +*/ +QString Q3WhatsThis::text(const QPoint & /* pos */) +{ + if (parent() && parent()->isWidgetType()) + return static_cast<QWidget*>(parent())->whatsThis(); + return QString(); +} + +/*! + 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 + an empty string 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 Q3WhatsThis::clicked(const QString & /* href */) +{ + return true; +} + +/*! + \fn void Q3WhatsThis::enterWhatsThisMode() + + Enters "What's This?" mode and returns immediately. + + Qt will install a special cursor and take over mouse input until + the user clicks somewhere. It then shows any help available and + ends "What's This?" mode. Finally, Qt removes the special cursor + and help window and then restores ordinary event processing, at + which point the left mouse button is no longer pressed. + + The user can also use the Esc key to leave "What's This?" mode. + + \sa inWhatsThisMode(), leaveWhatsThisMode() +*/ + +/*! + \fn bool Q3WhatsThis::inWhatsThisMode() + + Returns true if the application is in "What's This?" mode; + otherwise returns false. + + \sa enterWhatsThisMode(), leaveWhatsThisMode() +*/ + +/*! + \fn void Q3WhatsThis::add(QWidget *widget, const QString &text) + + Adds \a text as "What's This?" help for \a widget. If the text is + rich text formatted (i.e. it contains markup) it will be rendered + with the default stylesheet QStyleSheet::defaultSheet(). + + The text is destroyed if the widget is later destroyed, so it need + not be explicitly removed. + + \sa remove() +*/ + +/*! + \fn void Q3WhatsThis::remove(QWidget *widget) + + Removes the "What's This?" help associated with the \a widget. + This happens automatically if the widget is destroyed. + + \sa add() +*/ + +/*! + \fn void Q3WhatsThis::leaveWhatsThisMode(const QString& text = QString(), const QPoint& pos = QCursor::pos(), QWidget* widget = 0) + + This function is used internally by widgets that support + QWidget::customWhatsThis(); applications do not usually call it. + An example of such a widget is Q3PopupMenu: menus still work + normally in "What's This?" mode but also provide help texts for + individual menu items. + + If \a text is not empty, a "What's This?" help window is + displayed at the global screen position \a pos. If widget \a widget is + not 0 and has its own dedicated QWhatsThis object, this object + will receive clicked() messages when the user clicks on hyperlinks + inside the help text. + + \sa inWhatsThisMode(), enterWhatsThisMode(), clicked() +*/ + +/*! + \fn void Q3WhatsThis::display(const QString &text, const QPoint &pos, QWidget *widget) + + Display \a text in a help window at the global screen position \a + pos. + + If widget \a widget is not 0 and has its own dedicated QWhatsThis + object, this object will receive clicked() messages when the user + clicks on hyperlinks inside the help text. + + \sa clicked() +*/ + +/*! + Creates a QToolButton preconfigured to enter "What's This?" mode + when clicked. You will often use this with a tool bar as \a + parent: + + \snippet doc/src/snippets/code/src_qt3support_widgets_q3whatsthis.cpp 0 +*/ +QToolButton *Q3WhatsThis::whatsThisButton(QWidget * parent) +{ + return QWhatsThis::whatsThisButton(parent); +} + +QT_END_NAMESPACE + +#endif // QT_NO_WHATSTHIS diff --git a/src/qt3support/widgets/q3whatsthis.h b/src/qt3support/widgets/q3whatsthis.h new file mode 100644 index 0000000..5080786 --- /dev/null +++ b/src/qt3support/widgets/q3whatsthis.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3WHATSTHIS_H +#define Q3WHATSTHIS_H + +#include <QtGui/qcursor.h> +#include <QtGui/qwhatsthis.h> +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +#ifndef QT_NO_WHATSTHIS + +class QToolButton; + +class Q_COMPAT_EXPORT Q3WhatsThis: public QObject +{ + Q_OBJECT +public: + Q3WhatsThis(QWidget *); + ~Q3WhatsThis(); + bool eventFilter(QObject *, QEvent *); + + static inline void enterWhatsThisMode() { QWhatsThis::enterWhatsThisMode(); } + static inline bool inWhatsThisMode() { return QWhatsThis::inWhatsThisMode(); } + + static inline void add(QWidget *w, const QString &s) { w->setWhatsThis(s); } + static inline void remove(QWidget *w) { w->setWhatsThis(QString()); } + static QToolButton * whatsThisButton(QWidget * parent); + static inline void leaveWhatsThisMode(const QString& text = QString(), const QPoint& pos = QCursor::pos(), QWidget* w = 0) + { QWhatsThis::showText(pos, text, w); } + static inline void display(const QString& text, const QPoint& pos = QCursor::pos(), QWidget* w = 0) + { QWhatsThis::showText(pos, text, w); } + + virtual QString text(const QPoint &); + virtual bool clicked(const QString& href); + +}; + +#endif // QT_NO_WHATSTHIS + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3WHATSTHIS_H diff --git a/src/qt3support/widgets/q3widgetstack.cpp b/src/qt3support/widgets/q3widgetstack.cpp new file mode 100644 index 0000000..43263dc --- /dev/null +++ b/src/qt3support/widgets/q3widgetstack.cpp @@ -0,0 +1,571 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "q3widgetstack.h" +#include "qlayout.h" +#include "private/qlayoutengine_p.h" +#include "qapplication.h" +#include "qpainter.h" + +QT_BEGIN_NAMESPACE + +using namespace Qt; + +class Q3WidgetStackPrivate { +public: + class Invisible: public QWidget + { + public: + Invisible(Q3WidgetStack * parent): QWidget(parent, "qt_invisible_widgetstack") + { + setBackgroundMode(NoBackground); + } + const char * className() const + { + return "Q3WidgetStackPrivate::Invisible"; + } + protected: + void paintEvent(QPaintEvent *) + { + QPainter(this).eraseRect(rect()); + } + }; + + int nextNegativeID; + int nextPositiveID; +}; + + + +/*! + \class Q3WidgetStack + \brief The Q3WidgetStack class provides a stack of widgets of which + only the top widget is user-visible. + + \compat + + 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. + + Q3WidgetStack 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 + Q3WidgetStack, not globally), but Q3WidgetStack does not attach any + additional meaning to them. + + The default widget stack is frameless, but you can use the usual + Q3Frame functions (such as setFrameStyle()) to add a frame. + + Q3WidgetStack provides a signal, aboutToShow(), which is emitted + just before a managed widget is shown. + + \sa Q3TabDialog QTabWidget QTabBar Q3Frame +*/ + + +/*! + Constructs an empty widget stack. + + The \a parent, \a name and \a f arguments are passed to the Q3Frame + constructor. +*/ +Q3WidgetStack::Q3WidgetStack(QWidget * parent, const char *name, Qt::WindowFlags f) + : Q3Frame(parent, name, f) //## merge constructors in 4.0 +{ + init(); +} + +void Q3WidgetStack::init() +{ + d = new Q3WidgetStackPrivate(); + d->nextNegativeID = -2; + d->nextPositiveID = 0; + dict = new Q3IntDict<QWidget>; + focusWidgets = 0; + topWidget = 0; + invisible = 0; + invisible = new Q3WidgetStackPrivate::Invisible(this); + invisible->hide(); +} + + +/*! + Destroys the object and frees any allocated resources. +*/ + +Q3WidgetStack::~Q3WidgetStack() +{ + 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 Q3WidgetStack moves it using + reparent(). +*/ + +int Q3WidgetStack::addWidget(QWidget * w, int id) +{ + if (!w || w == invisible || invisible == 0) + return -1; + + // prevent duplicates + removeWidget(w); + + if (id >= 0 && dict->find(id)) + id = -2; + if (id < -1) + id = d->nextNegativeID--; + else if (id == -1) + id = d->nextPositiveID++; + else + d->nextPositiveID = qMax(d->nextPositiveID, id + 1); + // use id >= 0 as-is + + dict->insert(id, w); + + // preserve existing focus + QWidget * f = w->focusWidget(); + while(f && f != w) + f = f->parentWidget(); + if (f) { + if (!focusWidgets) + focusWidgets = new Q3PtrDict<QWidget>(17); + focusWidgets->replace(w, w->focusWidget()); + } + + w->hide(); + if (w->parent() != this) + w->reparent(this, contentsRect().topLeft(), false); + w->setGeometry(contentsRect()); + updateGeometry(); + return id; +} + + +/*! + Removes widget \a w from this stack of widgets. Does not delete \a + w. If \a w is the currently visible widget, no other widget is + substituted. + + \sa visibleWidget() raiseWidget() +*/ + +void Q3WidgetStack::removeWidget(QWidget * w) +{ + int i; + if (!w || (i = id(w)) == -1) + return ; + + 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 Q3WidgetStack::raiseWidget(int id) +{ + if (id == -1) + return; + QWidget * w = dict->find(id); + if (w) + raiseWidget(w); +} + +static bool isChildOf(QWidget* child, QWidget *parent) +{ + if (!child) + return false; + QObjectList list = parent->children(); + for (int i = 0; i < list.size(); ++i) { + QObject *obj = list.at(i); + if (!obj->isWidgetType()) + continue; + QWidget *widget = static_cast<QWidget *>(obj); + if (!widget->isWindow()) + continue; + if (widget == child || isChildOf(child, widget)) + return true; + } + return false; +} + +/*! + \overload + + Raises widget \a w to the top of the widget stack. +*/ + +void Q3WidgetStack::raiseWidget(QWidget *w) +{ + if (!w || w == invisible || w->parent() != this || w == topWidget) + return; + + if (id(w) == -1) + addWidget(w); + if (!isVisible()) { + topWidget = w; + return; + } + + if (w->maximumSize().width() < invisible->width() + || w->maximumSize().height() < invisible->height()) + invisible->setBackgroundMode(backgroundMode()); + else if (invisible->backgroundMode() != NoBackground) + invisible->setBackgroundMode(NoBackground); + + if (invisible->isHidden()) { + invisible->setGeometry(contentsRect()); + invisible->lower(); + invisible->show(); + QApplication::sendPostedEvents(invisible, QEvent::ShowWindowRequest); + } + + // try to move focus onto the incoming widget if focus + // was somewhere on the outgoing widget. + if (topWidget) { + QWidget * fw = window()->focusWidget(); + if (topWidget->isAncestorOf(fw)) { // focus was on old page + // look for the best focus widget we can find + QWidget *p = w->focusWidget(); + if (!p) { + // second best == first child widget in the focus chain + QWidget *i = fw; + while ((i = i->nextInFocusChain()) != fw) { + if (((i->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) + && !i->focusProxy() && i->isVisibleTo(w) && i->isEnabled() + && w->isAncestorOf(i)) { + p = i; + break; + } + } + } + if (p) + p->setFocus(); + } else { + // the focus wasn't on the old page, so we have to ensure focus doesn't go to + // the widget in the page that last had focus when we show the page again. + QWidget *oldfw = topWidget->focusWidget(); + if (oldfw) + oldfw->clearFocus(); + } + } + + if (isVisible()) { + emit aboutToShow(w); + int i = id(w); + if (i != -1) + emit aboutToShow(i); + } + + topWidget = w; + + QObjectList c = children(); + for (int i = 0; i < c.size(); ++i) { + QObject * o = c.at(i); + if (o->isWidgetType() && o != w && o != invisible) + static_cast<QWidget *>(o)->hide(); + } + + w->setGeometry(invisible->geometry()); + w->show(); +} + +/*! + \reimp +*/ + +void Q3WidgetStack::frameChanged() +{ + Q3Frame::frameChanged(); + setChildGeometries(); +} + + +/*! + \internal +*/ + +void Q3WidgetStack::setFrameRect(const QRect & r) +{ + // ### this function used to be virtual in QFrame in Qt 3; it is no longer virtual in Qt 4 + Q3Frame::setFrameRect(r); + setChildGeometries(); +} + + +/*! + Fixes up the children's geometries. +*/ + +void Q3WidgetStack::setChildGeometries() +{ + invisible->setGeometry(contentsRect()); + if (topWidget) + topWidget->setGeometry(invisible->geometry()); +} + + +/*! + \reimp +*/ +void Q3WidgetStack::setVisible(bool visible) +{ + if (visible) { + // Reimplemented in order to set the children's geometries + // appropriately and to pick the first widget as d->topWidget if no + // topwidget was defined + QObjectList c = children(); + if (!isVisible() && !c.isEmpty()) { + for (int i = 0; i < c.size(); ++i) { + QObject * o = c.at(i); + if (o->isWidgetType()) { + if (!topWidget && o != invisible) + topWidget = static_cast<QWidget*>(o); + if (o == topWidget) + static_cast<QWidget *>(o)->show(); + else + static_cast<QWidget *>(o)->hide(); + } + } + setChildGeometries(); + } + } + Q3Frame::setVisible(visible); +} + + +/*! + Returns the widget with ID \a id. Returns 0 if this widget stack + does not manage a widget with ID \a id. + + \sa id() addWidget() +*/ + +QWidget * Q3WidgetStack::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 Q3WidgetStack::id(QWidget * widget) const +{ + if (!widget) + return -1; + + Q3IntDictIterator<QWidget> it(*dict); + while (it.current() && it.current() != widget) + ++it; + return it.current() == widget ? it.currentKey() : -1; +} + + +/*! + Returns the currently visible widget (the one at the top of the + stack), or 0 if nothing is currently being shown. + + \sa aboutToShow() id() raiseWidget() +*/ + +QWidget * Q3WidgetStack::visibleWidget() const +{ + return topWidget; +} + + +/*! + \fn void Q3WidgetStack::aboutToShow(int id) + + This signal is emitted just before a managed widget is shown if + that managed widget has an ID != -1. The \a id parameter 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 Q3WidgetStack::aboutToShow(QWidget *widget) + + \overload + + This signal is emitted just before a managed widget is shown. The + argument is a pointer to the \a 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 Q3WidgetStack::resizeEvent(QResizeEvent * e) +{ + Q3Frame::resizeEvent(e); + setChildGeometries(); +} + + +/*! + \reimp +*/ + +QSize Q3WidgetStack::sizeHint() const +{ + constPolish(); + + QSize size(0, 0); + + Q3IntDictIterator<QWidget> it(*dict); + QWidget *w; + + while ((w = it.current()) != 0) { + ++it; + QSize sh = w->sizeHint(); + if (w->sizePolicy().horData() == QSizePolicy::Ignored) + sh.rwidth() = 0; + if (w->sizePolicy().verData() == QSizePolicy::Ignored) + sh.rheight() = 0; +#ifndef QT_NO_LAYOUT + size = size.expandedTo(sh).expandedTo(qSmartMinSize(w)); +#endif + } + if (size.isNull()) + size = QSize(128, 64); + size += QSize(2*frameWidth(), 2*frameWidth()); + return size; +} + + +/*! + \reimp +*/ +QSize Q3WidgetStack::minimumSizeHint() const +{ + constPolish(); + + QSize size(0, 0); + + Q3IntDictIterator<QWidget> it(*dict); + QWidget *w; + + while ((w = it.current()) != 0) { + ++it; + QSize sh = w->minimumSizeHint(); + if (w->sizePolicy().horData() == QSizePolicy::Ignored) + sh.rwidth() = 0; + if (w->sizePolicy().verData() == QSizePolicy::Ignored) + sh.rheight() = 0; +#ifndef QT_NO_LAYOUT + size = size.expandedTo(sh).expandedTo(w->minimumSize()); +#endif + } + if (size.isNull()) + size = QSize(64, 32); + size += QSize(2*frameWidth(), 2*frameWidth()); + return size; +} + +/*! + \reimp +*/ +void Q3WidgetStack::childEvent(QChildEvent *e) +{ + if (e->child()->isWidgetType() && e->removed()) + removeWidget((QWidget *) e->child()); +} + + +/*! + \reimp +*/ +bool Q3WidgetStack::event(QEvent* e) +{ + if (e->type() == QEvent::LayoutRequest || e->type() == QEvent::LayoutHint ) + updateGeometry(); // propgate layout hints to parent + return Q3Frame::event(e); +} + +QT_END_NAMESPACE diff --git a/src/qt3support/widgets/q3widgetstack.h b/src/qt3support/widgets/q3widgetstack.h new file mode 100644 index 0000000..e63830f --- /dev/null +++ b/src/qt3support/widgets/q3widgetstack.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt3Support module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q3WIDGETSTACK_H +#define Q3WIDGETSTACK_H + +#include <Qt3Support/q3frame.h> +#include <Qt3Support/q3intdict.h> +#include <Qt3Support/q3ptrdict.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Qt3SupportLight) + +class Q3WidgetStackPrivate; + + +class Q_COMPAT_EXPORT Q3WidgetStack: public Q3Frame +{ + Q_OBJECT +public: + Q3WidgetStack(QWidget* parent, const char* name=0, Qt::WindowFlags f=0); + + ~Q3WidgetStack(); + + int addWidget(QWidget *, int = -1); + void removeWidget(QWidget *); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + void setVisible(bool visible); + + QWidget * widget(int) const; + int id(QWidget *) const; + + QWidget * visibleWidget() const; + + void setFrameRect(const QRect &); + +Q_SIGNALS: + void aboutToShow(int); + void aboutToShow(QWidget *); + +public Q_SLOTS: + void raiseWidget(int); + void raiseWidget(QWidget *); + +protected: + void frameChanged(); + void resizeEvent(QResizeEvent *); + bool event(QEvent* e); + + virtual void setChildGeometries(); + void childEvent(QChildEvent *); + +private: + void init(); + + Q3WidgetStackPrivate * d; + Q3IntDict<QWidget> * dict; + Q3PtrDict<QWidget> * focusWidgets; + QWidget * topWidget; + QWidget * invisible; + + Q_DISABLE_COPY(Q3WidgetStack) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // Q3WIDGETSTACK_H diff --git a/src/qt3support/widgets/widgets.pri b/src/qt3support/widgets/widgets.pri new file mode 100644 index 0000000..9bd6fb4 --- /dev/null +++ b/src/qt3support/widgets/widgets.pri @@ -0,0 +1,58 @@ +# Qt compat module + +HEADERS += \ + widgets/q3action.h \ + widgets/q3buttongroup.h \ + widgets/q3datetimeedit.h \ + widgets/q3dockarea.h \ + widgets/q3dockwindow.h \ + widgets/q3frame.h \ + widgets/q3vbox.h \ + widgets/q3vgroupbox.h \ + widgets/q3hbox.h \ + widgets/q3hgroupbox.h \ + widgets/q3grid.h \ + widgets/q3gridview.h \ + widgets/q3groupbox.h \ + widgets/q3header.h \ + widgets/q3mainwindow.h \ + widgets/q3mainwindow_p.h \ + widgets/q3progressbar.h \ + widgets/q3scrollview.h \ + widgets/q3titlebar_p.h \ + widgets/q3toolbar.h \ + widgets/q3whatsthis.h \ + widgets/q3widgetstack.h \ + widgets/q3button.h \ + widgets/q3rangecontrol.h \ + widgets/q3popupmenu.h \ + widgets/q3combobox.h + +SOURCES += \ + widgets/q3action.cpp \ + widgets/q3buttongroup.cpp \ + widgets/q3datetimeedit.cpp \ + widgets/q3dockarea.cpp \ + widgets/q3dockwindow.cpp \ + widgets/q3frame.cpp \ + widgets/q3vbox.cpp \ + widgets/q3vgroupbox.cpp \ + widgets/q3hbox.cpp \ + widgets/q3hgroupbox.cpp \ + widgets/q3grid.cpp \ + widgets/q3gridview.cpp \ + widgets/q3groupbox.cpp \ + widgets/q3header.cpp \ + widgets/q3mainwindow.cpp \ + widgets/q3progressbar.cpp \ + widgets/q3scrollview.cpp \ + widgets/q3titlebar.cpp \ + widgets/q3toolbar.cpp \ + widgets/q3whatsthis.cpp \ + widgets/q3widgetstack.cpp \ + widgets/q3button.cpp \ + widgets/q3rangecontrol.cpp \ + widgets/q3spinwidget.cpp \ + widgets/q3popupmenu.cpp \ + widgets/q3combobox.cpp + |