/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: openBossa - INdT (renato.chencarek@openbossa.org) ** ** $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 Technology Preview License Agreement accompanying ** this package. ** ** 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.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** the openBossa stream from INdT (renato.chencarek@openbossa.org). ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include "scrollarea.h" #include "kineticscroll.h" class ScrollAreaPrivate { public: ScrollAreaPrivate(ScrollArea *q); int offset; int maximumOffset; QGraphicsWidget *widget; KineticScroll *kinetic; bool isDragging; int mouseDownPos; int moveConstant; int clickConstant; ScrollArea *q; QList ignoreList; void reconfigure(); int smoothPos(int y); bool isClickPossible(int y); void sendClick(int x, int y); void updateMaximumOffset(); }; ScrollAreaPrivate::ScrollAreaPrivate(ScrollArea *q) : offset(0), maximumOffset(0), widget(0), kinetic(0), isDragging(false), mouseDownPos(-1), moveConstant(15), clickConstant(25), q(q) { } int ScrollAreaPrivate::smoothPos(int y) { if (abs(mouseDownPos - y) <= moveConstant) return y; else if (mouseDownPos - y < 0) return y - moveConstant; else return y + moveConstant; } bool ScrollAreaPrivate::isClickPossible(int y) { if (isDragging || mouseDownPos < 0) return false; else return abs(y - mouseDownPos) <= clickConstant; } void ScrollAreaPrivate::updateMaximumOffset() { const int value = (!widget) ? 0 : qMax(0, widget->size().height() - q->size().height()); if (value != maximumOffset) { maximumOffset = value; emit q->maximumOffsetChanged(); } } void ScrollAreaPrivate::reconfigure() { if (widget) { widget->resize(q->size().width(), widget->size().height()); updateMaximumOffset(); q->setOffset(offset); } } void ScrollAreaPrivate::sendClick(int x, int y) { if (!q->scene()) return; QGraphicsSceneMouseEvent *event; event = new QGraphicsSceneMouseEvent(QEvent::GraphicsSceneMousePress); event->setButton(Qt::LeftButton); event->setScenePos(QPointF(x, y)); ignoreList << event; QCoreApplication::postEvent(q->scene(), event); event = new QGraphicsSceneMouseEvent(QEvent::GraphicsSceneMouseRelease); event->setButton(Qt::LeftButton); event->setScenePos(QPointF(x, y)); ignoreList << event; QCoreApplication::postEvent(q->scene(), event); } ScrollArea::ScrollArea(QGraphicsItem *parent) : QGraphicsWidget(parent), d(new ScrollAreaPrivate(this)) { setFlag(QGraphicsItem::ItemHasNoContents); setFlag(QGraphicsItem::ItemClipsChildrenToShape); d->kinetic = new KineticScroll(this); connect(d->kinetic, SIGNAL(signalMoveOffset(int)), SLOT(kineticMove(int))); } ScrollArea::~ScrollArea() { delete d; } QGraphicsWidget *ScrollArea::widget() const { return d->widget; } void ScrollArea::setWidget(QGraphicsWidget *widget) { if (d->widget) { d->widget->setParentItem(0); d->widget->removeEventFilter(this); d->widget = 0; } if (widget) { d->widget = widget; d->widget->setParentItem(this); d->widget->installEventFilter(this); d->widget->setPos(0, 0); d->widget->setFlag(QGraphicsItem::ItemStacksBehindParent); d->reconfigure(); } } int ScrollArea::offset() const { return d->offset; } void ScrollArea::setOffset(int offset) { if (d->widget) { const int value = qBound(0, offset, d->maximumOffset); if (value != d->offset) { d->offset = value; d->widget->setY(-value); offsetChanged(); } } } int ScrollArea::maximumOffset() const { return d->maximumOffset; } void ScrollArea::resizeEvent(QGraphicsSceneResizeEvent *event) { QGraphicsWidget::resizeEvent(event); d->reconfigure(); } bool ScrollArea::eventFilter(QObject *object, QEvent *event) { if (object == d->widget && event->type() == QEvent::GraphicsSceneResize) d->reconfigure(); return false; } void ScrollArea::mousePressEvent(QGraphicsSceneMouseEvent *e) { if (d->ignoreList.contains(e)) { d->ignoreList.removeOne(e); e->ignore(); return; } int y = e->pos().y(); d->mouseDownPos = y; d->isDragging = !d->kinetic->mouseDown(y); } void ScrollArea::stopKinetic() { d->kinetic->kineticStop(); } void ScrollArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) { if (d->ignoreList.contains(e)) { d->ignoreList.removeOne(e); e->ignore(); return; } if (d->mouseDownPos >= 0) { int y = e->pos().y(); if (d->isClickPossible(y)) { d->sendClick(e->scenePos().x(), e->scenePos().y()); d->kinetic->mouseCancel(); } else { d->kinetic->mouseUp(d->smoothPos(y)); } } d->mouseDownPos = -1; } void ScrollArea::mouseMoveEvent(QGraphicsSceneMouseEvent *e) { if (d->mouseDownPos >= 0) { int y = e->pos().y(); if (!d->isClickPossible(y)) d->isDragging = true; if (abs(d->mouseDownPos - y) > d->moveConstant) d->kinetic->mouseMove(d->smoothPos(y)); } } bool ScrollArea::kineticMove(int value) { int finalOffset = offset() - value; setOffset(finalOffset); if (value == 0 || finalOffset != offset()) { d->kinetic->kineticStop(); return false; } return true; }