diff options
Diffstat (limited to 'src/qt3support/widgets/q3widgetstack.cpp')
-rw-r--r-- | src/qt3support/widgets/q3widgetstack.cpp | 571 |
1 files changed, 571 insertions, 0 deletions
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 |