• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Plasma

scrollwidget.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright 2009 Marco Martin <notmart@gmail.com>
00003  *
00004  *   This program is free software; you can redistribute it and/or modify
00005  *   it under the terms of the GNU Library General Public License as
00006  *   published by the Free Software Foundation; either version 2, or
00007  *   (at your option) any later version.
00008  *
00009  *   This program is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details
00013  *
00014  *   You should have received a copy of the GNU Library General Public
00015  *   License along with this program; if not, write to the
00016  *   Free Software Foundation, Inc.,
00017  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00018  */
00019 
00020 #include "scrollwidget.h"
00021 
00022 #include <cmath>
00023 
00024 //Qt
00025 #include <QGraphicsSceneResizeEvent>
00026 #include <QGraphicsGridLayout>
00027 #include <QGraphicsScene>
00028 #include <QApplication>
00029 #include <QKeyEvent>
00030 #include <QWidget>
00031 #include <QTimer>
00032 #include <QTime>
00033 #include <QPropertyAnimation>
00034 #include <QSequentialAnimationGroup>
00035 
00036 #include <QLabel>
00037 
00038 //KDE
00039 #include <kmimetype.h>
00040 #include <kdebug.h>
00041 #include <kglobalsettings.h>
00042 #include <kiconloader.h>
00043 #include <ktextedit.h>
00044 #include <ktextbrowser.h>
00045 
00046 //Plasma
00047 #include <plasma/widgets/scrollbar.h>
00048 #include <plasma/widgets/svgwidget.h>
00049 #include <plasma/widgets/label.h>
00050 #include <plasma/widgets/textedit.h>
00051 #include <plasma/widgets/textbrowser.h>
00052 #include <plasma/animator.h>
00053 #include <plasma/svg.h>
00054 
00055 
00056 #define DEBUG 0
00057 
00058 /*
00059   The flicking code is largely based on the behavior of
00060   the flickable widget in QDeclerative so porting between
00061   the two should preserve the behavior.
00062   The code that figures out velocity could use some
00063   improvements, in particular IGNORE_SUSPICIOUS_MOVES
00064   is a hack that shouldn't be necessary.
00065  */
00066 
00067 //XXX fixme
00068 //    we use a timer between move events to figure out
00069 //    the velocity of a move, but sometimes we're getting move
00070 //    events with big positional changes with no break
00071 //    in between them, which causes us to compute
00072 //    huge velocities. this define just filters out
00073 //    events which come at insanly small time intervals.
00074 //    at some point we need to figure out how to do it properly
00075 #define IGNORE_SUSPICIOUS_MOVES 1
00076 
00077 // FlickThreshold determines how far the "mouse" must have moved
00078 // before we perform a flick.
00079 static const int FlickThreshold = 20;
00080 
00081 
00082 static const qreal MinimumFlickVelocity = 200;
00083 static const qreal MaxVelocity = 2000;
00084 
00085 // time it takes the widget to flick back to its
00086 //  bounds when overshot
00087 static const qreal FixupDuration = 600;
00088 
00089 namespace Plasma
00090 {
00091 
00092 class ScrollWidgetPrivate
00093 {
00094 public:
00095     enum Gesture {
00096         GestureNone = 0,
00097         GestureUndefined,
00098         GestureScroll,
00099         GestureZoom
00100     };
00101 
00102     ScrollWidgetPrivate(ScrollWidget *parent)
00103         : q(parent),
00104           topBorder(0),
00105           bottomBorder(0),
00106           leftBorder(0),
00107           rightBorder(0),
00108           dragging(false),
00109           overflowBordersVisible(true),
00110           multitouchGesture(GestureNone)
00111     {
00112     }
00113 
00114     ~ScrollWidgetPrivate()
00115     {
00116     }
00117 
00118     void commonConstructor()
00119     {
00120         q->setFocusPolicy(Qt::StrongFocus);
00121         q->setFiltersChildEvents(true);
00122         layout = new QGraphicsGridLayout(q);
00123         q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
00124         layout->setContentsMargins(0, 0, 0, 0);
00125         scrollingWidget = new QGraphicsWidget(q);
00126         scrollingWidget->setFlag(QGraphicsItem::ItemHasNoContents);
00127         scrollingWidget->installEventFilter(q);
00128         layout->addItem(scrollingWidget, 0, 0);
00129         borderSvg = new Plasma::Svg(q);
00130         borderSvg->setImagePath("widgets/scrollwidget");
00131 
00132         adjustScrollbarsTimer = new QTimer(q);
00133         adjustScrollbarsTimer->setSingleShot(true);
00134         QObject::connect(adjustScrollbarsTimer, SIGNAL(timeout()), q, SLOT(adjustScrollbars()));
00135 
00136         wheelTimer =  new QTimer(q);
00137         wheelTimer->setSingleShot(true);
00138 
00139         verticalScrollBarPolicy = Qt::ScrollBarAsNeeded;
00140         verticalScrollBar = new Plasma::ScrollBar(q);
00141         verticalScrollBar->setFocusPolicy(Qt::NoFocus);
00142         layout->addItem(verticalScrollBar, 0, 1);
00143         verticalScrollBar->nativeWidget()->setMinimum(0);
00144         verticalScrollBar->nativeWidget()->setMaximum(100);
00145         QObject::connect(verticalScrollBar, SIGNAL(valueChanged(int)), q, SLOT(verticalScroll(int)));
00146 
00147         horizontalScrollBarPolicy = Qt::ScrollBarAsNeeded;
00148         horizontalScrollBar = new Plasma::ScrollBar(q);
00149         verticalScrollBar->setFocusPolicy(Qt::NoFocus);
00150         horizontalScrollBar->setOrientation(Qt::Horizontal);
00151         layout->addItem(horizontalScrollBar, 1, 0);
00152         horizontalScrollBar->nativeWidget()->setMinimum(0);
00153         horizontalScrollBar->nativeWidget()->setMaximum(100);
00154         QObject::connect(horizontalScrollBar, SIGNAL(valueChanged(int)), q, SLOT(horizontalScroll(int)));
00155 
00156         layout->setColumnSpacing(0, 0);
00157         layout->setColumnSpacing(1, 0);
00158         layout->setRowSpacing(0, 0);
00159         layout->setRowSpacing(1, 0);
00160 
00161         flickAnimationX = 0;
00162         flickAnimationY = 0;
00163         fixupAnimation.groupX = 0;
00164         fixupAnimation.startX = 0;
00165         fixupAnimation.endX = 0;
00166         fixupAnimation.groupY = 0;
00167         fixupAnimation.startY = 0;
00168         fixupAnimation.endY = 0;
00169         fixupAnimation.snapX = 0;
00170         fixupAnimation.snapY = 0;
00171         directMoveAnimation = 0;
00172         stealEvent = false;
00173         hasOvershoot = true;
00174 
00175         alignment = Qt::AlignLeft | Qt::AlignTop;
00176 
00177         hasContentsProperty = false;
00178         hasOffsetProperty = false;
00179         hasXProperty = false;
00180         hasYProperty = false;
00181     }
00182 
00183     void adjustScrollbars()
00184     {
00185         if (!widget) {
00186             return;
00187         }
00188 
00189         const bool verticalVisible = widget.data()->size().height() > q->size().height();
00190         const bool horizontalVisible = widget.data()->size().width() > q->size().width();
00191 
00192         verticalScrollBar->nativeWidget()->setMaximum(qMax(0, int((widget.data()->size().height() - scrollingWidget->size().height())/10)));
00193         verticalScrollBar->nativeWidget()->setPageStep(int(scrollingWidget->size().height())/10);
00194 
00195         if (verticalScrollBarPolicy == Qt::ScrollBarAlwaysOff ||
00196             !verticalVisible) {
00197             if (layout->count() > 2 && layout->itemAt(2) == verticalScrollBar) {
00198                 layout->removeAt(2);
00199             } else if (layout->count() > 1 && layout->itemAt(1) == verticalScrollBar) {
00200                 layout->removeAt(1);
00201             }
00202             verticalScrollBar->hide();
00203         } else if (!verticalScrollBar->isVisible()) {
00204             layout->addItem(verticalScrollBar, 0, 1);
00205             verticalScrollBar->show();
00206         }
00207 
00208         horizontalScrollBar->nativeWidget()->setMaximum(qMax(0, int((widget.data()->size().width() - scrollingWidget->size().width())/10)));
00209         horizontalScrollBar->nativeWidget()->setPageStep(int(scrollingWidget->size().width())/10);
00210 
00211         if (horizontalScrollBarPolicy == Qt::ScrollBarAlwaysOff ||
00212             !horizontalVisible) {
00213             if (layout->count() > 2 && layout->itemAt(2) == horizontalScrollBar) {
00214                 layout->removeAt(2);
00215             } else if (layout->count() > 1 && layout->itemAt(1) == horizontalScrollBar) {
00216                 layout->removeAt(1);
00217             }
00218             horizontalScrollBar->hide();
00219         } else if (!horizontalScrollBar->isVisible()) {
00220             layout->addItem(horizontalScrollBar, 1, 0);
00221             horizontalScrollBar->show();
00222         }
00223 
00224          if (widget && !topBorder && verticalVisible) {
00225             topBorder = new Plasma::SvgWidget(q);
00226             topBorder->setSvg(borderSvg);
00227             topBorder->setElementID("border-top");
00228             topBorder->setZValue(900);
00229             topBorder->resize(topBorder->effectiveSizeHint(Qt::PreferredSize));
00230             topBorder->setVisible(overflowBordersVisible);
00231 
00232             bottomBorder = new Plasma::SvgWidget(q);
00233             bottomBorder->setSvg(borderSvg);
00234             bottomBorder->setElementID("border-bottom");
00235             bottomBorder->setZValue(900);
00236             bottomBorder->resize(bottomBorder->effectiveSizeHint(Qt::PreferredSize));
00237             bottomBorder->setVisible(overflowBordersVisible);
00238         } else if (topBorder && widget && !verticalVisible) {
00239             //FIXME: in some cases topBorder->deleteLater() is deleteNever(), why?
00240             topBorder->hide();
00241             bottomBorder->hide();
00242             topBorder->deleteLater();
00243             bottomBorder->deleteLater();
00244             topBorder = 0;
00245             bottomBorder = 0;
00246         }
00247 
00248 
00249         if (widget && !leftBorder && horizontalVisible) {
00250             leftBorder = new Plasma::SvgWidget(q);
00251             leftBorder->setSvg(borderSvg);
00252             leftBorder->setElementID("border-left");
00253             leftBorder->setZValue(900);
00254             leftBorder->resize(leftBorder->effectiveSizeHint(Qt::PreferredSize));
00255             leftBorder->setVisible(overflowBordersVisible);
00256 
00257             rightBorder = new Plasma::SvgWidget(q);
00258             rightBorder->setSvg(borderSvg);
00259             rightBorder->setElementID("border-right");
00260             rightBorder->setZValue(900);
00261             rightBorder->resize(rightBorder->effectiveSizeHint(Qt::PreferredSize));
00262             rightBorder->setVisible(overflowBordersVisible);
00263         } else if (leftBorder && widget && !horizontalVisible) {
00264             leftBorder->hide();
00265             rightBorder->hide();
00266             leftBorder->deleteLater();
00267             rightBorder->deleteLater();
00268             leftBorder = 0;
00269             rightBorder = 0;
00270         }
00271 
00272         layout->activate();
00273 
00274         if (topBorder) {
00275             topBorder->resize(q->size().width(), topBorder->size().height());
00276             bottomBorder->resize(q->size().width(), bottomBorder->size().height());
00277             bottomBorder->setPos(0, q->size().height() - topBorder->size().height());
00278         }
00279         if (leftBorder) {
00280             leftBorder->resize(leftBorder->size().width(), q->size().height());
00281             rightBorder->resize(rightBorder->size().width(), q->size().height());
00282             rightBorder->setPos(q->size().width() - rightBorder->size().width(), 0);
00283         }
00284 
00285         QSizeF widgetSize = widget.data()->size();
00286         if (widget.data()->sizePolicy().expandingDirections() & Qt::Horizontal) {
00287             //keep a 1 pixel border
00288             widgetSize.setWidth(scrollingWidget->size().width());
00289         }
00290         if (widget.data()->sizePolicy().expandingDirections() & Qt::Vertical) {
00291             widgetSize.setHeight(scrollingWidget->size().height());
00292         }
00293         widget.data()->resize(widgetSize);
00294 
00295         adjustClipping();
00296     }
00297 
00298     void verticalScroll(int value)
00299     {
00300         if (!widget) {
00301             return;
00302         }
00303 
00304         if (!dragging) {
00305             widget.data()->setPos(QPoint(widget.data()->pos().x(), -value*10));
00306         }
00307     }
00308 
00309     void horizontalScroll(int value)
00310     {
00311         if (!widget) {
00312             return;
00313         }
00314 
00315         if (!dragging) {
00316             widget.data()->setPos(QPoint(-value*10, widget.data()->pos().y()));
00317         }
00318     }
00319 
00320     void adjustClipping()
00321     {
00322         if (!widget) {
00323             return;
00324         }
00325 
00326         const bool clip = widget.data()->size().width() > scrollingWidget->size().width() || widget.data()->size().height() > scrollingWidget->size().height();
00327 
00328         scrollingWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape, clip);
00329     }
00330 
00331     qreal overShootDistance(qreal velocity, qreal size) const
00332     {
00333         if (MaxVelocity <= 0)
00334             return 0.0;
00335 
00336         velocity = qAbs(velocity);
00337         if (velocity > MaxVelocity)
00338             velocity = MaxVelocity;
00339         qreal dist = size / 4 * velocity / MaxVelocity;
00340         return dist;
00341     }
00342 
00343     void animateMoveTo(const QPointF &pos)
00344     {
00345         qreal duration = 800;
00346         QPointF start = q->scrollPosition();
00347         QSizeF threshold = q->viewportGeometry().size();
00348         QPointF diff = pos - start;
00349 
00350         //reduce if it's within the viewport
00351         if (qAbs(diff.x()) < threshold.width() ||
00352             qAbs(diff.y()) < threshold.height())
00353             duration /= 2;
00354 
00355         fixupAnimation.groupX->stop();
00356         fixupAnimation.groupY->stop();
00357         fixupAnimation.snapX->stop();
00358         fixupAnimation.snapY->stop();
00359 
00360         directMoveAnimation->setStartValue(start);
00361         directMoveAnimation->setEndValue(pos);
00362         directMoveAnimation->setDuration(duration);
00363         directMoveAnimation->start();
00364     }
00365 
00366     void flick(QPropertyAnimation *anim,
00367                qreal velocity,
00368                qreal val,
00369                qreal minExtent,
00370                qreal maxExtent,
00371                qreal size)
00372     {
00373         qreal deceleration = 500;
00374         qreal maxDistance = -1;
00375         qreal target = 0;
00376         // -ve velocity means list is moving up
00377         if (velocity > 0) {
00378             if (val < minExtent)
00379                 maxDistance = qAbs(minExtent - val + (hasOvershoot?overShootDistance(velocity,size):0));
00380             target = minExtent;
00381             deceleration = -deceleration;
00382         } else {
00383             if (val > maxExtent)
00384                 maxDistance = qAbs(maxExtent - val) + (hasOvershoot?overShootDistance(velocity,size):0);
00385             target = maxExtent;
00386         }
00387         if (maxDistance > 0) {
00388             qreal v = velocity;
00389             if (MaxVelocity != -1 && MaxVelocity < qAbs(v)) {
00390                 if (v < 0)
00391                     v = -MaxVelocity;
00392                 else
00393                     v = MaxVelocity;
00394             }
00395             qreal duration = qAbs(v / deceleration);
00396             qreal diffY = v * duration + (0.5  * deceleration * duration * duration);
00397             qreal startY = val;
00398 
00399             qreal endY = startY + diffY;
00400 
00401             if (velocity > 0) {
00402                 if (endY > target)
00403                     endY = startY + maxDistance;
00404             } else {
00405                 if (endY < target)
00406                     endY = startY - maxDistance;
00407             }
00408             duration = qAbs((endY-startY)/ (-v/2));
00409 
00410             if (hasYProperty) {
00411                 startY = -startY;
00412                 endY = -endY;
00413             }
00414 
00415 
00416 #if DEBUG
00417             qDebug()<<"XXX velocity = "<<v <<", target = "<< target
00418                     <<", maxDist = "<<maxDistance;
00419             qDebug()<<"duration = "<<duration<<" secs, ("
00420                     << (duration * 1000) <<" msecs)";
00421             qDebug()<<"startY = "<<startY;
00422             qDebug()<<"endY = "<<endY;
00423             qDebug()<<"overshoot = "<<overShootDistance(v, size);
00424             qDebug()<<"avg velocity = "<< ((endY-startY)/duration);
00425 #endif
00426 
00427             anim->setStartValue(startY);
00428             anim->setEndValue(endY);
00429             anim->setDuration(duration * 1000);
00430             anim->start();
00431         } else {
00432             if (anim == flickAnimationX)
00433                 fixupX();
00434             else
00435                 fixupY();
00436         }
00437     }
00438     void flickX(qreal velocity)
00439     {
00440         flick(flickAnimationX, velocity, widgetX(), minXExtent(), maxXExtent(),
00441               q->viewportGeometry().width());
00442     }
00443     void flickY(qreal velocity)
00444     {
00445         flick(flickAnimationY, velocity, widgetY(),minYExtent(), maxYExtent(),
00446               q->viewportGeometry().height());
00447     }
00448     void fixup(QAnimationGroup *group,
00449                QPropertyAnimation *start, QPropertyAnimation *end,
00450                qreal val, qreal minExtent, qreal maxExtent)
00451     {
00452         if (val > minExtent || maxExtent > minExtent) {
00453             if (!qFuzzyCompare(val, minExtent)) {
00454                 if (FixupDuration) {
00455                     //TODO: we should consider the case where there is one axis available not the other
00456                     if (hasXProperty && hasYProperty) {
00457                         val = -val;
00458                         minExtent = -minExtent;
00459                     }
00460                     qreal dist = minExtent - val;
00461                     start->setStartValue(val);
00462                     start->setEndValue(minExtent - dist/2);
00463                     end->setStartValue(minExtent - dist/2);
00464                     end->setEndValue(minExtent);
00465                     start->setDuration(FixupDuration/4);
00466                     end->setDuration(3*FixupDuration/4);
00467                     group->start();
00468                 } else {
00469                     QObject *obj = start->targetObject();
00470                     obj->setProperty(start->propertyName(), minExtent);
00471                 }
00472             }
00473         } else if (val < maxExtent) {
00474             if (FixupDuration) {
00475                 if (hasXProperty && hasYProperty) {
00476                     val = -val;
00477                     maxExtent = -maxExtent;
00478                 }
00479                 qreal dist = maxExtent - val;
00480                 start->setStartValue(val);
00481                 start->setEndValue(maxExtent - dist/2);
00482                 end->setStartValue(maxExtent - dist/2);
00483                 end->setEndValue(maxExtent);
00484                 start->setDuration(FixupDuration/4);
00485                 end->setDuration(3*FixupDuration/4);
00486                 group->start();
00487             } else {
00488                 QObject *obj = start->targetObject();
00489                 obj->setProperty(start->propertyName(), maxExtent);
00490             }
00491         } else if (end == fixupAnimation.endX && snapSize.width() > 1 &&
00492                    q->contentsSize().width() > q->viewportGeometry().width()) {
00493             int target = snapSize.width() * round(val/snapSize.width());
00494             fixupAnimation.snapX->setStartValue(val);
00495             fixupAnimation.snapX->setEndValue(target);
00496             fixupAnimation.snapX->setDuration(FixupDuration);
00497             fixupAnimation.snapX->start();
00498         } else if (end == fixupAnimation.endY && snapSize.height() > 1 &&
00499                    q->contentsSize().height() > q->viewportGeometry().height()) {
00500             int target = snapSize.height() * round(val/snapSize.height());
00501             fixupAnimation.snapY->setStartValue(val);
00502             fixupAnimation.snapY->setEndValue(target);
00503             fixupAnimation.snapY->setDuration(FixupDuration);
00504             fixupAnimation.snapY->start();
00505         }
00506     }
00507     void fixupX()
00508     {
00509         fixup(fixupAnimation.groupX, fixupAnimation.startX, fixupAnimation.endX,
00510               widgetX(), minXExtent(), maxXExtent());
00511     }
00512     void fixupY()
00513     {
00514         fixup(fixupAnimation.groupY, fixupAnimation.startY, fixupAnimation.endY,
00515               widgetY(), minYExtent(), maxYExtent());
00516     }
00517 
00518     void makeRectVisible()
00519     {
00520         if (!widget) {
00521             return;
00522         }
00523 
00524         QRectF viewRect = scrollingWidget->boundingRect();
00525         //ensure the rect is not outside the widget bounding rect
00526         QRectF mappedRect = QRectF(QPointF(qBound((qreal)0.0, rectToBeVisible.x(), widget.data()->size().width() - rectToBeVisible.width()),
00527                                            qBound((qreal)0.0, rectToBeVisible.y(), widget.data()->size().height() - rectToBeVisible.height())),
00528                                            rectToBeVisible.size());
00529         mappedRect = widget.data()->mapToItem(scrollingWidget, mappedRect).boundingRect();
00530 
00531         if (viewRect.contains(mappedRect)) {
00532             return;
00533         }
00534 
00535         QPointF delta(0, 0);
00536 
00537         if (mappedRect.top() < 0) {
00538             delta.setY(-mappedRect.top());
00539         } else if  (mappedRect.bottom() > viewRect.bottom()) {
00540             delta.setY(viewRect.bottom() - mappedRect.bottom());
00541         }
00542 
00543         if (mappedRect.left() < 0) {
00544             delta.setX(-mappedRect.left());
00545         } else if  (mappedRect.right() > viewRect.right()) {
00546             delta.setX(viewRect.right() - mappedRect.right());
00547         }
00548 
00549         animateMoveTo(q->scrollPosition() - delta);
00550     }
00551 
00552     void makeItemVisible(QGraphicsItem *itemToBeVisible)
00553     {
00554         if (!widget) {
00555             return;
00556         }
00557 
00558         QRectF rect(widget.data()->mapFromScene(itemToBeVisible->scenePos()), itemToBeVisible->boundingRect().size());
00559         rectToBeVisible = rect;
00560 
00561         makeRectVisible();
00562     }
00563 
00564     void makeItemVisible()
00565     {
00566         if (widgetToBeVisible) {
00567             makeItemVisible(widgetToBeVisible.data());
00568         }
00569     }
00570 
00571     void stopAnimations()
00572     {
00573         flickAnimationX->stop();
00574         flickAnimationY->stop();
00575         fixupAnimation.groupX->stop();
00576         fixupAnimation.groupY->stop();
00577     }
00578 
00579     void setWidgetX(qreal x)
00580     {
00581         if (hasXProperty) {
00582             widget.data()->setProperty("scrollPositionX", -x);
00583         } else
00584             widget.data()->setX(x);
00585     }
00586     void setWidgetY(qreal y)
00587     {
00588         if (hasYProperty) {
00589             widget.data()->setProperty("scrollPositionY", -y);
00590         } else
00591             widget.data()->setY(y);
00592     }
00593     qreal widgetX() const
00594     {
00595         if (hasXProperty) {
00596             return -widget.data()->property("scrollPositionX").toReal();
00597         } else
00598             return widget.data()->x();
00599     }
00600     qreal widgetY() const
00601     {
00602         if (hasYProperty) {
00603             return -widget.data()->property("scrollPositionY").toReal();
00604         } else
00605             return widget.data()->y();
00606     }
00607 
00608     void handleKeyPressEvent(QKeyEvent *event)
00609     {
00610         if (!widget.data()) {
00611             event->ignore();
00612             return;
00613         }
00614 
00615         QPointF start = q->scrollPosition();
00616         QPointF end = start;
00617 
00618         qreal step = 100;
00619 
00620         switch (event->key()) {
00621         case Qt::Key_Left:
00622             if (canXFlick()) {
00623                 end += QPointF(-step, 0);
00624             }
00625             break;
00626         case Qt::Key_Right:
00627             if (canXFlick()) {
00628                 end += QPointF(step, 0);
00629             }
00630             break;
00631         case Qt::Key_Up:
00632             if (canYFlick()) {
00633                 end += QPointF(0, -step);
00634             }
00635             break;
00636         case Qt::Key_Down:
00637             if (canYFlick()) {
00638                 end += QPointF(0, step);
00639             }
00640             break;
00641         default:
00642             event->ignore();
00643             return;
00644         }
00645 
00646         fixupAnimation.groupX->stop();
00647         fixupAnimation.groupY->stop();
00648         fixupAnimation.snapX->stop();
00649         fixupAnimation.snapY->stop();
00650         directMoveAnimation->setStartValue(start);
00651         directMoveAnimation->setEndValue(end);
00652         directMoveAnimation->setDuration(200);
00653         directMoveAnimation->start();
00654     }
00655 
00656     void handleMousePressEvent(QGraphicsSceneMouseEvent *event)
00657     {
00658         lastPos = QPoint();
00659         lastPosTime = QTime::currentTime();
00660         pressPos = event->scenePos();
00661         pressScrollPos = -q->scrollPosition();
00662         pressTime = QTime::currentTime();
00663         velocity = QPointF();
00664         stopAnimations();
00665     }
00666 
00667     void handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
00668     {
00669         if (lastPosTime.isNull())
00670             return;
00671         bool rejectY = false;
00672         bool rejectX = false;
00673         bool moved = false;
00674 
00675         if (canYFlick()) {
00676             int dy = int(event->scenePos().y() - pressPos.y());
00677             if (qAbs(dy) > KGlobalSettings::dndEventDelay() || elapsed(pressTime) > 200) {
00678                 qreal newY = dy + pressScrollPos.y();
00679                 const qreal minY = minYExtent();
00680                 const qreal maxY = maxYExtent();
00681                 if (newY > minY)
00682                     newY = minY + (newY - minY) / 2;
00683                 if (newY < maxY && maxY - minY <= 0)
00684                     newY = maxY + (newY - maxY) / 2;
00685                 if (!hasOvershoot && (newY > minY || newY < maxY)) {
00686                     if (newY > minY)
00687                         newY = minY;
00688                     else if (newY < maxY)
00689                         newY = maxY;
00690                     else
00691                         rejectY = true;
00692                 }
00693                 if (!rejectY && stealEvent) {
00694                     setWidgetY(qRound(newY));
00695                     moved = true;
00696                 }
00697                 if (qAbs(dy) > KGlobalSettings::dndEventDelay())
00698                     stealEvent = true;
00699             }
00700         }
00701 
00702         if (canXFlick()) {
00703             int dx = int(event->scenePos().x() - pressPos.x());
00704             if (qAbs(dx) > KGlobalSettings::dndEventDelay() || elapsed(pressTime) > 200) {
00705                 qreal newX = dx + pressScrollPos.x();
00706                 const qreal minX = minXExtent();
00707                 const qreal maxX = maxXExtent();
00708                 if (newX > minX)
00709                     newX = minX + (newX - minX) / 2;
00710                 if (newX < maxX && maxX - minX <= 0)
00711                     newX = maxX + (newX - maxX) / 2;
00712                 if (!hasOvershoot && (newX > minX || newX < maxX)) {
00713                     if (newX > minX)
00714                         newX = minX;
00715                     else if (newX < maxX)
00716                         newX = maxX;
00717                     else
00718                         rejectX = true;
00719                 }
00720                 if (!rejectX && stealEvent) {
00721                     setWidgetX(qRound(newX));
00722                     moved = true;
00723                 }
00724 
00725                 if (qAbs(dx) > KGlobalSettings::dndEventDelay())
00726                     stealEvent = true;
00727             }
00728         }
00729 
00730         if (!lastPos.isNull()) {
00731             qreal msecs = qreal(restart(lastPosTime));
00732             qreal elapsed =  msecs / 1000.;
00733 #if IGNORE_SUSPICIOUS_MOVES
00734             if (msecs > 3) {
00735 #endif
00736             if (elapsed <= 0)
00737                 elapsed = 1;
00738             if (canYFlick()) {
00739                 qreal diff = event->scenePos().y() - lastPos.y();
00740                 // average to reduce the effect of spurious moves
00741                 velocity.setY( velocity.y() + (diff / elapsed) );
00742                 velocity.setY( velocity.y() / 2 );
00743             }
00744 
00745             if (canXFlick()) {
00746                 qreal diff = event->scenePos().x() - lastPos.x();
00747                 // average to reduce the effect of spurious moves
00748                 velocity.setX( velocity.x() + (diff / elapsed) );
00749                 velocity.setX( velocity.x() / 2 );
00750             }
00751 #if IGNORE_SUSPICIOUS_MOVES
00752             }
00753 #endif
00754         }
00755 
00756         if (rejectX) velocity.setX(0);
00757         if (rejectY) velocity.setY(0);
00758 
00759         lastPos = event->scenePos();
00760     }
00761 
00762     void handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00763     {
00764         stealEvent = false;
00765         if (lastPosTime.isNull())
00766             return;
00767 
00768         if (elapsed(lastPosTime) > 100) {
00769             // if we drag then pause before release we should not cause a flick.
00770             velocity = QPointF();
00771         }
00772 
00773         if (qAbs(velocity.y()) > 10 &&
00774             qAbs(event->scenePos().y() - pressPos.y()) > FlickThreshold) {
00775             qreal vVelocity = velocity.y();
00776             // Minimum velocity to avoid annoyingly slow flicks.
00777             if (qAbs(vVelocity) < MinimumFlickVelocity)
00778                 vVelocity = vVelocity < 0 ? -MinimumFlickVelocity : MinimumFlickVelocity;
00779             flickY(vVelocity);
00780         } else {
00781             fixupY();
00782         }
00783 
00784         if (qAbs(velocity.x()) > 10 &&
00785             qAbs(event->scenePos().x() - pressPos.x()) > FlickThreshold) {
00786             qreal hVelocity = velocity.x();
00787             // Minimum velocity to avoid annoyingly slow flicks.
00788             if (qAbs(hVelocity) < MinimumFlickVelocity)
00789                 hVelocity = hVelocity < 0 ? -MinimumFlickVelocity : MinimumFlickVelocity;
00790             flickX(hVelocity);
00791         } else {
00792             fixupX();
00793         }
00794 
00795         lastPosTime = QTime();
00796     }
00797 
00798     void handleWheelEvent(QGraphicsSceneWheelEvent *event)
00799     {
00800         //only scroll when the animation is done, this avoids to receive too many events and getting mad when they arrive from a touchpad
00801         if (!widget.data() || wheelTimer->isActive()) {
00802             return;
00803         }
00804 
00805         QPointF start = q->scrollPosition();
00806         QPointF end = start;
00807 
00808         //At some point we should switch to
00809         // step = QApplication::wheelScrollLines() *
00810         //      (event->delta()/120) *
00811         //      scrollBar->singleStep();
00812         // which gives us exactly the number of lines to scroll but the issue
00813         // is that at this point we don't have any clue what a "line" is and if
00814         // we make it a pixel then scrolling by 3 (default) pixels will be
00815         // very painful
00816         qreal step = -event->delta()/3;
00817 
00818         //ifthe widget can scroll in a single axis and the wheel is the other one, scroll the other one
00819         Qt::Orientation orientation = event->orientation();
00820         if (orientation == Qt::Vertical) {
00821             if (!canYFlick() && canXFlick()) {
00822                 end += QPointF(step, 0);
00823             } else if (canYFlick()) {
00824                 end += QPointF(0, step);
00825             } else {
00826                 return;
00827             }
00828         } else {
00829             if (canYFlick() && !canXFlick()) {
00830                 end += QPointF(0, step);
00831             } else if (canXFlick()) {
00832                 end += QPointF(step, 0);
00833             } else {
00834                 return;
00835             }
00836         }
00837 
00838         fixupAnimation.groupX->stop();
00839         fixupAnimation.groupY->stop();
00840         fixupAnimation.snapX->stop();
00841         fixupAnimation.snapY->stop();
00842         directMoveAnimation->setStartValue(start);
00843         directMoveAnimation->setEndValue(end);
00844         directMoveAnimation->setDuration(200);
00845         directMoveAnimation->start();
00846         wheelTimer->start(50);
00847     }
00848 
00849     qreal minXExtent() const
00850     {
00851         if (alignment & Qt::AlignLeft)
00852             return 0;
00853         else {
00854             qreal vWidth = q->viewportGeometry().width();
00855             qreal cWidth = q->contentsSize().width();
00856             if (cWidth < vWidth) {
00857                 if (alignment & Qt::AlignRight)
00858                     return  vWidth - cWidth;
00859                 else if (alignment & Qt::AlignHCenter)
00860                     return vWidth / 2 - cWidth / 2;
00861             }
00862         }
00863 
00864         return 0;
00865     }
00866 
00867     qreal maxXExtent() const
00868     {
00869         return q->viewportGeometry().width() -
00870             q->contentsSize().width();
00871     }
00872 
00873     qreal minYExtent() const
00874     {
00875         if (alignment & Qt::AlignTop)
00876             return 0;
00877         else {
00878             qreal vHeight = q->viewportGeometry().height();
00879             qreal cHeight = q->contentsSize().height();
00880             if (cHeight < vHeight) {
00881                 if (alignment & Qt::AlignBottom)
00882                     return  vHeight - cHeight;
00883                 else if (alignment & Qt::AlignVCenter)
00884                     return vHeight / 2 - cHeight / 2;
00885             }
00886         }
00887 
00888         return 0;
00889     }
00890 
00891     qreal maxYExtent() const
00892     {
00893         return q->viewportGeometry().height() -
00894             q->contentsSize().height();
00895     }
00896 
00897     bool canXFlick() const
00898     {
00899         //make the thing feel quite "fixed" don't permit to flick when the contents size is less than the viewport
00900         return q->contentsSize().width() > q->viewportGeometry().width();
00901     }
00902 
00903     bool canYFlick() const
00904     {
00905         return q->contentsSize().height() > q->viewportGeometry().height();
00906     }
00907 
00908     int elapsed(const QTime &t) const
00909     {
00910         int n = t.msecsTo(QTime::currentTime());
00911         if (n < 0) // passed midnight
00912             n += 86400 * 1000;
00913         return n;
00914     }
00915 
00916     int restart(QTime &t) const
00917     {
00918         QTime time = QTime::currentTime();
00919         int n = t.msecsTo(time);
00920         if (n < 0) // passed midnight
00921             n += 86400*1000;
00922         t = time;
00923         return n;
00924     }
00925 
00926     void createFlickAnimations()
00927     {
00928         if (widget.data()) {
00929             QString xProp = QString::fromLatin1("x");
00930             QString yProp = QString::fromLatin1("y");
00931 
00932             if (hasXProperty)
00933                 xProp = QString::fromLatin1("scrollPositionX");
00934             if (hasYProperty)
00935                 yProp = QString::fromLatin1("scrollPositionY");
00936 
00937             flickAnimationX = new QPropertyAnimation(widget.data(),
00938                                                      xProp.toLatin1(), widget.data());
00939             flickAnimationY = new QPropertyAnimation(widget.data(),
00940                                                      yProp.toLatin1(), widget.data());
00941             QObject::connect(flickAnimationX, SIGNAL(finished()),
00942                              q, SLOT(fixupX()));
00943             QObject::connect(flickAnimationY, SIGNAL(finished()),
00944                              q, SLOT(fixupY()));
00945 
00946             QObject::connect(flickAnimationX,
00947                              SIGNAL(stateChanged(QAbstractAnimation::State,
00948                                                  QAbstractAnimation::State)),
00949                              q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
00950                                                           QAbstractAnimation::State)));
00951             QObject::connect(flickAnimationY,
00952                              SIGNAL(stateChanged(QAbstractAnimation::State,
00953                                                  QAbstractAnimation::State)),
00954                              q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
00955                                                           QAbstractAnimation::State)));
00956 
00957             flickAnimationX->setEasingCurve(QEasingCurve::OutCirc);
00958             flickAnimationY->setEasingCurve(QEasingCurve::OutCirc);
00959 
00960 
00961             fixupAnimation.groupX = new QSequentialAnimationGroup(widget.data());
00962             fixupAnimation.groupY = new QSequentialAnimationGroup(widget.data());
00963             fixupAnimation.startX  = new QPropertyAnimation(widget.data(),
00964                                                             xProp.toLatin1(), widget.data());
00965             fixupAnimation.startY  = new QPropertyAnimation(widget.data(),
00966                                                             yProp.toLatin1(), widget.data());
00967             fixupAnimation.endX = new QPropertyAnimation(widget.data(),
00968                                                          xProp.toLatin1(), widget.data());
00969             fixupAnimation.endY = new QPropertyAnimation(widget.data(),
00970                                                          yProp.toLatin1(), widget.data());
00971             fixupAnimation.groupX->addAnimation(
00972                 fixupAnimation.startX);
00973             fixupAnimation.groupY->addAnimation(
00974                 fixupAnimation.startY);
00975             fixupAnimation.groupX->addAnimation(
00976                 fixupAnimation.endX);
00977             fixupAnimation.groupY->addAnimation(
00978                 fixupAnimation.endY);
00979 
00980             fixupAnimation.startX->setEasingCurve(QEasingCurve::InQuad);
00981             fixupAnimation.endX->setEasingCurve(QEasingCurve::OutQuint);
00982             fixupAnimation.startY->setEasingCurve(QEasingCurve::InQuad);
00983             fixupAnimation.endY->setEasingCurve(QEasingCurve::OutQuint);
00984 
00985             fixupAnimation.snapX = new QPropertyAnimation(widget.data(),
00986                                                           xProp.toLatin1(), widget.data());
00987             fixupAnimation.snapY = new QPropertyAnimation(widget.data(),
00988                                                           yProp.toLatin1(), widget.data());
00989             fixupAnimation.snapX->setEasingCurve(QEasingCurve::InOutQuad);
00990             fixupAnimation.snapY->setEasingCurve(QEasingCurve::InOutQuad);
00991 
00992             QObject::connect(fixupAnimation.groupX,
00993                              SIGNAL(stateChanged(QAbstractAnimation::State,
00994                                                  QAbstractAnimation::State)),
00995                              q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
00996                                                           QAbstractAnimation::State)));
00997             QObject::connect(fixupAnimation.groupY,
00998                              SIGNAL(stateChanged(QAbstractAnimation::State,
00999                                                  QAbstractAnimation::State)),
01000                              q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
01001                                                           QAbstractAnimation::State)));
01002 
01003             directMoveAnimation = new QPropertyAnimation(q,
01004                                                          "scrollPosition",
01005                                                          q);
01006             QObject::connect(directMoveAnimation, SIGNAL(finished()),
01007                              q, SLOT(fixupX()));
01008             QObject::connect(directMoveAnimation, SIGNAL(finished()),
01009                              q, SLOT(fixupY()));
01010             QObject::connect(directMoveAnimation,
01011                              SIGNAL(stateChanged(QAbstractAnimation::State,
01012                                                  QAbstractAnimation::State)),
01013                              q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
01014                                                           QAbstractAnimation::State)));
01015             directMoveAnimation->setEasingCurve(QEasingCurve::OutCirc);
01016         }
01017     }
01018 
01019     void deleteFlickAnimations()
01020     {
01021         if (flickAnimationX)
01022             flickAnimationX->stop();
01023         if (flickAnimationY)
01024             flickAnimationY->stop();
01025         delete flickAnimationX;
01026         delete flickAnimationY;
01027         delete fixupAnimation.groupX;
01028         delete fixupAnimation.groupY;
01029         delete directMoveAnimation;
01030         delete fixupAnimation.snapX;
01031         delete fixupAnimation.snapY;
01032     }
01033 
01034     void setScrollX()
01035     {
01036         if (horizontalScrollBarPolicy != Qt::ScrollBarAlwaysOff) {
01037             horizontalScrollBar->blockSignals(true);
01038             horizontalScrollBar->setValue(-widget.data()->pos().x()/10.);
01039             horizontalScrollBar->blockSignals(false);
01040         }
01041     }
01042 
01043     void setScrollY()
01044     {
01045         if (verticalScrollBarPolicy != Qt::ScrollBarAlwaysOff) {
01046             verticalScrollBar->blockSignals(true);
01047             verticalScrollBar->setValue(-widget.data()->pos().y()/10.);
01048             verticalScrollBar->blockSignals(false);
01049         }
01050     }
01051 
01052     ScrollWidget *q;
01053     QGraphicsWidget *scrollingWidget;
01054     QWeakPointer<QGraphicsWidget> widget;
01055     Plasma::Svg *borderSvg;
01056     Plasma::SvgWidget *topBorder;
01057     Plasma::SvgWidget *bottomBorder;
01058     Plasma::SvgWidget *leftBorder;
01059     Plasma::SvgWidget *rightBorder;
01060     QGraphicsGridLayout *layout;
01061     ScrollBar *verticalScrollBar;
01062     Qt::ScrollBarPolicy verticalScrollBarPolicy;
01063     ScrollBar *horizontalScrollBar;
01064     Qt::ScrollBarPolicy horizontalScrollBarPolicy;
01065     QString styleSheet;
01066     QWeakPointer<QGraphicsWidget> widgetToBeVisible;
01067     QRectF rectToBeVisible;
01068     QPointF dragHandleClicked;
01069     bool dragging;
01070     QTimer *adjustScrollbarsTimer;
01071     QTimer *wheelTimer;
01072 
01073     QPointF pressPos;
01074     QPointF pressScrollPos;
01075     QPointF velocity;
01076     QPointF lastPos;
01077     QTime pressTime;
01078     QTime lastPosTime;
01079     QPropertyAnimation *flickAnimationX;
01080     QPropertyAnimation *flickAnimationY;
01081     struct {
01082         QAnimationGroup *groupX;
01083         QPropertyAnimation *startX;
01084         QPropertyAnimation *endX;
01085 
01086         QAnimationGroup *groupY;
01087         QPropertyAnimation *startY;
01088         QPropertyAnimation *endY;
01089 
01090         QPropertyAnimation *snapX;
01091         QPropertyAnimation *snapY;
01092     } fixupAnimation;
01093     QPropertyAnimation *directMoveAnimation;
01094     QSizeF snapSize;
01095     bool stealEvent;
01096     bool hasOvershoot;
01097     bool overflowBordersVisible;
01098 
01099     Qt::Alignment alignment;
01100 
01101     Gesture multitouchGesture;
01102 
01103     bool hasContentsProperty;
01104     bool hasOffsetProperty;
01105     bool hasXProperty;
01106     bool hasYProperty;
01107 };
01108 
01109 
01110 ScrollWidget::ScrollWidget(QGraphicsItem *parent)
01111     : QGraphicsWidget(parent),
01112       d(new ScrollWidgetPrivate(this))
01113 {
01114     d->commonConstructor();
01115 }
01116 
01117 ScrollWidget::ScrollWidget(QGraphicsWidget *parent)
01118     : QGraphicsWidget(parent),
01119       d(new ScrollWidgetPrivate(this))
01120 {
01121     d->commonConstructor();
01122 }
01123 
01124 ScrollWidget::~ScrollWidget()
01125 {
01126     delete d;
01127 }
01128 
01129 void ScrollWidget::setWidget(QGraphicsWidget *widget)
01130 {
01131     if (d->widget && d->widget.data() != widget) {
01132         d->deleteFlickAnimations();
01133         d->widget.data()->removeEventFilter(this);
01134         delete d->widget.data();
01135     }
01136 
01137     d->widget = widget;
01138     //it's not good it's setting a size policy here, but it's done to be retrocompatible with older applications
01139     if (widget) {
01140         d->hasContentsProperty = widget->property("contentsSize").isValid();
01141         d->hasOffsetProperty = widget->property("scrollPosition").isValid();
01142         d->hasXProperty = widget->property("scrollPositionX").isValid();
01143         d->hasYProperty = widget->property("scrollPositionY").isValid();
01144         d->createFlickAnimations();
01145 
01146         connect(widget, SIGNAL(xChanged()), this, SLOT(setScrollX()));
01147         connect(widget, SIGNAL(yChanged()), this, SLOT(setScrollY()));
01148         widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
01149         widget->setParentItem(d->scrollingWidget);
01150         widget->setPos(d->minXExtent(), d->minYExtent());
01151         widget->installEventFilter(this);
01152         d->adjustScrollbarsTimer->start(200);
01153     }
01154 }
01155 
01156 QGraphicsWidget *ScrollWidget::widget() const
01157 {
01158     return d->widget.data();
01159 }
01160 
01161 
01162 void ScrollWidget::setHorizontalScrollBarPolicy(const Qt::ScrollBarPolicy policy)
01163 {
01164     d->horizontalScrollBarPolicy = policy;
01165 }
01166 
01167 
01168 Qt::ScrollBarPolicy ScrollWidget::horizontalScrollBarPolicy() const
01169 {
01170     return d->horizontalScrollBarPolicy;
01171 }
01172 
01173 
01174 void ScrollWidget::setVerticalScrollBarPolicy(const Qt::ScrollBarPolicy policy)
01175 {
01176     d->verticalScrollBarPolicy = policy;
01177 }
01178 
01179 Qt::ScrollBarPolicy ScrollWidget::verticalScrollBarPolicy() const
01180 {
01181     return d->verticalScrollBarPolicy;
01182 }
01183 
01184 bool ScrollWidget::overflowBordersVisible() const
01185 {
01186     return d->overflowBordersVisible;
01187 }
01188 
01189 void ScrollWidget::setOverflowBordersVisible(const bool visible)
01190 {
01191     if (d->overflowBordersVisible == visible) {
01192         return;
01193     }
01194 
01195     d->overflowBordersVisible = visible;
01196     d->adjustScrollbars();
01197 }
01198 
01199 void ScrollWidget::ensureRectVisible(const QRectF &rect)
01200 {
01201     if (!d->widget) {
01202         return;
01203     }
01204 
01205     d->rectToBeVisible = rect;
01206     d->makeRectVisible();
01207 }
01208 
01209 void ScrollWidget::ensureItemVisible(QGraphicsItem *item)
01210 {
01211     if (!d->widget || !item) {
01212         return;
01213     }
01214 
01215     QGraphicsItem *parentOfItem = item->parentItem();
01216     while (parentOfItem != d->widget.data()) {
01217         if (!parentOfItem) {
01218             return;
01219         }
01220 
01221         parentOfItem = parentOfItem->parentItem();
01222     }
01223 
01224     //since we can't ensure it'll stay alive we can delay only if it's a qgraphicswidget
01225     QGraphicsWidget *widget = qgraphicsitem_cast<QGraphicsWidget *>(item);
01226     if (widget) {
01227         d->widgetToBeVisible = widget;
01228 
01229         // We need to wait for the parent item to resize...
01230         QTimer::singleShot(0, this, SLOT(makeItemVisible()));
01231     } else {
01232         d->makeItemVisible(item);
01233     }
01234 }
01235 
01236 #ifndef KDE_NO_DEPRECATED
01237 void ScrollWidget::registerAsDragHandle(QGraphicsWidget *item)
01238 {
01239     Q_UNUSED(item);
01240     return;
01241 }
01242 #endif
01243 
01244 #ifndef KDE_NO_DEPRECATED
01245 void ScrollWidget::unregisterAsDragHandle(QGraphicsWidget *item)
01246 {
01247     Q_UNUSED(item);
01248     return;
01249 }
01250 #endif
01251 
01252 QRectF ScrollWidget::viewportGeometry() const
01253 {
01254     QRectF result;
01255     if (!d->widget) {
01256         return result;
01257     }
01258 
01259     return d->scrollingWidget->boundingRect();
01260 }
01261 
01262 QSizeF ScrollWidget::contentsSize() const
01263 {
01264     if (d->widget) {
01265         if (d->hasContentsProperty) {
01266             QVariant var = d->widget.data()->property("contentsSize");
01267             return var.toSizeF();
01268         } else
01269             return d->widget.data()->size();
01270     }
01271     return QSizeF();
01272 }
01273 
01274 void ScrollWidget::setScrollPosition(const QPointF &position)
01275 {
01276     if (d->widget) {
01277         if (d->hasOffsetProperty)
01278             d->widget.data()->setProperty("scrollPosition", position);
01279         else
01280             d->widget.data()->setPos(-position.toPoint());
01281     }
01282 }
01283 
01284 QPointF ScrollWidget::scrollPosition() const
01285 {
01286     if (d->widget) {
01287         if (d->hasOffsetProperty) {
01288             QVariant var = d->widget.data()->property("scrollPosition");
01289             return var.toPointF();
01290         } else {
01291             return -d->widget.data()->pos();
01292         }
01293     }
01294     return QPointF();
01295 }
01296 
01297 void ScrollWidget::setSnapSize(const QSizeF &size)
01298 {
01299     d->snapSize = size;
01300 }
01301 
01302 QSizeF ScrollWidget::snapSize() const
01303 {
01304     return d->snapSize;
01305 }
01306 
01307 void ScrollWidget::setStyleSheet(const QString &styleSheet)
01308 {
01309     d->styleSheet = styleSheet;
01310     d->verticalScrollBar->setStyleSheet(styleSheet);
01311     d->horizontalScrollBar->setStyleSheet(styleSheet);
01312 }
01313 
01314 QString ScrollWidget::styleSheet() const
01315 {
01316     return d->styleSheet;
01317 }
01318 
01319 QWidget *ScrollWidget::nativeWidget() const
01320 {
01321     return 0;
01322 }
01323 
01324 void ScrollWidget::focusInEvent(QFocusEvent *event)
01325 {
01326     Q_UNUSED(event)
01327 
01328     if (d->widget) {
01329         d->widget.data()->setFocus();
01330     }
01331 }
01332 
01333 
01334 void ScrollWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
01335 {
01336     if (!d->widget) {
01337         QGraphicsWidget::resizeEvent(event);
01338         return;
01339     }
01340 
01341     d->adjustScrollbarsTimer->start(200);
01342 
01343     //if topBorder exists bottomBorder too
01344     if (d->topBorder) {
01345         d->topBorder->resize(event->newSize().width(), d->topBorder->size().height());
01346         d->bottomBorder->resize(event->newSize().width(), d->bottomBorder->size().height());
01347         d->bottomBorder->setPos(0, event->newSize().height() - d->bottomBorder->size().height());
01348     }
01349     if (d->leftBorder) {
01350         d->leftBorder->resize(d->leftBorder->size().width(), event->newSize().height());
01351         d->rightBorder->resize(d->rightBorder->size().width(), event->newSize().height());
01352         d->rightBorder->setPos(event->newSize().width() - d->rightBorder->size().width(), 0);
01353     }
01354 
01355     QGraphicsWidget::resizeEvent(event);
01356 }
01357 
01358 void ScrollWidget::keyPressEvent(QKeyEvent *event)
01359 {
01360     d->handleKeyPressEvent(event);
01361 }
01362 
01363 void ScrollWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
01364 {
01365     if (!d->widget) {
01366         return;
01367     }
01368 
01369     d->handleMouseMoveEvent(event);
01370     event->accept();
01371 
01372     return QGraphicsWidget::mouseMoveEvent(event);
01373 }
01374 
01375 void ScrollWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
01376 {
01377     if (!d->widget) {
01378         return;
01379     } else if (!d->canYFlick() && !d->canXFlick()) {
01380         event->ignore();
01381         return;
01382     }
01383 
01384     d->handleMousePressEvent(event);
01385 
01386     if (event->button() == Qt::LeftButton) {
01387         event->accept();
01388     } else {
01389         QGraphicsWidget::mousePressEvent(event);
01390     }
01391 }
01392 
01393 void ScrollWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
01394 {
01395     if (!d->widget) {
01396         return;
01397     }
01398 
01399     d->handleMouseReleaseEvent(event);
01400     event->accept();
01401 }
01402 
01403 void ScrollWidget::wheelEvent(QGraphicsSceneWheelEvent *event)
01404 {
01405     if (!d->widget) {
01406         return;
01407     } else if (!d->canYFlick() && !d->canXFlick()) {
01408         event->ignore();
01409         return;
01410     }
01411     d->handleWheelEvent(event);
01412     event->accept();
01413 }
01414 
01415 bool ScrollWidget::eventFilter(QObject *watched, QEvent *event)
01416 {
01417     if (!d->widget) {
01418         return false;
01419     }
01420 
01421     if (watched == d->scrollingWidget && (event->type() == QEvent::GraphicsSceneResize ||
01422          event->type() == QEvent::Move)) {
01423         emit viewportGeometryChanged(viewportGeometry());
01424     } else if (watched == d->widget.data() && event->type() == QEvent::GraphicsSceneResize) {
01425         d->stopAnimations();
01426         d->adjustScrollbarsTimer->start(200);
01427         updateGeometry();
01428 
01429         QPointF newPos = d->widget.data()->pos();
01430         if (d->widget.data()->size().width() <= viewportGeometry().width()) {
01431             newPos.setX(d->minXExtent());
01432         }
01433         if (d->widget.data()->size().height() <= viewportGeometry().height()) {
01434             newPos.setY(d->minYExtent());
01435         }
01436         //check if the content is visible
01437         if (d->widget.data()->geometry().right() < 0) {
01438             newPos.setX(-d->widget.data()->geometry().width()+viewportGeometry().width());
01439         }
01440         if (d->widget.data()->geometry().bottom() < 0) {
01441             newPos.setY(-d->widget.data()->geometry().height()+viewportGeometry().height());
01442         }
01443         d->widget.data()->setPos(newPos);
01444 
01445     } else if (watched == d->widget.data() && event->type() == QEvent::GraphicsSceneMove) {
01446         d->horizontalScrollBar->blockSignals(true);
01447         d->verticalScrollBar->blockSignals(true);
01448         d->horizontalScrollBar->setValue(-d->widget.data()->pos().x()/10);
01449         d->verticalScrollBar->setValue(-d->widget.data()->pos().y()/10);
01450         d->horizontalScrollBar->blockSignals(false);
01451         d->verticalScrollBar->blockSignals(false);
01452     }
01453 
01454     return false;
01455 }
01456 
01457 QSizeF ScrollWidget::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
01458 {
01459     if (!d->widget || which == Qt::MaximumSize) {
01460         return QGraphicsWidget::sizeHint(which, constraint);
01461     //FIXME: it should ake the minimum hint of the contained widget, but the result is in a ridiculously big widget
01462     } else if (which == Qt::MinimumSize) {
01463         return QSizeF(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous);
01464     }
01465 
01466     QSizeF hint = d->widget.data()->effectiveSizeHint(which, constraint);
01467     if (d->horizontalScrollBar && d->horizontalScrollBar->isVisible()) {
01468         hint += QSize(0, d->horizontalScrollBar->size().height());
01469     }
01470     if (d->verticalScrollBar && d->verticalScrollBar->isVisible()) {
01471         hint += QSize(d->verticalScrollBar->size().width(), 0);
01472     }
01473 
01474     return hint;
01475 }
01476 
01477 
01478 bool ScrollWidget::sceneEventFilter(QGraphicsItem *i, QEvent *e)
01479 {
01480     //only the scrolling widget and its children
01481     if (!d->widget.data() ||
01482         (!d->scrollingWidget->isAncestorOf(i) && i != d->scrollingWidget) ||
01483         i == d->horizontalScrollBar || i == d->verticalScrollBar) {
01484         return false;
01485     }
01486 
01487     if (i->isWidget()) {
01488         Plasma::Label *label = dynamic_cast<Plasma::Label *>(static_cast<QGraphicsWidget *>(i));
01489         if (label && (label->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) {
01490             return false;
01491         }
01492 
01493         Plasma::TextEdit *textEdit = dynamic_cast<Plasma::TextEdit *>(static_cast<QGraphicsWidget *>(i));
01494         if (textEdit && (textEdit->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) {
01495             return false;
01496         }
01497 
01498         Plasma::TextBrowser *textBrowser= dynamic_cast<Plasma::TextBrowser *>(static_cast<QGraphicsWidget *>(i));
01499         if (textBrowser && (textBrowser->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) {
01500             return false;
01501         }
01502     }
01503 
01504     bool stealThisEvent = d->stealEvent;
01505     //still pass around mouse moves: try to make still possible to make items start a drag event. thi could be either necessary or annoying, let's see how it goes. (add QEvent::GraphicsSceneMouseMove to block them)
01506     stealThisEvent &= (e->type() == QEvent::GraphicsSceneMousePress ||
01507                        e->type() == QEvent::GraphicsSceneMouseRelease);
01508 #if DEBUG
01509     qDebug()<<"sceneEventFilter = " <<i<<", "
01510             <<QTime::currentTime().toString(QString::fromLatin1("hh:mm:ss.zzz"));
01511 #endif
01512     switch (e->type()) {
01513     case QEvent::GraphicsSceneMousePress:
01514         d->handleMousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(e));
01515         break;
01516     case QEvent::GraphicsSceneMouseMove:
01517         d->handleMouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(e));
01518         break;
01519     case QEvent::GraphicsSceneMouseRelease:
01520         d->handleMouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(e));
01521         break;
01522 
01523     //Multitouch related events, we actually need only TouchUpdate
01524     case QEvent::TouchUpdate: {
01525         QList<QTouchEvent::TouchPoint> touchPoints = static_cast<QTouchEvent *>(e)->touchPoints();
01526         if (touchPoints.count() == 2) {
01527             const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
01528             const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
01529             const QLineF line0(touchPoint0.lastPos(), touchPoint1.lastPos());
01530             const QLineF line1(touchPoint0.pos(), touchPoint1.pos());
01531             const QLineF startLine(touchPoint0.startPos(), touchPoint1.startPos());
01532             const QPointF point = line1.pointAt(0.5);
01533             const QPointF lastPoint = line0.pointAt(0.5);
01534 
01535             if (d->multitouchGesture == ScrollWidgetPrivate::GestureNone) {
01536                 d->multitouchGesture = ScrollWidgetPrivate::GestureUndefined;
01537             }
01538             if (d->multitouchGesture == ScrollWidgetPrivate::GestureUndefined) {
01539                 const int zoomDistance = qAbs(line1.length() - startLine.length());
01540                 const int dragDistance = (startLine.pointAt(0.5) - point).manhattanLength();
01541 
01542                 if (zoomDistance - dragDistance > 30) {
01543                     d->multitouchGesture = ScrollWidgetPrivate::GestureZoom;
01544                 } else if (dragDistance - zoomDistance > 30) {
01545                     d->multitouchGesture = ScrollWidgetPrivate::GestureScroll;
01546                 }
01547             }
01548 
01549             if (d->multitouchGesture ==  ScrollWidgetPrivate::GestureScroll) {
01550                 QGraphicsSceneMouseEvent fakeEvent;
01551                 fakeEvent.setPos(point);
01552                 fakeEvent.setLastPos(lastPoint);
01553                 d->handleMouseMoveEvent(&fakeEvent);
01554             } else if (d->multitouchGesture == ScrollWidgetPrivate::GestureZoom) {
01555                 if (d->widget && d->widget.data()->property("zoomFactor").isValid()) {
01556                     qreal scaleFactor = 1;
01557                     if (line0.length() > 0) {
01558                         scaleFactor = line1.length() / line0.length();
01559                     }
01560 
01561                     qreal zoom = d->widget.data()->property("zoomFactor").toReal();
01562                     d->widget.data()->setProperty("zoomFactor", zoom * scaleFactor);
01563                 }
01564             }
01565         }
01566         break;
01567     }
01568     default:
01569         break;
01570     }
01571     if (stealThisEvent)
01572         return true;
01573     return QGraphicsWidget::sceneEventFilter(i, e);
01574 }
01575 
01576 void Plasma::ScrollWidget::setAlignment(Qt::Alignment align)
01577 {
01578     d->alignment = align;
01579     if (d->widget.data() &&
01580         d->widget.data()->isVisible()) {
01581         d->widget.data()->setPos(d->minXExtent(),
01582                                  d->minYExtent());
01583     }
01584 }
01585 
01586 Qt::Alignment Plasma::ScrollWidget::alignment() const
01587 {
01588     return d->alignment;
01589 }
01590 
01591 void ScrollWidget::setOverShoot(bool enable)
01592 {
01593     d->hasOvershoot = enable;
01594 }
01595 
01596 bool ScrollWidget::hasOverShoot() const
01597 {
01598     return d->hasOvershoot;
01599 }
01600 
01601 } // namespace Plasma
01602 
01603 
01604 #include <scrollwidget.moc>
01605 

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal