diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:34:13 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:34:13 (GMT) |
commit | 67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch) | |
tree | 1dbf50b3dff8d5ca7e9344733968c72704eb15ff /src/gui/widgets/qabstractscrollarea.cpp | |
download | Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.zip Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.bz2 |
Long live Qt!
Diffstat (limited to 'src/gui/widgets/qabstractscrollarea.cpp')
-rw-r--r-- | src/gui/widgets/qabstractscrollarea.cpp | 1303 |
1 files changed, 1303 insertions, 0 deletions
diff --git a/src/gui/widgets/qabstractscrollarea.cpp b/src/gui/widgets/qabstractscrollarea.cpp new file mode 100644 index 0000000..9886969 --- /dev/null +++ b/src/gui/widgets/qabstractscrollarea.cpp @@ -0,0 +1,1303 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui 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 "qabstractscrollarea.h" + +#ifndef QT_NO_SCROLLAREA + +#include "qscrollbar.h" +#include "qapplication.h" +#include "qstyle.h" +#include "qstyleoption.h" +#include "qevent.h" +#include "qdebug.h" +#include "qboxlayout.h" +#include "qpainter.h" + +#include "qabstractscrollarea_p.h" +#include <qwidget.h> + +#ifdef Q_WS_MAC +#include <private/qt_mac_p.h> +#include <private/qt_cocoa_helpers_mac_p.h> +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractScrollArea + \brief The QAbstractScrollArea widget provides a scrolling area with + on-demand scroll bars. + + \ingroup abstractwidgets + + QAbstractScrollArea is a low-level abstraction of a scrolling + area. The area provides a central widget called the viewport, in + which the contents of the area is to be scrolled (i.e, the + visible parts of the contents are rendered in the viewport). + + Next to the viewport is a vertical scroll bar, and below is a + horizontal scroll bar. When all of the area contents fits in the + viewport, each scroll bar can be either visible or hidden + depending on the scroll bar's Qt::ScrollBarPolicy. When a scroll + bar is hidden, the viewport expands in order to cover all + available space. When a scroll bar becomes visible again, the + viewport shrinks in order to make room for the scroll bar. + + It is possible to reserve a margin area around the viewport, see + setViewportMargins(). The feature is mostly used to place a + QHeaderView widget above or beside the scrolling area. Subclasses + of QAbstractScrollArea should implement margins. + + When inheriting QAbstractScrollArea, you need to do the + following: + + \list + \o Control the scroll bars by setting their + range, value, page step, and tracking their + movements. + \o Draw the contents of the area in the viewport according + to the values of the scroll bars. + \o Handle events received by the viewport in + viewportEvent() - notably resize events. + \o Use \c{viewport->update()} to update the contents of the + viewport instead of \l{QWidget::update()}{update()} + as all painting operations take place on the viewport. + \endlist + + With a scroll bar policy of Qt::ScrollBarAsNeeded (the default), + QAbstractScrollArea shows scroll bars when they provide a non-zero + scrolling range, and hides them otherwise. + + The scroll bars and viewport should be updated whenever the viewport + receives a resize event or the size of the contents changes. + The viewport also needs to be updated when the scroll bars + values change. The initial values of the scroll bars are often + set when the area receives new contents. + + We give a simple example, in which we have implemented a scroll area + that can scroll any QWidget. We make the widget a child of the + viewport; this way, we do not have to calculate which part of + the widget to draw but can simply move the widget with + QWidget::move(). When the area contents or the viewport size + changes, we do the following: + + \snippet doc/src/snippets/myscrollarea.cpp 1 + + When the scroll bars change value, we need to update the widget + position, i.e., find the part of the widget that is to be drawn in + the viewport: + + \snippet doc/src/snippets/myscrollarea.cpp 0 + + In order to track scroll bar movements, reimplement the virtual + function scrollContentsBy(). In order to fine-tune scrolling + behavior, connect to a scroll bar's + QAbstractSlider::actionTriggered() signal and adjust the \l + QAbstractSlider::sliderPosition as you wish. + + For convenience, QAbstractScrollArea makes all viewport events + available in the virtual viewportEvent() handler. QWidget's + specialized handlers are remapped to viewport events in the cases + where this makes sense. The remapped specialized handlers are: + paintEvent(), mousePressEvent(), mouseReleaseEvent(), + mouseDoubleClickEvent(), mouseMoveEvent(), wheelEvent(), + dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(), dropEvent(), + contextMenuEvent(), and resizeEvent(). + + QScrollArea, which inherits QAbstractScrollArea, provides smooth + scrolling for any QWidget (i.e., the widget is scrolled pixel by + pixel). You only need to subclass QAbstractScrollArea if you need + more specialized behavior. This is, for instance, true if the + entire contents of the area is not suitable for being drawn on a + QWidget or if you do not want smooth scrolling. + + \sa QScrollArea +*/ + +QAbstractScrollAreaPrivate::QAbstractScrollAreaPrivate() + :hbar(0), vbar(0), vbarpolicy(Qt::ScrollBarAsNeeded), hbarpolicy(Qt::ScrollBarAsNeeded), + viewport(0), cornerWidget(0), left(0), top(0), right(0), bottom(0), + xoffset(0), yoffset(0), viewportFilter(0) +{ +} + +QAbstractScrollAreaScrollBarContainer::QAbstractScrollAreaScrollBarContainer(Qt::Orientation orientation, QWidget *parent) + :QWidget(parent), scrollBar(new QScrollBar(orientation, this)), + layout(new QBoxLayout(orientation == Qt::Horizontal ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom)), + orientation(orientation) +{ + setLayout(layout); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(scrollBar); +} + +/*! \internal + Adds a widget to the scroll bar container. +*/ +void QAbstractScrollAreaScrollBarContainer::addWidget(QWidget *widget, LogicalPosition position) +{ + QSizePolicy policy = widget->sizePolicy(); + if (orientation == Qt::Vertical) + policy.setHorizontalPolicy(QSizePolicy::Ignored); + else + policy.setVerticalPolicy(QSizePolicy::Ignored); + widget->setSizePolicy(policy); + widget->setParent(this); + + const int insertIndex = (position & LogicalLeft) ? 0 : scrollBarLayoutIndex() + 1; + layout->insertWidget(insertIndex, widget); +} + +/*! \internal + Retuns a list of scroll bar widgets for the given position. The scroll bar + itself is not returned. +*/ +QWidgetList QAbstractScrollAreaScrollBarContainer::widgets(LogicalPosition position) +{ + QWidgetList list; + const int scrollBarIndex = scrollBarLayoutIndex(); + if (position == LogicalLeft) { + for (int i = 0; i < scrollBarIndex; ++i) + list.append(layout->itemAt(i)->widget()); + } else if (position == LogicalRight) { + const int layoutItemCount = layout->count(); + for (int i = scrollBarIndex + 1; i < layoutItemCount; ++i) + list.append(layout->itemAt(i)->widget()); + } + return list; +} + +/*! \internal + Returns the layout index for the scroll bar. This needs to be + recalculated by a linear search for each use, since items in + the layout can be removed at any time (i.e. when a widget is + deleted or re-parented). +*/ +int QAbstractScrollAreaScrollBarContainer::scrollBarLayoutIndex() const +{ + const int layoutItemCount = layout->count(); + for (int i = 0; i < layoutItemCount; ++i) { + if (qobject_cast<QScrollBar *>(layout->itemAt(i)->widget())) + return i; + } + return -1; +} + +/*! \internal +*/ +void QAbstractScrollAreaPrivate::replaceScrollBar(QScrollBar *scrollBar, + Qt::Orientation orientation) +{ + Q_Q(QAbstractScrollArea); + + QAbstractScrollAreaScrollBarContainer *container = scrollBarContainers[orientation]; + bool horizontal = (orientation == Qt::Horizontal); + QScrollBar *oldBar = horizontal ? hbar : vbar; + if (horizontal) + hbar = scrollBar; + else + vbar = scrollBar; + scrollBar->setParent(container); + container->scrollBar = scrollBar; + container->layout->removeWidget(oldBar); + container->layout->insertWidget(0, scrollBar); + scrollBar->setVisible(oldBar->isVisibleTo(container)); + scrollBar->setInvertedAppearance(oldBar->invertedAppearance()); + scrollBar->setInvertedControls(oldBar->invertedControls()); + scrollBar->setRange(oldBar->minimum(), oldBar->maximum()); + scrollBar->setOrientation(oldBar->orientation()); + scrollBar->setPageStep(oldBar->pageStep()); + scrollBar->setSingleStep(oldBar->singleStep()); + scrollBar->setSliderDown(oldBar->isSliderDown()); + scrollBar->setSliderPosition(oldBar->sliderPosition()); + scrollBar->setTracking(oldBar->hasTracking()); + scrollBar->setValue(oldBar->value()); + delete oldBar; + + QObject::connect(scrollBar, SIGNAL(valueChanged(int)), + q, horizontal ? SLOT(_q_hslide(int)) : SLOT(_q_vslide(int))); + QObject::connect(scrollBar, SIGNAL(rangeChanged(int,int)), + q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection); +} + +void QAbstractScrollAreaPrivate::init() +{ + Q_Q(QAbstractScrollArea); + viewport = new QWidget(q); + viewport->setObjectName(QLatin1String("qt_scrollarea_viewport")); + viewport->setBackgroundRole(QPalette::Base); + viewport->setAutoFillBackground(true); + scrollBarContainers[Qt::Horizontal] = new QAbstractScrollAreaScrollBarContainer(Qt::Horizontal, q); + scrollBarContainers[Qt::Horizontal]->setObjectName(QLatin1String("qt_scrollarea_hcontainer")); + hbar = scrollBarContainers[Qt::Horizontal]->scrollBar; + hbar->setRange(0,0); + scrollBarContainers[Qt::Horizontal]->setVisible(false); + QObject::connect(hbar, SIGNAL(valueChanged(int)), q, SLOT(_q_hslide(int))); + QObject::connect(hbar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection); + scrollBarContainers[Qt::Vertical] = new QAbstractScrollAreaScrollBarContainer(Qt::Vertical, q); + scrollBarContainers[Qt::Vertical]->setObjectName(QLatin1String("qt_scrollarea_vcontainer")); + vbar = scrollBarContainers[Qt::Vertical]->scrollBar; + vbar->setRange(0,0); + scrollBarContainers[Qt::Vertical]->setVisible(false); + QObject::connect(vbar, SIGNAL(valueChanged(int)), q, SLOT(_q_vslide(int))); + QObject::connect(vbar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection); + viewportFilter = new QAbstractScrollAreaFilter(this); + viewport->installEventFilter(viewportFilter); + viewport->setFocusProxy(q); + q->setFocusPolicy(Qt::WheelFocus); + q->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layoutChildren(); +} + +void QAbstractScrollAreaPrivate::layoutChildren() +{ + Q_Q(QAbstractScrollArea); + bool needh = (hbarpolicy == Qt::ScrollBarAlwaysOn + || (hbarpolicy == Qt::ScrollBarAsNeeded && hbar->minimum() < hbar->maximum())); + + bool needv = (vbarpolicy == Qt::ScrollBarAlwaysOn + || (vbarpolicy == Qt::ScrollBarAsNeeded && vbar->minimum() < vbar->maximum())); + +#ifdef Q_WS_MAC + QWidget * const window = q->window(); + + // Use small scroll bars for tool windows, to match the native size grip. + bool hbarIsSmall = hbar->testAttribute(Qt::WA_MacSmallSize); + bool vbarIsSmall = vbar->testAttribute(Qt::WA_MacSmallSize); + const Qt::WindowType windowType = window->windowType(); + if (windowType == Qt::Tool) { + if (!hbarIsSmall) { + hbar->setAttribute(Qt::WA_MacMiniSize, false); + hbar->setAttribute(Qt::WA_MacNormalSize, false); + hbar->setAttribute(Qt::WA_MacSmallSize, true); + } + if (!vbarIsSmall) { + vbar->setAttribute(Qt::WA_MacMiniSize, false); + vbar->setAttribute(Qt::WA_MacNormalSize, false); + vbar->setAttribute(Qt::WA_MacSmallSize, true); + } + } else { + if (hbarIsSmall) { + hbar->setAttribute(Qt::WA_MacMiniSize, false); + hbar->setAttribute(Qt::WA_MacNormalSize, false); + hbar->setAttribute(Qt::WA_MacSmallSize, false); + } + if (vbarIsSmall) { + vbar->setAttribute(Qt::WA_MacMiniSize, false); + vbar->setAttribute(Qt::WA_MacNormalSize, false); + vbar->setAttribute(Qt::WA_MacSmallSize, false); + } + } +#endif + + const int hsbExt = hbar->sizeHint().height(); + const int vsbExt = vbar->sizeHint().width(); + const QPoint extPoint(vsbExt, hsbExt); + const QSize extSize(vsbExt, hsbExt); + + const QRect widgetRect = q->rect(); + QStyleOption opt(0); + opt.init(q); + + const bool hasCornerWidget = (cornerWidget != 0); + +// If the scroll bars are at the very right and bottom of the window we +// move their positions to be aligned with the size grip. +#ifdef Q_WS_MAC + // Check if a native sizegrip is present. + bool hasMacReverseSizeGrip = false; + bool hasMacSizeGrip = false; + bool nativeGripPresent = false; + if (q->testAttribute(Qt::WA_WState_Created)) + nativeGripPresent = qt_mac_checkForNativeSizeGrip(q); + + if (nativeGripPresent) { + // Look for a native size grip at the visual window bottom right and at the + // absolute window bottom right. In reverse mode, the native size grip does not + // swich side, so we need to check if it is on the "wrong side". + const QPoint scrollAreaBottomRight = q->mapTo(window, widgetRect.bottomRight() - QPoint(frameWidth, frameWidth)); + const QPoint windowBottomRight = window->rect().bottomRight(); + const QPoint visualWindowBottomRight = QStyle::visualPos(opt.direction, opt.rect, windowBottomRight); + const QPoint offset = windowBottomRight - scrollAreaBottomRight; + const QPoint visualOffset = visualWindowBottomRight - scrollAreaBottomRight; + hasMacSizeGrip = (visualOffset.manhattanLength() < vsbExt); + hasMacReverseSizeGrip = (hasMacSizeGrip == false && (offset.manhattanLength() < hsbExt)); + } +#endif + + QPoint cornerOffset(needv ? vsbExt : 0, needh ? hsbExt : 0); + QRect controlsRect; + QRect viewportRect; + + // In FrameOnlyAroundContents mode the frame is drawn between the controls and + // the viewport, else the frame rect is equal to the widget rect. + if ((frameStyle != QFrame::NoFrame) && + q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, q)) { + controlsRect = widgetRect; + const int extra = q->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing); + const QPoint cornerExtra(needv ? extra : 0, needh ? extra : 0); + QRect frameRect = widgetRect; + frameRect.adjust(0, 0, -cornerOffset.x() - cornerExtra.x(), -cornerOffset.y() - cornerExtra.y()); + q->setFrameRect(QStyle::visualRect(opt.direction, opt.rect, frameRect)); + // The frame rect needs to be in logical coords, however we need to flip + // the contentsRect back before passing it on to the viewportRect + // since the viewportRect has its logical coords calculated later. + viewportRect = QStyle::visualRect(opt.direction, opt.rect, q->contentsRect()); + } else { + q->setFrameRect(QStyle::visualRect(opt.direction, opt.rect, widgetRect)); + controlsRect = q->contentsRect(); + viewportRect = QRect(controlsRect.topLeft(), controlsRect.bottomRight() - cornerOffset); + } + + // If we have a corner widget and are only showing one scroll bar, we need to move it + // to make room for the corner widget. + if (hasCornerWidget && (needv || needh)) + cornerOffset = extPoint; + +#ifdef Q_WS_MAC + // Also move the scroll bars if they are covered by the native Mac size grip. + if (hasMacSizeGrip) + cornerOffset = extPoint; +#endif + + // The corner point is where the scroll bar rects, the corner widget rect and the + // viewport rect meets. + const QPoint cornerPoint(controlsRect.bottomRight() + QPoint(1, 1) - cornerOffset); + + // Some styles paints the corner if both scorllbars are showing and there is + // no corner widget. Also, on the Mac we paint if there is a native + // (transparent) sizegrip in the area where a corner widget would be. + if ((needv && needh && hasCornerWidget == false) + || ((needv || needh) +#ifdef Q_WS_MAC + && hasMacSizeGrip +#endif + ) + ) { + cornerPaintingRect = QStyle::visualRect(opt.direction, opt.rect, QRect(cornerPoint, extSize)); + } else { + cornerPaintingRect = QRect(); + } + +#ifdef Q_WS_MAC + if (hasMacReverseSizeGrip) + reverseCornerPaintingRect = QRect(controlsRect.bottomRight() + QPoint(1, 1) - extPoint, extSize); + else + reverseCornerPaintingRect = QRect(); +#endif + + if (needh) { + QRect horizontalScrollBarRect(QPoint(controlsRect.left(), cornerPoint.y()), QPoint(cornerPoint.x() - 1, controlsRect.bottom())); +#ifdef Q_WS_MAC + if (hasMacReverseSizeGrip) + horizontalScrollBarRect.adjust(vsbExt, 0, 0, 0); +#endif + scrollBarContainers[Qt::Horizontal]->setGeometry(QStyle::visualRect(opt.direction, opt.rect, horizontalScrollBarRect)); + scrollBarContainers[Qt::Horizontal]->raise(); + } + + if (needv) { + const QRect verticalScrollBarRect (QPoint(cornerPoint.x(), controlsRect.top()), QPoint(controlsRect.right(), cornerPoint.y() - 1)); + scrollBarContainers[Qt::Vertical]->setGeometry(QStyle::visualRect(opt.direction, opt.rect, verticalScrollBarRect)); + scrollBarContainers[Qt::Vertical]->raise(); + } + + if (cornerWidget) { + const QRect cornerWidgetRect(cornerPoint, controlsRect.bottomRight()); + cornerWidget->setGeometry(QStyle::visualRect(opt.direction, opt.rect, cornerWidgetRect)); + } + + scrollBarContainers[Qt::Horizontal]->setVisible(needh); + scrollBarContainers[Qt::Vertical]->setVisible(needv); + + if (q->isRightToLeft()) + viewportRect.adjust(right, top, -left, -bottom); + else + viewportRect.adjust(left, top, -right, -bottom); + + viewport->setGeometry(QStyle::visualRect(opt.direction, opt.rect, viewportRect)); // resize the viewport last +} + +// ### Fix for 4.4, talk to Bjoern E or Girish. +void QAbstractScrollAreaPrivate::scrollBarPolicyChanged(Qt::Orientation, Qt::ScrollBarPolicy) {} + +/*! + \internal + + Creates a new QAbstractScrollAreaPrivate, \a dd with the given \a parent. +*/ +QAbstractScrollArea::QAbstractScrollArea(QAbstractScrollAreaPrivate &dd, QWidget *parent) + :QFrame(dd, parent) +{ + Q_D(QAbstractScrollArea); + d->init(); +} + +/*! + Constructs a viewport. + + The \a parent arguments is sent to the QWidget constructor. +*/ +QAbstractScrollArea::QAbstractScrollArea(QWidget *parent) + :QFrame(*new QAbstractScrollAreaPrivate, parent) +{ + Q_D(QAbstractScrollArea); + d->init(); +} + + +/*! + Destroys the viewport. + */ +QAbstractScrollArea::~QAbstractScrollArea() +{ + Q_D(QAbstractScrollArea); + delete d->viewportFilter; +} + + +/*! + \since 4.2 + Sets the viewport to be the given \a widget. + The QAbstractScrollArea will take ownership of the given \a widget. + + If \a widget is 0, QAbstractScrollArea will assign a new QWidget instance + for the viewport. + + \sa viewport() +*/ +void QAbstractScrollArea::setViewport(QWidget *widget) +{ + Q_D(QAbstractScrollArea); + if (widget != d->viewport) { + QWidget *oldViewport = d->viewport; + if (!widget) + widget = new QWidget; + d->viewport = widget; + d->viewport->setParent(this); + d->viewport->setFocusProxy(this); + d->viewport->installEventFilter(d->viewportFilter); + d->layoutChildren(); + if (isVisible()) + d->viewport->show(); + QMetaObject::invokeMethod(this, "setupViewport", Q_ARG(QWidget *, widget)); + delete oldViewport; + } +} + +/*! + Returns the viewport widget. + + Use the QScrollArea::widget() function to retrieve the contents of + the viewport widget. + + \sa QScrollArea::widget() +*/ +QWidget *QAbstractScrollArea::viewport() const +{ + Q_D(const QAbstractScrollArea); + return d->viewport; +} + + +/*! +Returns the size of the viewport as if the scroll bars had no valid +scrolling range. +*/ +// ### still thinking about the name +QSize QAbstractScrollArea::maximumViewportSize() const +{ + Q_D(const QAbstractScrollArea); + int hsbExt = d->hbar->sizeHint().height(); + int vsbExt = d->vbar->sizeHint().width(); + + int f = 2 * d->frameWidth; + QSize max = size() - QSize(f + d->left + d->right, f + d->top + d->bottom); + if (d->vbarpolicy == Qt::ScrollBarAlwaysOn) + max.rwidth() -= vsbExt; + if (d->hbarpolicy == Qt::ScrollBarAlwaysOn) + max.rheight() -= hsbExt; + return max; +} + +/*! + \property QAbstractScrollArea::verticalScrollBarPolicy + \brief the policy for the vertical scroll bar + + The default policy is Qt::ScrollBarAsNeeded. + + \sa horizontalScrollBarPolicy +*/ + +Qt::ScrollBarPolicy QAbstractScrollArea::verticalScrollBarPolicy() const +{ + Q_D(const QAbstractScrollArea); + return d->vbarpolicy; +} + +void QAbstractScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy) +{ + Q_D(QAbstractScrollArea); + const Qt::ScrollBarPolicy oldPolicy = d->vbarpolicy; + d->vbarpolicy = policy; + if (isVisible()) + d->layoutChildren(); + if (oldPolicy != d->vbarpolicy) + d->scrollBarPolicyChanged(Qt::Vertical, d->vbarpolicy); +} + + +/*! + Returns the vertical scroll bar. + + \sa verticalScrollBarPolicy, horizontalScrollBar() + */ +QScrollBar *QAbstractScrollArea::verticalScrollBar() const +{ + Q_D(const QAbstractScrollArea); + return d->vbar; +} + +/*! + \since 4.2 + Replaces the existing vertical scroll bar with \a scrollBar, and sets all + the former scroll bar's slider properties on the new scroll bar. The former + scroll bar is then deleted. + + QAbstractScrollArea already provides vertical and horizontal scroll bars by + default. You can call this function to replace the default vertical + scroll bar with your own custom scroll bar. + + \sa verticalScrollBar(), setHorizontalScrollBar() +*/ +void QAbstractScrollArea::setVerticalScrollBar(QScrollBar *scrollBar) +{ + Q_D(QAbstractScrollArea); + if (!scrollBar) { + qWarning("QAbstractScrollArea::setVerticalScrollBar: Cannot set a null scroll bar"); + return; + } + + d->replaceScrollBar(scrollBar, Qt::Vertical); +} + +/*! + \property QAbstractScrollArea::horizontalScrollBarPolicy + \brief the policy for the horizontal scroll bar + + The default policy is Qt::ScrollBarAsNeeded. + + \sa verticalScrollBarPolicy +*/ + +Qt::ScrollBarPolicy QAbstractScrollArea::horizontalScrollBarPolicy() const +{ + Q_D(const QAbstractScrollArea); + return d->hbarpolicy; +} + +void QAbstractScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy) +{ + Q_D(QAbstractScrollArea); + const Qt::ScrollBarPolicy oldPolicy = d->hbarpolicy; + d->hbarpolicy = policy; + if (isVisible()) + d->layoutChildren(); + if (oldPolicy != d->hbarpolicy) + d->scrollBarPolicyChanged(Qt::Horizontal, d->hbarpolicy); +} + +/*! + Returns the horizontal scroll bar. + + \sa horizontalScrollBarPolicy, verticalScrollBar() + */ +QScrollBar *QAbstractScrollArea::horizontalScrollBar() const +{ + Q_D(const QAbstractScrollArea); + return d->hbar; +} + +/*! + \since 4.2 + + Replaces the existing horizontal scroll bar with \a scrollBar, and sets all + the former scroll bar's slider properties on the new scroll bar. The former + scroll bar is then deleted. + + QAbstractScrollArea already provides horizontal and vertical scroll bars by + default. You can call this function to replace the default horizontal + scroll bar with your own custom scroll bar. + + \sa horizontalScrollBar(), setVerticalScrollBar() +*/ +void QAbstractScrollArea::setHorizontalScrollBar(QScrollBar *scrollBar) +{ + Q_D(QAbstractScrollArea); + if (!scrollBar) { + qWarning("QAbstractScrollArea::setHorizontalScrollBar: Cannot set a null scroll bar"); + return; + } + + d->replaceScrollBar(scrollBar, Qt::Horizontal); +} + +/*! + \since 4.2 + + Returns the widget in the corner between the two scroll bars. + + By default, no corner widget is present. +*/ +QWidget *QAbstractScrollArea::cornerWidget() const +{ + Q_D(const QAbstractScrollArea); + return d->cornerWidget; +} + +/*! + \since 4.2 + + Sets the widget in the corner between the two scroll bars to be + \a widget. + + 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 corner widget is hidden. + + You may call setCornerWidget() with the same widget at different + times. + + All widgets set here will be deleted by the scroll area 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 horizontalScrollBarPolicy, horizontalScrollBarPolicy +*/ +void QAbstractScrollArea::setCornerWidget(QWidget *widget) +{ + Q_D(QAbstractScrollArea); + QWidget* oldWidget = d->cornerWidget; + if (oldWidget != widget) { + if (oldWidget) + oldWidget->hide(); + d->cornerWidget = widget; + + if (widget && widget->parentWidget() != this) + widget->setParent(this); + + d->layoutChildren(); + if (widget) + widget->show(); + } else { + d->cornerWidget = widget; + d->layoutChildren(); + } +} + +/*! + \since 4.2 + Adds \a widget as a scroll bar widget in the location specified + by \a alignment. + + Scroll bar widgets are shown next to the horizontal or vertical + scroll bar, and can be placed on either side of it. If you want + the scroll bar widgets to be always visible, set the + scrollBarPolicy for the corresponding scroll bar to \c AlwaysOn. + + \a alignment must be one of Qt::Alignleft and Qt::AlignRight, + which maps to the horizontal scroll bar, or Qt::AlignTop and + Qt::AlignBottom, which maps to the vertical scroll bar. + + A scroll bar widget can be removed by either re-parenting the + widget or deleting it. It's also possible to hide a widget with + QWidget::hide() + + The scroll bar widget will be resized to fit the scroll bar + geometry for the current style. The following describes the case + for scroll bar widgets on the horizontal scroll bar: + + The height of the widget will be set to match the height of the + scroll bar. To control the width of the widget, use + QWidget::setMinimumWidth and QWidget::setMaximumWidth, or + implement QWidget::sizeHint() and set a horizontal size policy. + If you want a square widget, call + QStyle::pixelMetric(QStyle::PM_ScrollBarExtent) and set the + width to this value. + + \sa scrollBarWidgets() +*/ +void QAbstractScrollArea::addScrollBarWidget(QWidget *widget, Qt::Alignment alignment) +{ + Q_D(QAbstractScrollArea); + + if (widget == 0) + return; + + const Qt::Orientation scrollBarOrientation + = ((alignment & Qt::AlignLeft) || (alignment & Qt::AlignRight)) ? Qt::Horizontal : Qt::Vertical; + const QAbstractScrollAreaScrollBarContainer::LogicalPosition position + = ((alignment & Qt::AlignRight) || (alignment & Qt::AlignBottom)) + ? QAbstractScrollAreaScrollBarContainer::LogicalRight : QAbstractScrollAreaScrollBarContainer::LogicalLeft; + d->scrollBarContainers[scrollBarOrientation]->addWidget(widget, position); + d->layoutChildren(); + if (isHidden() == false) + widget->show(); +} + +/*! + \since 4.2 + Returns a list of the currently set scroll bar widgets. \a alignment + can be any combination of the four location flags. + + \sa addScrollBarWidget() +*/ +QWidgetList QAbstractScrollArea::scrollBarWidgets(Qt::Alignment alignment) +{ + Q_D(QAbstractScrollArea); + + QWidgetList list; + + if (alignment & Qt::AlignLeft) + list += d->scrollBarContainers[Qt::Horizontal]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalLeft); + if (alignment & Qt::AlignRight) + list += d->scrollBarContainers[Qt::Horizontal]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalRight); + if (alignment & Qt::AlignTop) + list += d->scrollBarContainers[Qt::Vertical]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalLeft); + if (alignment & Qt::AlignBottom) + list += d->scrollBarContainers[Qt::Vertical]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalRight); + + return list; +} + +/*! + 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 + is left blank; put widgets in the unused area. + + Note that this function is frequently called by QTreeView and + QTableView, so margins must be implemented by QAbstractScrollArea + subclasses. Also, if the subclasses are to be used in item views, + they should not call this function. + + By default all margins are zero. + +*/ +void QAbstractScrollArea::setViewportMargins(int left, int top, int right, int bottom) +{ + Q_D(QAbstractScrollArea); + d->left = left; + d->top = top; + d->right = right; + d->bottom = bottom; + d->layoutChildren(); +} + +/*! + \fn bool QAbstractScrollArea::event(QEvent *event) + + \reimp + + This is the main event handler for the QAbstractScrollArea widget (\e not + the scrolling area viewport()). The specified \a event is a general event + object that may need to be cast to the appropriate class depending on its + type. + + \sa QEvent::type() +*/ +bool QAbstractScrollArea::event(QEvent *e) +{ + Q_D(QAbstractScrollArea); + switch (e->type()) { + case QEvent::AcceptDropsChange: + // There was a chance that with accessibility client we get an + // event before the viewport was created. + // Also, in some cases we might get here from QWidget::event() virtual function which is (indirectly) called + // from the viewport constructor at the time when the d->viewport is not yet initialized even without any + // accessibility client. See qabstractscrollarea autotest for a test case. + if (d->viewport) + d->viewport->setAcceptDrops(acceptDrops()); + break; + case QEvent::MouseTrackingChange: + d->viewport->setMouseTracking(hasMouseTracking()); + break; + case QEvent::Resize: + d->layoutChildren(); + break; + case QEvent::Paint: + if (d->cornerPaintingRect.isValid()) { + QStyleOption option; + option.rect = d->cornerPaintingRect; + QPainter p(this); + style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &option, &p, this); + } +#ifdef Q_WS_MAC + if (d->reverseCornerPaintingRect.isValid()) { + QStyleOption option; + option.rect = d->reverseCornerPaintingRect; + QPainter p(this); + style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &option, &p, this); + } +#endif + QFrame::paintEvent((QPaintEvent*)e); + break; +#ifndef QT_NO_CONTEXTMENU + case QEvent::ContextMenu: + if (static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) + return QFrame::event(e); + e->ignore(); + break; +#endif // QT_NO_CONTEXTMENU + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + case QEvent::Wheel: +#ifndef QT_NO_DRAGANDDROP + case QEvent::Drop: + case QEvent::DragEnter: + case QEvent::DragMove: + case QEvent::DragLeave: +#endif + return false; + case QEvent::StyleChange: + case QEvent::LayoutDirectionChange: + case QEvent::ApplicationLayoutDirectionChange: + d->layoutChildren(); + // fall through + default: + return QFrame::event(e); + } + return true; +} + +/*! + \fn bool QAbstractScrollArea::viewportEvent(QEvent *event) + + The main event handler for the scrolling area (the viewport() widget). + It handles the \a event specified, and can be called by subclasses to + provide reasonable default behavior. + + Returns true to indicate to the event system that the event has been + handled, and needs no further processing; otherwise returns false to + indicate that the event should be propagated further. + + You can reimplement this function in a subclass, but we recommend + using one of the specialized event handlers instead. + + Specialised handlers for viewport events are: paintEvent(), + mousePressEvent(), mouseReleaseEvent(), mouseDoubleClickEvent(), + mouseMoveEvent(), wheelEvent(), dragEnterEvent(), dragMoveEvent(), + dragLeaveEvent(), dropEvent(), contextMenuEvent(), and + resizeEvent(). +*/ +bool QAbstractScrollArea::viewportEvent(QEvent *e) +{ + switch (e->type()) { + case QEvent::Resize: + case QEvent::Paint: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::MouseMove: + case QEvent::ContextMenu: +#ifndef QT_NO_WHEELEVENT + case QEvent::Wheel: +#endif +#ifndef QT_NO_DRAGANDDROP + case QEvent::Drop: + case QEvent::DragEnter: + case QEvent::DragMove: + case QEvent::DragLeave: +#endif + return QFrame::event(e); + case QEvent::LayoutRequest: + return event(e); + default: + break; + } + return false; // let the viewport widget handle the event +} + +/*! + \fn void QAbstractScrollArea::resizeEvent(QResizeEvent *event) + + This event handler can be reimplemented in a subclass to receive + resize events (passed in \a event), for the viewport() widget. + + When resizeEvent() is called, the viewport already has its new + geometry: Its new size is accessible through the + QResizeEvent::size() function, and the old size through + QResizeEvent::oldSize(). + + \sa QWidget::resizeEvent() + */ +void QAbstractScrollArea::resizeEvent(QResizeEvent *) +{ +} + +/*! + \fn void QAbstractScrollArea::paintEvent(QPaintEvent *event) + + This event handler can be reimplemented in a subclass to receive + paint events (passed in \a event), for the viewport() widget. + + \note If you open a painter, make sure to open it on the viewport(). + + \sa QWidget::paintEvent() +*/ +void QAbstractScrollArea::paintEvent(QPaintEvent*) +{ +} + +/*! + This event handler can be reimplemented in a subclass to receive + mouse press events for the viewport() widget. The event is passed + in \a e. + + \sa QWidget::mousePressEvent() +*/ +void QAbstractScrollArea::mousePressEvent(QMouseEvent *e) +{ + e->ignore(); +} + +/*! + This event handler can be reimplemented in a subclass to receive + mouse release events for the viewport() widget. The event is + passed in \a e. + + \sa QWidget::mouseReleaseEvent() +*/ +void QAbstractScrollArea::mouseReleaseEvent(QMouseEvent *e) +{ + e->ignore(); +} + +/*! + This event handler can be reimplemented in a subclass to receive + mouse double click events for the viewport() widget. The event is + passed in \a e. + + \sa QWidget::mouseDoubleClickEvent() +*/ +void QAbstractScrollArea::mouseDoubleClickEvent(QMouseEvent *e) +{ + e->ignore(); +} + +/*! + This event handler can be reimplemented in a subclass to receive + mouse move events for the viewport() widget. The event is passed + in \a e. + + \sa QWidget::mouseMoveEvent() +*/ +void QAbstractScrollArea::mouseMoveEvent(QMouseEvent *e) +{ + e->ignore(); +} + +/*! + This event handler can be reimplemented in a subclass to receive + wheel events for the viewport() widget. The event is passed in \a + e. + + \sa QWidget::wheelEvent() +*/ +#ifndef QT_NO_WHEELEVENT +void QAbstractScrollArea::wheelEvent(QWheelEvent *e) +{ + Q_D(QAbstractScrollArea); + if (static_cast<QWheelEvent*>(e)->orientation() == Qt::Horizontal) + QApplication::sendEvent(d->hbar, e); + else + QApplication::sendEvent(d->vbar, e); +} +#endif + +#ifndef QT_NO_CONTEXTMENU +/*! + This event handler can be reimplemented in a subclass to receive + context menu events for the viewport() widget. The event is passed + in \a e. + + \sa QWidget::contextMenuEvent() +*/ +void QAbstractScrollArea::contextMenuEvent(QContextMenuEvent *e) +{ + e->ignore(); +} +#endif // QT_NO_CONTEXTMENU + +/*! + This function is called with key event \a e when key presses + occur. It handles PageUp, PageDown, Up, Down, Left, and Right, and + ignores all other key presses. +*/ +void QAbstractScrollArea::keyPressEvent(QKeyEvent * e) +{ + Q_D(QAbstractScrollArea); + if (false){ +#ifndef QT_NO_SHORTCUT + } else if (e == QKeySequence::MoveToPreviousPage) { + d->vbar->triggerAction(QScrollBar::SliderPageStepSub); + } else if (e == QKeySequence::MoveToNextPage) { + d->vbar->triggerAction(QScrollBar::SliderPageStepAdd); +#endif + } else { +#ifdef QT_KEYPAD_NAVIGATION + if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) { + e->ignore(); + return; + } +#endif + switch (e->key()) { + case Qt::Key_Up: + d->vbar->triggerAction(QScrollBar::SliderSingleStepSub); + break; + case Qt::Key_Down: + d->vbar->triggerAction(QScrollBar::SliderSingleStepAdd); + break; + case Qt::Key_Left: +#ifdef QT_KEYPAD_NAVIGATION + if (QApplication::keypadNavigationEnabled() && hasEditFocus() + && (!d->hbar->isVisible() || d->hbar->value() == d->hbar->minimum())) { + //if we aren't using the hbar or we are already at the leftmost point ignore + e->ignore(); + return; + } +#endif + d->hbar->triggerAction( + layoutDirection() == Qt::LeftToRight + ? QScrollBar::SliderSingleStepSub : QScrollBar::SliderSingleStepAdd); + break; + case Qt::Key_Right: +#ifdef QT_KEYPAD_NAVIGATION + if (QApplication::keypadNavigationEnabled() && hasEditFocus() + && (!d->hbar->isVisible() || d->hbar->value() == d->hbar->maximum())) { + //if we aren't using the hbar or we are already at the rightmost point ignore + e->ignore(); + return; + } +#endif + d->hbar->triggerAction( + layoutDirection() == Qt::LeftToRight + ? QScrollBar::SliderSingleStepAdd : QScrollBar::SliderSingleStepSub); + break; + default: + e->ignore(); + return; + } + } + e->accept(); +} + + +#ifndef QT_NO_DRAGANDDROP +/*! + \fn void QAbstractScrollArea::dragEnterEvent(QDragEnterEvent *event) + + This event handler can be reimplemented in a subclass to receive + drag enter events (passed in \a event), for the viewport() widget. + + \sa QWidget::dragEnterEvent() +*/ +void QAbstractScrollArea::dragEnterEvent(QDragEnterEvent *) +{ +} + +/*! + \fn void QAbstractScrollArea::dragMoveEvent(QDragMoveEvent *event) + + This event handler can be reimplemented in a subclass to receive + drag move events (passed in \a event), for the viewport() widget. + + \sa QWidget::dragMoveEvent() +*/ +void QAbstractScrollArea::dragMoveEvent(QDragMoveEvent *) +{ +} + +/*! + \fn void QAbstractScrollArea::dragLeaveEvent(QDragLeaveEvent *event) + + This event handler can be reimplemented in a subclass to receive + drag leave events (passed in \a event), for the viewport() widget. + + \sa QWidget::dragLeaveEvent() +*/ +void QAbstractScrollArea::dragLeaveEvent(QDragLeaveEvent *) +{ +} + +/*! + \fn void QAbstractScrollArea::dropEvent(QDropEvent *event) + + This event handler can be reimplemented in a subclass to receive + drop events (passed in \a event), for the viewport() widget. + + \sa QWidget::dropEvent() +*/ +void QAbstractScrollArea::dropEvent(QDropEvent *) +{ +} + + +#endif + +/*! + This virtual handler is called when the scroll bars are moved by + \a dx, \a dy, and consequently the viewport's contents should be + scrolled accordingly. + + The default implementation simply calls update() on the entire + viewport(), subclasses can reimplement this handler for + optimization purposes, or - like QScrollArea - to move a contents + widget. The parameters \a dx and \a dy are there for convenience, + so that the class knows how much should be scrolled (useful + e.g. when doing pixel-shifts). You may just as well ignore these + values and scroll directly to the position the scroll bars + indicate. + + Calling this function in order to scroll programmatically is an + error, use the scroll bars instead (e.g. by calling + QScrollBar::setValue() directly). +*/ +void QAbstractScrollArea::scrollContentsBy(int, int) +{ + viewport()->update(); +} + +void QAbstractScrollAreaPrivate::_q_hslide(int x) +{ + Q_Q(QAbstractScrollArea); + int dx = xoffset - x; + xoffset = x; + q->scrollContentsBy(dx, 0); +} + +void QAbstractScrollAreaPrivate::_q_vslide(int y) +{ + Q_Q(QAbstractScrollArea); + int dy = yoffset - y; + yoffset = y; + q->scrollContentsBy(0, dy); +} + +void QAbstractScrollAreaPrivate::_q_showOrHideScrollBars() +{ + layoutChildren(); +} + +QPoint QAbstractScrollAreaPrivate::contentsOffset() const +{ + Q_Q(const QAbstractScrollArea); + QPoint offset; + if (vbar->isVisible()) + offset.setY(vbar->value()); + if (hbar->isVisible()) { + if (q->isRightToLeft()) + offset.setX(hbar->maximum() - hbar->value()); + else + offset.setX(hbar->value()); + } + return offset; +} + +/*! + \reimp + +*/ +QSize QAbstractScrollArea::minimumSizeHint() const +{ + Q_D(const QAbstractScrollArea); + int hsbExt = d->hbar->sizeHint().height(); + int vsbExt = d->vbar->sizeHint().width(); + int extra = 2 * d->frameWidth; + return QSize(d->scrollBarContainers[Qt::Horizontal]->sizeHint().width() + vsbExt + extra, + d->scrollBarContainers[Qt::Vertical]->sizeHint().height() + hsbExt + extra); +} + +/*! + \reimp +*/ +QSize QAbstractScrollArea::sizeHint() const +{ + return QSize(256, 192); +#if 0 + Q_D(const QAbstractScrollArea); + int h = qMax(10, fontMetrics().height()); + int f = 2 * d->frameWidth; + return QSize((6 * h) + f, (4 * h) + f); +#endif +} + +/*! + This slot is called by QAbstractScrollArea after setViewport(\a + viewport) has been called. Reimplement this function in a + subclass of QAbstractScrollArea to initialize the new \a viewport + before it is used. + + \sa setViewport() +*/ +void QAbstractScrollArea::setupViewport(QWidget *viewport) +{ + Q_UNUSED(viewport); +} + +QT_END_NAMESPACE + +#include "moc_qabstractscrollarea.cpp" +#include "moc_qabstractscrollarea_p.cpp" + +#endif // QT_NO_SCROLLAREA |