/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the Qt3Support module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** 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.1, 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. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $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 |