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
KDE 4.6 API Reference