• 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             return;
00612 
00613         QPointF start = q->scrollPosition();
00614         QPointF end = start;
00615 
00616         qreal step = 100;
00617 
00618         switch (event->key()) {
00619         case Qt::Key_Left:
00620             if (canXFlick()) {
00621                 end += QPointF(-step, 0);
00622             }
00623             break;
00624         case Qt::Key_Right:
00625             if (canXFlick()) {
00626                 end += QPointF(step, 0);
00627             }
00628             break;
00629         case Qt::Key_Up:
00630             if (canYFlick()) {
00631                 end += QPointF(0, -step);
00632             }
00633             break;
00634         case Qt::Key_Down:
00635             if (canYFlick()) {
00636                 end += QPointF(0, step);
00637             }
00638             break;
00639         default:
00640             break;
00641         }
00642 
00643         fixupAnimation.groupX->stop();
00644         fixupAnimation.groupY->stop();
00645         fixupAnimation.snapX->stop();
00646         fixupAnimation.snapY->stop();
00647         directMoveAnimation->setStartValue(start);
00648         directMoveAnimation->setEndValue(end);
00649         directMoveAnimation->setDuration(200);
00650         directMoveAnimation->start();
00651     }
00652 
00653     void handleMousePressEvent(QGraphicsSceneMouseEvent *event)
00654     {
00655         lastPos = QPoint();
00656         lastPosTime = QTime::currentTime();
00657         pressPos = event->scenePos();
00658         pressScrollPos = -q->scrollPosition();
00659         pressTime = QTime::currentTime();
00660         velocity = QPointF();
00661         stopAnimations();
00662     }
00663 
00664     void handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
00665     {
00666         if (lastPosTime.isNull())
00667             return;
00668         bool rejectY = false;
00669         bool rejectX = false;
00670         bool moved = false;
00671 
00672         if (canYFlick()) {
00673             int dy = int(event->scenePos().y() - pressPos.y());
00674             if (qAbs(dy) > KGlobalSettings::dndEventDelay() || elapsed(pressTime) > 200) {
00675                 qreal newY = dy + pressScrollPos.y();
00676                 const qreal minY = minYExtent();
00677                 const qreal maxY = maxYExtent();
00678                 if (newY > minY)
00679                     newY = minY + (newY - minY) / 2;
00680                 if (newY < maxY && maxY - minY <= 0)
00681                     newY = maxY + (newY - maxY) / 2;
00682                 if (!hasOvershoot && (newY > minY || newY < maxY)) {
00683                     if (newY > minY)
00684                         newY = minY;
00685                     else if (newY < maxY)
00686                         newY = maxY;
00687                     else
00688                         rejectY = true;
00689                 }
00690                 if (!rejectY && stealEvent) {
00691                     setWidgetY(qRound(newY));
00692                     moved = true;
00693                 }
00694                 if (qAbs(dy) > KGlobalSettings::dndEventDelay())
00695                     stealEvent = true;
00696             }
00697         }
00698 
00699         if (canXFlick()) {
00700             int dx = int(event->scenePos().x() - pressPos.x());
00701             if (qAbs(dx) > KGlobalSettings::dndEventDelay() || elapsed(pressTime) > 200) {
00702                 qreal newX = dx + pressScrollPos.x();
00703                 const qreal minX = minXExtent();
00704                 const qreal maxX = maxXExtent();
00705                 if (newX > minX)
00706                     newX = minX + (newX - minX) / 2;
00707                 if (newX < maxX && maxX - minX <= 0)
00708                     newX = maxX + (newX - maxX) / 2;
00709                 if (!hasOvershoot && (newX > minX || newX < maxX)) {
00710                     if (newX > minX)
00711                         newX = minX;
00712                     else if (newX < maxX)
00713                         newX = maxX;
00714                     else
00715                         rejectX = true;
00716                 }
00717                 if (!rejectX && stealEvent) {
00718                     setWidgetX(qRound(newX));
00719                     moved = true;
00720                 }
00721 
00722                 if (qAbs(dx) > KGlobalSettings::dndEventDelay())
00723                     stealEvent = true;
00724             }
00725         }
00726 
00727         if (!lastPos.isNull()) {
00728             qreal msecs = qreal(restart(lastPosTime));
00729             qreal elapsed =  msecs / 1000.;
00730 #if IGNORE_SUSPICIOUS_MOVES
00731             if (msecs > 3) {
00732 #endif
00733             if (elapsed <= 0)
00734                 elapsed = 1;
00735             if (canYFlick()) {
00736                 qreal diff = event->scenePos().y() - lastPos.y();
00737                 // average to reduce the effect of spurious moves
00738                 velocity.setY( velocity.y() + (diff / elapsed) );
00739                 velocity.setY( velocity.y() / 2 );
00740             }
00741 
00742             if (canXFlick()) {
00743                 qreal diff = event->scenePos().x() - lastPos.x();
00744                 // average to reduce the effect of spurious moves
00745                 velocity.setX( velocity.x() + (diff / elapsed) );
00746                 velocity.setX( velocity.x() / 2 );
00747             }
00748 #if IGNORE_SUSPICIOUS_MOVES
00749             }
00750 #endif
00751         }
00752 
00753         if (rejectX) velocity.setX(0);
00754         if (rejectY) velocity.setY(0);
00755 
00756         lastPos = event->scenePos();
00757     }
00758 
00759     void handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00760     {
00761         stealEvent = false;
00762         if (lastPosTime.isNull())
00763             return;
00764 
00765         if (elapsed(lastPosTime) > 100) {
00766             // if we drag then pause before release we should not cause a flick.
00767             velocity = QPointF();
00768         }
00769 
00770         if (qAbs(velocity.y()) > 10 &&
00771             qAbs(event->scenePos().y() - pressPos.y()) > FlickThreshold) {
00772             qreal vVelocity = velocity.y();
00773             // Minimum velocity to avoid annoyingly slow flicks.
00774             if (qAbs(vVelocity) < MinimumFlickVelocity)
00775                 vVelocity = vVelocity < 0 ? -MinimumFlickVelocity : MinimumFlickVelocity;
00776             flickY(vVelocity);
00777         } else {
00778             fixupY();
00779         }
00780 
00781         if (qAbs(velocity.x()) > 10 &&
00782             qAbs(event->scenePos().x() - pressPos.x()) > FlickThreshold) {
00783             qreal hVelocity = velocity.x();
00784             // Minimum velocity to avoid annoyingly slow flicks.
00785             if (qAbs(hVelocity) < MinimumFlickVelocity)
00786                 hVelocity = hVelocity < 0 ? -MinimumFlickVelocity : MinimumFlickVelocity;
00787             flickX(hVelocity);
00788         } else {
00789             fixupX();
00790         }
00791 
00792         lastPosTime = QTime();
00793     }
00794 
00795     void handleWheelEvent(QGraphicsSceneWheelEvent *event)
00796     {
00797         //only scroll when the animation is done, this avoids to receive too many events and getting mad when they arrive from a touchpad
00798         if (!widget.data() || wheelTimer->isActive()) {
00799             return;
00800         }
00801 
00802         QPointF start = q->scrollPosition();
00803         QPointF end = start;
00804 
00805         //At some point we should switch to
00806         // step = QApplication::wheelScrollLines() *
00807         //      (event->delta()/120) *
00808         //      scrollBar->singleStep();
00809         // which gives us exactly the number of lines to scroll but the issue
00810         // is that at this point we don't have any clue what a "line" is and if
00811         // we make it a pixel then scrolling by 3 (default) pixels will be
00812         // very painful
00813         qreal step = -event->delta()/3;
00814 
00815         //ifthe widget can scroll in a single axis and the wheel is the other one, scroll the other one
00816         Qt::Orientation orientation = event->orientation();
00817         if (orientation == Qt::Vertical) {
00818             if (!canYFlick() && canXFlick()) {
00819                 end += QPointF(step, 0);
00820             } else if (canYFlick()) {
00821                 end += QPointF(0, step);
00822             } else {
00823                 return;
00824             }
00825         } else {
00826             if (canYFlick() && !canXFlick()) {
00827                 end += QPointF(0, step);
00828             } else if (canXFlick()) {
00829                 end += QPointF(step, 0);
00830             } else {
00831                 return;
00832             }
00833         }
00834 
00835         fixupAnimation.groupX->stop();
00836         fixupAnimation.groupY->stop();
00837         fixupAnimation.snapX->stop();
00838         fixupAnimation.snapY->stop();
00839         directMoveAnimation->setStartValue(start);
00840         directMoveAnimation->setEndValue(end);
00841         directMoveAnimation->setDuration(200);
00842         directMoveAnimation->start();
00843         wheelTimer->start(50);
00844     }
00845 
00846     qreal minXExtent() const
00847     {
00848         if (alignment & Qt::AlignLeft)
00849             return 0;
00850         else {
00851             qreal vWidth = q->viewportGeometry().width();
00852             qreal cWidth = q->contentsSize().width();
00853             if (cWidth < vWidth) {
00854                 if (alignment & Qt::AlignRight)
00855                     return  vWidth - cWidth;
00856                 else if (alignment & Qt::AlignHCenter)
00857                     return vWidth / 2 - cWidth / 2;
00858             }
00859         }
00860 
00861         return 0;
00862     }
00863 
00864     qreal maxXExtent() const
00865     {
00866         return q->viewportGeometry().width() -
00867             q->contentsSize().width();
00868     }
00869 
00870     qreal minYExtent() const
00871     {
00872         if (alignment & Qt::AlignTop)
00873             return 0;
00874         else {
00875             qreal vHeight = q->viewportGeometry().height();
00876             qreal cHeight = q->contentsSize().height();
00877             if (cHeight < vHeight) {
00878                 if (alignment & Qt::AlignBottom)
00879                     return  vHeight - cHeight;
00880                 else if (alignment & Qt::AlignVCenter)
00881                     return vHeight / 2 - cHeight / 2;
00882             }
00883         }
00884 
00885         return 0;
00886     }
00887 
00888     qreal maxYExtent() const
00889     {
00890         return q->viewportGeometry().height() -
00891             q->contentsSize().height();
00892     }
00893 
00894     bool canXFlick() const
00895     {
00896         //make the thing feel quite "fixed" don't permit to flick when the contents size is less than the viewport
00897         return q->contentsSize().width() > q->viewportGeometry().width();
00898     }
00899 
00900     bool canYFlick() const
00901     {
00902         return q->contentsSize().height() > q->viewportGeometry().height();
00903     }
00904 
00905     int elapsed(const QTime &t) const
00906     {
00907         int n = t.msecsTo(QTime::currentTime());
00908         if (n < 0) // passed midnight
00909             n += 86400 * 1000;
00910         return n;
00911     }
00912 
00913     int restart(QTime &t) const
00914     {
00915         QTime time = QTime::currentTime();
00916         int n = t.msecsTo(time);
00917         if (n < 0) // passed midnight
00918             n += 86400*1000;
00919         t = time;
00920         return n;
00921     }
00922 
00923     void createFlickAnimations()
00924     {
00925         if (widget.data()) {
00926             QString xProp = QString::fromLatin1("x");
00927             QString yProp = QString::fromLatin1("y");
00928 
00929             if (hasXProperty)
00930                 xProp = QString::fromLatin1("scrollPositionX");
00931             if (hasYProperty)
00932                 yProp = QString::fromLatin1("scrollPositionY");
00933 
00934             flickAnimationX = new QPropertyAnimation(widget.data(),
00935                                                      xProp.toLatin1(), widget.data());
00936             flickAnimationY = new QPropertyAnimation(widget.data(),
00937                                                      yProp.toLatin1(), widget.data());
00938             QObject::connect(flickAnimationX, SIGNAL(finished()),
00939                              q, SLOT(fixupX()));
00940             QObject::connect(flickAnimationY, SIGNAL(finished()),
00941                              q, SLOT(fixupY()));
00942 
00943             QObject::connect(flickAnimationX,
00944                              SIGNAL(stateChanged(QAbstractAnimation::State,
00945                                                  QAbstractAnimation::State)),
00946                              q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
00947                                                           QAbstractAnimation::State)));
00948             QObject::connect(flickAnimationY,
00949                              SIGNAL(stateChanged(QAbstractAnimation::State,
00950                                                  QAbstractAnimation::State)),
00951                              q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
00952                                                           QAbstractAnimation::State)));
00953 
00954             flickAnimationX->setEasingCurve(QEasingCurve::OutCirc);
00955             flickAnimationY->setEasingCurve(QEasingCurve::OutCirc);
00956 
00957 
00958             fixupAnimation.groupX = new QSequentialAnimationGroup(widget.data());
00959             fixupAnimation.groupY = new QSequentialAnimationGroup(widget.data());
00960             fixupAnimation.startX  = new QPropertyAnimation(widget.data(),
00961                                                             xProp.toLatin1(), widget.data());
00962             fixupAnimation.startY  = new QPropertyAnimation(widget.data(),
00963                                                             yProp.toLatin1(), widget.data());
00964             fixupAnimation.endX = new QPropertyAnimation(widget.data(),
00965                                                          xProp.toLatin1(), widget.data());
00966             fixupAnimation.endY = new QPropertyAnimation(widget.data(),
00967                                                          yProp.toLatin1(), widget.data());
00968             fixupAnimation.groupX->addAnimation(
00969                 fixupAnimation.startX);
00970             fixupAnimation.groupY->addAnimation(
00971                 fixupAnimation.startY);
00972             fixupAnimation.groupX->addAnimation(
00973                 fixupAnimation.endX);
00974             fixupAnimation.groupY->addAnimation(
00975                 fixupAnimation.endY);
00976 
00977             fixupAnimation.startX->setEasingCurve(QEasingCurve::InQuad);
00978             fixupAnimation.endX->setEasingCurve(QEasingCurve::OutQuint);
00979             fixupAnimation.startY->setEasingCurve(QEasingCurve::InQuad);
00980             fixupAnimation.endY->setEasingCurve(QEasingCurve::OutQuint);
00981 
00982             fixupAnimation.snapX = new QPropertyAnimation(widget.data(),
00983                                                           xProp.toLatin1(), widget.data());
00984             fixupAnimation.snapY = new QPropertyAnimation(widget.data(),
00985                                                           yProp.toLatin1(), widget.data());
00986             fixupAnimation.snapX->setEasingCurve(QEasingCurve::InOutQuad);
00987             fixupAnimation.snapY->setEasingCurve(QEasingCurve::InOutQuad);
00988 
00989             QObject::connect(fixupAnimation.groupX,
00990                              SIGNAL(stateChanged(QAbstractAnimation::State,
00991                                                  QAbstractAnimation::State)),
00992                              q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
00993                                                           QAbstractAnimation::State)));
00994             QObject::connect(fixupAnimation.groupY,
00995                              SIGNAL(stateChanged(QAbstractAnimation::State,
00996                                                  QAbstractAnimation::State)),
00997                              q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
00998                                                           QAbstractAnimation::State)));
00999 
01000             directMoveAnimation = new QPropertyAnimation(q,
01001                                                          "scrollPosition",
01002                                                          q);
01003             QObject::connect(directMoveAnimation, SIGNAL(finished()),
01004                              q, SLOT(fixupX()));
01005             QObject::connect(directMoveAnimation, SIGNAL(finished()),
01006                              q, SLOT(fixupY()));
01007             QObject::connect(directMoveAnimation,
01008                              SIGNAL(stateChanged(QAbstractAnimation::State,
01009                                                  QAbstractAnimation::State)),
01010                              q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
01011                                                           QAbstractAnimation::State)));
01012             directMoveAnimation->setEasingCurve(QEasingCurve::OutCirc);
01013         }
01014     }
01015 
01016     void deleteFlickAnimations()
01017     {
01018         if (flickAnimationX)
01019             flickAnimationX->stop();
01020         if (flickAnimationY)
01021             flickAnimationY->stop();
01022         delete flickAnimationX;
01023         delete flickAnimationY;
01024         delete fixupAnimation.groupX;
01025         delete fixupAnimation.groupY;
01026         delete directMoveAnimation;
01027         delete fixupAnimation.snapX;
01028         delete fixupAnimation.snapY;
01029     }
01030 
01031     void setScrollX()
01032     {
01033         if (horizontalScrollBarPolicy != Qt::ScrollBarAlwaysOff) {
01034             horizontalScrollBar->blockSignals(true);
01035             horizontalScrollBar->setValue(-widget.data()->pos().x()/10.);
01036             horizontalScrollBar->blockSignals(false);
01037         }
01038     }
01039 
01040     void setScrollY()
01041     {
01042         if (verticalScrollBarPolicy != Qt::ScrollBarAlwaysOff) {
01043             verticalScrollBar->blockSignals(true);
01044             verticalScrollBar->setValue(-widget.data()->pos().y()/10.);
01045             verticalScrollBar->blockSignals(false);
01046         }
01047     }
01048 
01049     ScrollWidget *q;
01050     QGraphicsWidget *scrollingWidget;
01051     QWeakPointer<QGraphicsWidget> widget;
01052     Plasma::Svg *borderSvg;
01053     Plasma::SvgWidget *topBorder;
01054     Plasma::SvgWidget *bottomBorder;
01055     Plasma::SvgWidget *leftBorder;
01056     Plasma::SvgWidget *rightBorder;
01057     QGraphicsGridLayout *layout;
01058     ScrollBar *verticalScrollBar;
01059     Qt::ScrollBarPolicy verticalScrollBarPolicy;
01060     ScrollBar *horizontalScrollBar;
01061     Qt::ScrollBarPolicy horizontalScrollBarPolicy;
01062     QString styleSheet;
01063     QWeakPointer<QGraphicsWidget> widgetToBeVisible;
01064     QRectF rectToBeVisible;
01065     QPointF dragHandleClicked;
01066     bool dragging;
01067     QTimer *adjustScrollbarsTimer;
01068     QTimer *wheelTimer;
01069 
01070     QPointF pressPos;
01071     QPointF pressScrollPos;
01072     QPointF velocity;
01073     QPointF lastPos;
01074     QTime pressTime;
01075     QTime lastPosTime;
01076     QPropertyAnimation *flickAnimationX;
01077     QPropertyAnimation *flickAnimationY;
01078     struct {
01079         QAnimationGroup *groupX;
01080         QPropertyAnimation *startX;
01081         QPropertyAnimation *endX;
01082 
01083         QAnimationGroup *groupY;
01084         QPropertyAnimation *startY;
01085         QPropertyAnimation *endY;
01086 
01087         QPropertyAnimation *snapX;
01088         QPropertyAnimation *snapY;
01089     } fixupAnimation;
01090     QPropertyAnimation *directMoveAnimation;
01091     QSizeF snapSize;
01092     bool stealEvent;
01093     bool hasOvershoot;
01094     bool overflowBordersVisible;
01095 
01096     Qt::Alignment alignment;
01097 
01098     Gesture multitouchGesture;
01099 
01100     bool hasContentsProperty;
01101     bool hasOffsetProperty;
01102     bool hasXProperty;
01103     bool hasYProperty;
01104 };
01105 
01106 
01107 ScrollWidget::ScrollWidget(QGraphicsItem *parent)
01108     : QGraphicsWidget(parent),
01109       d(new ScrollWidgetPrivate(this))
01110 {
01111     d->commonConstructor();
01112 }
01113 
01114 ScrollWidget::ScrollWidget(QGraphicsWidget *parent)
01115     : QGraphicsWidget(parent),
01116       d(new ScrollWidgetPrivate(this))
01117 {
01118     d->commonConstructor();
01119 }
01120 
01121 ScrollWidget::~ScrollWidget()
01122 {
01123     delete d;
01124 }
01125 
01126 void ScrollWidget::setWidget(QGraphicsWidget *widget)
01127 {
01128     if (d->widget && d->widget.data() != widget) {
01129         d->deleteFlickAnimations();
01130         d->widget.data()->removeEventFilter(this);
01131         delete d->widget.data();
01132     }
01133 
01134     d->widget = widget;
01135     //it's not good it's setting a size policy here, but it's done to be retrocompatible with older applications
01136     if (widget) {
01137         d->hasContentsProperty = widget->property("contentsSize").isValid();
01138         d->hasOffsetProperty = widget->property("scrollPosition").isValid();
01139         d->hasXProperty = widget->property("scrollPositionX").isValid();
01140         d->hasYProperty = widget->property("scrollPositionY").isValid();
01141         d->createFlickAnimations();
01142 
01143         connect(widget, SIGNAL(xChanged()), this, SLOT(setScrollX()));
01144         connect(widget, SIGNAL(yChanged()), this, SLOT(setScrollY()));
01145         widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
01146         widget->setParentItem(d->scrollingWidget);
01147         widget->setPos(d->minXExtent(), d->minYExtent());
01148         widget->installEventFilter(this);
01149         d->adjustScrollbarsTimer->start(200);
01150     }
01151 }
01152 
01153 QGraphicsWidget *ScrollWidget::widget() const
01154 {
01155     return d->widget.data();
01156 }
01157 
01158 
01159 void ScrollWidget::setHorizontalScrollBarPolicy(const Qt::ScrollBarPolicy policy)
01160 {
01161     d->horizontalScrollBarPolicy = policy;
01162 }
01163 
01164 
01165 Qt::ScrollBarPolicy ScrollWidget::horizontalScrollBarPolicy() const
01166 {
01167     return d->horizontalScrollBarPolicy;
01168 }
01169 
01170 
01171 void ScrollWidget::setVerticalScrollBarPolicy(const Qt::ScrollBarPolicy policy)
01172 {
01173     d->verticalScrollBarPolicy = policy;
01174 }
01175 
01176 Qt::ScrollBarPolicy ScrollWidget::verticalScrollBarPolicy() const
01177 {
01178     return d->verticalScrollBarPolicy;
01179 }
01180 
01181 bool ScrollWidget::overflowBordersVisible() const
01182 {
01183     return d->overflowBordersVisible;
01184 }
01185 
01186 void ScrollWidget::setOverflowBordersVisible(const bool visible)
01187 {
01188     if (d->overflowBordersVisible == visible) {
01189         return;
01190     }
01191 
01192     d->overflowBordersVisible = visible;
01193     d->adjustScrollbars();
01194 }
01195 
01196 void ScrollWidget::ensureRectVisible(const QRectF &rect)
01197 {
01198     if (!d->widget) {
01199         return;
01200     }
01201 
01202     d->rectToBeVisible = rect;
01203     d->makeRectVisible();
01204 }
01205 
01206 void ScrollWidget::ensureItemVisible(QGraphicsItem *item)
01207 {
01208     if (!d->widget || !item) {
01209         return;
01210     }
01211 
01212     QGraphicsItem *parentOfItem = item->parentItem();
01213     while (parentOfItem != d->widget.data()) {
01214         if (!parentOfItem) {
01215             return;
01216         }
01217 
01218         parentOfItem = parentOfItem->parentItem();
01219     }
01220 
01221     //since we can't ensure it'll stay alive we can delay only if it's a qgraphicswidget
01222     QGraphicsWidget *widget = qgraphicsitem_cast<QGraphicsWidget *>(item);
01223     if (widget) {
01224         d->widgetToBeVisible = widget;
01225 
01226         // We need to wait for the parent item to resize...
01227         QTimer::singleShot(0, this, SLOT(makeItemVisible()));
01228     } else {
01229         d->makeItemVisible(item);
01230     }
01231 }
01232 
01233 #ifndef KDE_NO_DEPRECATED
01234 void ScrollWidget::registerAsDragHandle(QGraphicsWidget *item)
01235 {
01236     Q_UNUSED(item);
01237     return;
01238 }
01239 #endif
01240 
01241 #ifndef KDE_NO_DEPRECATED
01242 void ScrollWidget::unregisterAsDragHandle(QGraphicsWidget *item)
01243 {
01244     Q_UNUSED(item);
01245     return;
01246 }
01247 #endif
01248 
01249 QRectF ScrollWidget::viewportGeometry() const
01250 {
01251     QRectF result;
01252     if (!d->widget) {
01253         return result;
01254     }
01255 
01256     return d->scrollingWidget->boundingRect();
01257 }
01258 
01259 QSizeF ScrollWidget::contentsSize() const
01260 {
01261     if (d->widget) {
01262         if (d->hasContentsProperty) {
01263             QVariant var = d->widget.data()->property("contentsSize");
01264             return var.toSizeF();
01265         } else
01266             return d->widget.data()->size();
01267     }
01268     return QSizeF();
01269 }
01270 
01271 void ScrollWidget::setScrollPosition(const QPointF &position)
01272 {
01273     if (d->widget) {
01274         if (d->hasOffsetProperty)
01275             d->widget.data()->setProperty("scrollPosition", position);
01276         else
01277             d->widget.data()->setPos(-position.toPoint());
01278     }
01279 }
01280 
01281 QPointF ScrollWidget::scrollPosition() const
01282 {
01283     if (d->widget) {
01284         if (d->hasOffsetProperty) {
01285             QVariant var = d->widget.data()->property("scrollPosition");
01286             return var.toPointF();
01287         } else {
01288             return -d->widget.data()->pos();
01289         }
01290     }
01291     return QPointF();
01292 }
01293 
01294 void ScrollWidget::setSnapSize(const QSizeF &size)
01295 {
01296     d->snapSize = size;
01297 }
01298 
01299 QSizeF ScrollWidget::snapSize() const
01300 {
01301     return d->snapSize;
01302 }
01303 
01304 void ScrollWidget::setStyleSheet(const QString &styleSheet)
01305 {
01306     d->styleSheet = styleSheet;
01307     d->verticalScrollBar->setStyleSheet(styleSheet);
01308     d->horizontalScrollBar->setStyleSheet(styleSheet);
01309 }
01310 
01311 QString ScrollWidget::styleSheet() const
01312 {
01313     return d->styleSheet;
01314 }
01315 
01316 QWidget *ScrollWidget::nativeWidget() const
01317 {
01318     return 0;
01319 }
01320 
01321 void ScrollWidget::focusInEvent(QFocusEvent *event)
01322 {
01323     Q_UNUSED(event)
01324 
01325     if (d->widget) {
01326         d->widget.data()->setFocus();
01327     }
01328 }
01329 
01330 
01331 void ScrollWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
01332 {
01333     if (!d->widget) {
01334         QGraphicsWidget::resizeEvent(event);
01335         return;
01336     }
01337 
01338     d->adjustScrollbarsTimer->start(200);
01339 
01340     //if topBorder exists bottomBorder too
01341     if (d->topBorder) {
01342         d->topBorder->resize(event->newSize().width(), d->topBorder->size().height());
01343         d->bottomBorder->resize(event->newSize().width(), d->bottomBorder->size().height());
01344         d->bottomBorder->setPos(0, event->newSize().height() - d->bottomBorder->size().height());
01345     }
01346     if (d->leftBorder) {
01347         d->leftBorder->resize(d->leftBorder->size().width(), event->newSize().height());
01348         d->rightBorder->resize(d->rightBorder->size().width(), event->newSize().height());
01349         d->rightBorder->setPos(event->newSize().width() - d->rightBorder->size().width(), 0);
01350     }
01351 
01352     QGraphicsWidget::resizeEvent(event);
01353 }
01354 
01355 void ScrollWidget::keyPressEvent(QKeyEvent *event)
01356 {
01357     d->handleKeyPressEvent(event);
01358 }
01359 
01360 void ScrollWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
01361 {
01362     if (!d->widget) {
01363         return;
01364     }
01365 
01366     d->handleMouseMoveEvent(event);
01367     event->accept();
01368 
01369     return QGraphicsWidget::mouseMoveEvent(event);
01370 }
01371 
01372 void ScrollWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
01373 {
01374     if (!d->widget) {
01375         return;
01376     } else if (!d->canYFlick() && !d->canXFlick()) {
01377         event->ignore();
01378         return;
01379     }
01380 
01381     d->handleMousePressEvent(event);
01382 
01383     if (event->button() == Qt::LeftButton) {
01384         event->accept();
01385     } else {
01386         QGraphicsWidget::mousePressEvent(event);
01387     }
01388 }
01389 
01390 void ScrollWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
01391 {
01392     if (!d->widget) {
01393         return;
01394     }
01395 
01396     d->handleMouseReleaseEvent(event);
01397     event->accept();
01398 }
01399 
01400 void ScrollWidget::wheelEvent(QGraphicsSceneWheelEvent *event)
01401 {
01402     if (!d->widget) {
01403         return;
01404     } else if (!d->canYFlick() && !d->canXFlick()) {
01405         event->ignore();
01406         return;
01407     }
01408     d->handleWheelEvent(event);
01409     event->accept();
01410 }
01411 
01412 bool ScrollWidget::eventFilter(QObject *watched, QEvent *event)
01413 {
01414     if (!d->widget) {
01415         return false;
01416     }
01417 
01418     if (watched == d->scrollingWidget && (event->type() == QEvent::GraphicsSceneResize ||
01419          event->type() == QEvent::Move)) {
01420         emit viewportGeometryChanged(viewportGeometry());
01421     } else if (watched == d->widget.data() && event->type() == QEvent::GraphicsSceneResize) {
01422         d->stopAnimations();
01423         d->adjustScrollbarsTimer->start(200);
01424         updateGeometry();
01425         ensureItemVisible(d->widget.data());
01426     } else if (watched == d->widget.data() && event->type() == QEvent::GraphicsSceneMove) {
01427         d->horizontalScrollBar->blockSignals(true);
01428         d->verticalScrollBar->blockSignals(true);
01429         d->horizontalScrollBar->setValue(-d->widget.data()->pos().x()/10);
01430         d->verticalScrollBar->setValue(-d->widget.data()->pos().y()/10);
01431         d->horizontalScrollBar->blockSignals(false);
01432         d->verticalScrollBar->blockSignals(false);
01433     }
01434 
01435     return false;
01436 }
01437 
01438 QSizeF ScrollWidget::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
01439 {
01440     if (!d->widget || which == Qt::MaximumSize) {
01441         return QGraphicsWidget::sizeHint(which, constraint);
01442     //FIXME: it should ake the minimum hint of the contained widget, but the result is in a ridiculously big widget
01443     } else if (which == Qt::MinimumSize) {
01444         return QSizeF(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous);
01445     }
01446 
01447     QSizeF hint = d->widget.data()->effectiveSizeHint(which, constraint);
01448     if (d->horizontalScrollBar && d->horizontalScrollBar->isVisible()) {
01449         hint += QSize(0, d->horizontalScrollBar->size().height());
01450     }
01451     if (d->verticalScrollBar && d->verticalScrollBar->isVisible()) {
01452         hint += QSize(d->verticalScrollBar->size().width(), 0);
01453     }
01454 
01455     return hint;
01456 }
01457 
01458 
01459 bool ScrollWidget::sceneEventFilter(QGraphicsItem *i, QEvent *e)
01460 {
01461     //only the scrolling widget and its children
01462     if (!d->widget.data() ||
01463         (!d->scrollingWidget->isAncestorOf(i) && i != d->scrollingWidget) ||
01464         i == d->horizontalScrollBar || i == d->verticalScrollBar) {
01465         return false;
01466     }
01467 
01468     if (i->isWidget()) {
01469         Plasma::Label *label = dynamic_cast<Plasma::Label *>(static_cast<QGraphicsWidget *>(i));
01470         if (label && (label->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) {
01471             return false;
01472         }
01473 
01474         Plasma::TextEdit *textEdit = dynamic_cast<Plasma::TextEdit *>(static_cast<QGraphicsWidget *>(i));
01475         if (textEdit && (textEdit->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) {
01476             return false;
01477         }
01478 
01479         Plasma::TextBrowser *textBrowser= dynamic_cast<Plasma::TextBrowser *>(static_cast<QGraphicsWidget *>(i));
01480         if (textBrowser && (textBrowser->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) {
01481             return false;
01482         }
01483     }
01484 
01485     bool stealThisEvent = d->stealEvent;
01486     //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)
01487     stealThisEvent &= (e->type() == QEvent::GraphicsSceneMousePress ||
01488                        e->type() == QEvent::GraphicsSceneMouseRelease);
01489 #if DEBUG
01490     qDebug()<<"sceneEventFilter = " <<i<<", "
01491             <<QTime::currentTime().toString(QString::fromLatin1("hh:mm:ss.zzz"));
01492 #endif
01493     switch (e->type()) {
01494     case QEvent::GraphicsSceneMousePress:
01495         d->handleMousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(e));
01496         break;
01497     case QEvent::GraphicsSceneMouseMove:
01498         d->handleMouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(e));
01499         break;
01500     case QEvent::GraphicsSceneMouseRelease:
01501         d->handleMouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(e));
01502         break;
01503 
01504     //Multitouch related events, we actually need only TouchUpdate
01505     case QEvent::TouchUpdate: {
01506         QList<QTouchEvent::TouchPoint> touchPoints = static_cast<QTouchEvent *>(e)->touchPoints();
01507         if (touchPoints.count() == 2) {
01508             const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
01509             const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
01510             const QLineF line0(touchPoint0.lastPos(), touchPoint1.lastPos());
01511             const QLineF line1(touchPoint0.pos(), touchPoint1.pos());
01512             const QLineF startLine(touchPoint0.startPos(), touchPoint1.startPos());
01513             const QPointF point = line1.pointAt(0.5);
01514             const QPointF lastPoint = line0.pointAt(0.5);
01515 
01516             if (d->multitouchGesture == ScrollWidgetPrivate::GestureNone) {
01517                 d->multitouchGesture = ScrollWidgetPrivate::GestureUndefined;
01518             }
01519             if (d->multitouchGesture == ScrollWidgetPrivate::GestureUndefined) {
01520                 const int zoomDistance = qAbs(line1.length() - startLine.length());
01521                 const int dragDistance = (startLine.pointAt(0.5) - point).manhattanLength();
01522 
01523                 if (zoomDistance - dragDistance > 30) {
01524                     d->multitouchGesture = ScrollWidgetPrivate::GestureZoom;
01525                 } else if (dragDistance - zoomDistance > 30) {
01526                     d->multitouchGesture = ScrollWidgetPrivate::GestureScroll;
01527                 }
01528             }
01529 
01530             if (d->multitouchGesture ==  ScrollWidgetPrivate::GestureScroll) {
01531                 QGraphicsSceneMouseEvent fakeEvent;
01532                 fakeEvent.setPos(point);
01533                 fakeEvent.setLastPos(lastPoint);
01534                 d->handleMouseMoveEvent(&fakeEvent);
01535             } else if (d->multitouchGesture == ScrollWidgetPrivate::GestureZoom) {
01536                 if (d->widget && d->widget.data()->property("zoomFactor").isValid()) {
01537                     qreal scaleFactor = 1;
01538                     if (line0.length() > 0) {
01539                         scaleFactor = line1.length() / line0.length();
01540                     }
01541 
01542                     qreal zoom = d->widget.data()->property("zoomFactor").toReal();
01543                     d->widget.data()->setProperty("zoomFactor", zoom * scaleFactor);
01544                 }
01545             }
01546         }
01547         break;
01548     }
01549     default:
01550         break;
01551     }
01552     if (stealThisEvent)
01553         return true;
01554     return QGraphicsWidget::sceneEventFilter(i, e);
01555 }
01556 
01557 void Plasma::ScrollWidget::setAlignment(Qt::Alignment align)
01558 {
01559     d->alignment = align;
01560     if (d->widget.data() &&
01561         d->widget.data()->isVisible()) {
01562         d->widget.data()->setPos(d->minXExtent(),
01563                                  d->minYExtent());
01564     }
01565 }
01566 
01567 Qt::Alignment Plasma::ScrollWidget::alignment() const
01568 {
01569     return d->alignment;
01570 }
01571 
01572 void ScrollWidget::setOverShoot(bool enable)
01573 {
01574     d->hasOvershoot = enable;
01575 }
01576 
01577 bool ScrollWidget::hasOverShoot() const
01578 {
01579     return d->hasOvershoot;
01580 }
01581 
01582 } // namespace Plasma
01583 
01584 
01585 #include <scrollwidget.moc>
01586 

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