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

Plasma

  • sources
  • kde-4.12
  • kdelibs
  • plasma
  • widgets
scrollwidget.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2009 Marco Martin <notmart@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Library General Public License as
6  * published by the Free Software Foundation; either version 2, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 
20 #include "scrollwidget.h"
21 
22 #include <cmath>
23 
24 //Qt
25 #include <QGraphicsSceneResizeEvent>
26 #include <QGraphicsGridLayout>
27 #include <QGraphicsScene>
28 #include <QApplication>
29 #include <QKeyEvent>
30 #include <QWidget>
31 #include <QTimer>
32 #include <QTime>
33 #include <QPropertyAnimation>
34 #include <QSequentialAnimationGroup>
35 
36 #include <QLabel>
37 
38 //KDE
39 #include <kmimetype.h>
40 #include <kdebug.h>
41 #include <kglobalsettings.h>
42 #include <kiconloader.h>
43 #include <ktextedit.h>
44 #include <ktextbrowser.h>
45 
46 //Plasma
47 #include <plasma/widgets/scrollbar.h>
48 #include <plasma/widgets/svgwidget.h>
49 #include <plasma/widgets/label.h>
50 #include <plasma/widgets/textedit.h>
51 #include <plasma/widgets/textbrowser.h>
52 #include <plasma/animator.h>
53 #include <plasma/svg.h>
54 
55 
56 #define DEBUG 0
57 
58 /*
59  The flicking code is largely based on the behavior of
60  the flickable widget in QDeclerative so porting between
61  the two should preserve the behavior.
62  The code that figures out velocity could use some
63  improvements, in particular IGNORE_SUSPICIOUS_MOVES
64  is a hack that shouldn't be necessary.
65  */
66 
67 //XXX fixme
68 // we use a timer between move events to figure out
69 // the velocity of a move, but sometimes we're getting move
70 // events with big positional changes with no break
71 // in between them, which causes us to compute
72 // huge velocities. this define just filters out
73 // events which come at insanly small time intervals.
74 // at some point we need to figure out how to do it properly
75 #define IGNORE_SUSPICIOUS_MOVES 1
76 
77 // FlickThreshold determines how far the "mouse" must have moved
78 // before we perform a flick.
79 static const int FlickThreshold = 20;
80 
81 
82 static const qreal MinimumFlickVelocity = 200;
83 static const qreal MaxVelocity = 2000;
84 
85 // time it takes the widget to flick back to its
86 // bounds when overshot
87 static const qreal FixupDuration = 600;
88 
89 namespace Plasma
90 {
91 
92 class ScrollWidgetPrivate
93 {
94 public:
95  enum Gesture {
96  GestureNone = 0,
97  GestureUndefined,
98  GestureScroll,
99  GestureZoom
100  };
101 
102  ScrollWidgetPrivate(ScrollWidget *parent)
103  : q(parent),
104  topBorder(0),
105  bottomBorder(0),
106  leftBorder(0),
107  rightBorder(0),
108  dragging(false),
109  overflowBordersVisible(true),
110  multitouchGesture(GestureNone)
111  {
112  }
113 
114  ~ScrollWidgetPrivate()
115  {
116  }
117 
118  void commonConstructor()
119  {
120  q->setFocusPolicy(Qt::StrongFocus);
121  q->setFiltersChildEvents(true);
122  layout = new QGraphicsGridLayout(q);
123  q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
124  layout->setContentsMargins(0, 0, 0, 0);
125  scrollingWidget = new QGraphicsWidget(q);
126  scrollingWidget->setFlag(QGraphicsItem::ItemHasNoContents);
127  scrollingWidget->installEventFilter(q);
128  layout->addItem(scrollingWidget, 0, 0);
129  borderSvg = new Plasma::Svg(q);
130  borderSvg->setImagePath("widgets/scrollwidget");
131 
132  adjustScrollbarsTimer = new QTimer(q);
133  adjustScrollbarsTimer->setSingleShot(true);
134  QObject::connect(adjustScrollbarsTimer, SIGNAL(timeout()), q, SLOT(adjustScrollbars()));
135 
136  wheelTimer = new QTimer(q);
137  wheelTimer->setSingleShot(true);
138 
139  verticalScrollBarPolicy = Qt::ScrollBarAsNeeded;
140  verticalScrollBar = new Plasma::ScrollBar(q);
141  verticalScrollBar->setFocusPolicy(Qt::NoFocus);
142  layout->addItem(verticalScrollBar, 0, 1);
143  verticalScrollBar->nativeWidget()->setMinimum(0);
144  verticalScrollBar->nativeWidget()->setMaximum(100);
145  QObject::connect(verticalScrollBar, SIGNAL(valueChanged(int)), q, SLOT(verticalScroll(int)));
146 
147  horizontalScrollBarPolicy = Qt::ScrollBarAsNeeded;
148  horizontalScrollBar = new Plasma::ScrollBar(q);
149  verticalScrollBar->setFocusPolicy(Qt::NoFocus);
150  horizontalScrollBar->setOrientation(Qt::Horizontal);
151  layout->addItem(horizontalScrollBar, 1, 0);
152  horizontalScrollBar->nativeWidget()->setMinimum(0);
153  horizontalScrollBar->nativeWidget()->setMaximum(100);
154  QObject::connect(horizontalScrollBar, SIGNAL(valueChanged(int)), q, SLOT(horizontalScroll(int)));
155 
156  layout->setColumnSpacing(0, 0);
157  layout->setColumnSpacing(1, 0);
158  layout->setRowSpacing(0, 0);
159  layout->setRowSpacing(1, 0);
160 
161  flickAnimationX = 0;
162  flickAnimationY = 0;
163  fixupAnimation.groupX = 0;
164  fixupAnimation.startX = 0;
165  fixupAnimation.endX = 0;
166  fixupAnimation.groupY = 0;
167  fixupAnimation.startY = 0;
168  fixupAnimation.endY = 0;
169  fixupAnimation.snapX = 0;
170  fixupAnimation.snapY = 0;
171  directMoveAnimation = 0;
172  stealEvent = false;
173  hasOvershoot = true;
174 
175  alignment = Qt::AlignLeft | Qt::AlignTop;
176 
177  hasContentsProperty = false;
178  hasOffsetProperty = false;
179  hasXProperty = false;
180  hasYProperty = false;
181  }
182 
183  void adjustScrollbars()
184  {
185  if (!widget) {
186  return;
187  }
188 
189  const bool verticalVisible = widget.data()->size().height() > q->size().height();
190  const bool horizontalVisible = widget.data()->size().width() > q->size().width();
191 
192  verticalScrollBar->nativeWidget()->setMaximum(qMax(0, int((widget.data()->size().height() - scrollingWidget->size().height())/10)));
193  verticalScrollBar->nativeWidget()->setPageStep(int(scrollingWidget->size().height())/10);
194 
195  if (verticalScrollBarPolicy == Qt::ScrollBarAlwaysOff ||
196  !verticalVisible) {
197  if (layout->count() > 2 && layout->itemAt(2) == verticalScrollBar) {
198  layout->removeAt(2);
199  } else if (layout->count() > 1 && layout->itemAt(1) == verticalScrollBar) {
200  layout->removeAt(1);
201  }
202  verticalScrollBar->hide();
203  } else if (!verticalScrollBar->isVisible()) {
204  layout->addItem(verticalScrollBar, 0, 1);
205  verticalScrollBar->show();
206  }
207 
208  horizontalScrollBar->nativeWidget()->setMaximum(qMax(0, int((widget.data()->size().width() - scrollingWidget->size().width())/10)));
209  horizontalScrollBar->nativeWidget()->setPageStep(int(scrollingWidget->size().width())/10);
210 
211  if (horizontalScrollBarPolicy == Qt::ScrollBarAlwaysOff ||
212  !horizontalVisible) {
213  if (layout->count() > 2 && layout->itemAt(2) == horizontalScrollBar) {
214  layout->removeAt(2);
215  } else if (layout->count() > 1 && layout->itemAt(1) == horizontalScrollBar) {
216  layout->removeAt(1);
217  }
218  horizontalScrollBar->hide();
219  } else if (!horizontalScrollBar->isVisible()) {
220  layout->addItem(horizontalScrollBar, 1, 0);
221  horizontalScrollBar->show();
222  }
223 
224  if (widget && !topBorder && verticalVisible) {
225  topBorder = new Plasma::SvgWidget(q);
226  topBorder->setSvg(borderSvg);
227  topBorder->setElementID("border-top");
228  topBorder->setZValue(900);
229  topBorder->resize(topBorder->effectiveSizeHint(Qt::PreferredSize));
230  topBorder->setVisible(overflowBordersVisible);
231 
232  bottomBorder = new Plasma::SvgWidget(q);
233  bottomBorder->setSvg(borderSvg);
234  bottomBorder->setElementID("border-bottom");
235  bottomBorder->setZValue(900);
236  bottomBorder->resize(bottomBorder->effectiveSizeHint(Qt::PreferredSize));
237  bottomBorder->setVisible(overflowBordersVisible);
238  } else if (topBorder && widget && !verticalVisible) {
239  //FIXME: in some cases topBorder->deleteLater() is deleteNever(), why?
240  topBorder->hide();
241  bottomBorder->hide();
242  topBorder->deleteLater();
243  bottomBorder->deleteLater();
244  topBorder = 0;
245  bottomBorder = 0;
246  }
247 
248 
249  if (widget && !leftBorder && horizontalVisible) {
250  leftBorder = new Plasma::SvgWidget(q);
251  leftBorder->setSvg(borderSvg);
252  leftBorder->setElementID("border-left");
253  leftBorder->setZValue(900);
254  leftBorder->resize(leftBorder->effectiveSizeHint(Qt::PreferredSize));
255  leftBorder->setVisible(overflowBordersVisible);
256 
257  rightBorder = new Plasma::SvgWidget(q);
258  rightBorder->setSvg(borderSvg);
259  rightBorder->setElementID("border-right");
260  rightBorder->setZValue(900);
261  rightBorder->resize(rightBorder->effectiveSizeHint(Qt::PreferredSize));
262  rightBorder->setVisible(overflowBordersVisible);
263  } else if (leftBorder && widget && !horizontalVisible) {
264  leftBorder->hide();
265  rightBorder->hide();
266  leftBorder->deleteLater();
267  rightBorder->deleteLater();
268  leftBorder = 0;
269  rightBorder = 0;
270  }
271 
272  layout->activate();
273 
274  if (topBorder) {
275  topBorder->resize(q->size().width(), topBorder->size().height());
276  bottomBorder->resize(q->size().width(), bottomBorder->size().height());
277  bottomBorder->setPos(0, q->size().height() - topBorder->size().height());
278  }
279  if (leftBorder) {
280  leftBorder->resize(leftBorder->size().width(), q->size().height());
281  rightBorder->resize(rightBorder->size().width(), q->size().height());
282  rightBorder->setPos(q->size().width() - rightBorder->size().width(), 0);
283  }
284 
285  QSizeF widgetSize = widget.data()->size();
286  if (widget.data()->sizePolicy().expandingDirections() & Qt::Horizontal) {
287  //keep a 1 pixel border
288  widgetSize.setWidth(scrollingWidget->size().width());
289  }
290  if (widget.data()->sizePolicy().expandingDirections() & Qt::Vertical) {
291  widgetSize.setHeight(scrollingWidget->size().height());
292  }
293  widget.data()->resize(widgetSize);
294 
295  adjustClipping();
296  }
297 
298  void verticalScroll(int value)
299  {
300  if (!widget) {
301  return;
302  }
303 
304  if (!dragging) {
305  widget.data()->setPos(QPoint(widget.data()->pos().x(), -value*10));
306  }
307  }
308 
309  void horizontalScroll(int value)
310  {
311  if (!widget) {
312  return;
313  }
314 
315  if (!dragging) {
316  widget.data()->setPos(QPoint(-value*10, widget.data()->pos().y()));
317  }
318  }
319 
320  void adjustClipping()
321  {
322  if (!widget) {
323  return;
324  }
325 
326  const bool clip = widget.data()->size().width() > scrollingWidget->size().width() || widget.data()->size().height() > scrollingWidget->size().height();
327 
328  scrollingWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape, clip);
329  }
330 
331  qreal overShootDistance(qreal velocity, qreal size) const
332  {
333  if (MaxVelocity <= 0)
334  return 0.0;
335 
336  velocity = qAbs(velocity);
337  if (velocity > MaxVelocity)
338  velocity = MaxVelocity;
339  qreal dist = size / 4 * velocity / MaxVelocity;
340  return dist;
341  }
342 
343  void animateMoveTo(const QPointF &pos)
344  {
345  qreal duration = 800;
346  QPointF start = q->scrollPosition();
347  QSizeF threshold = q->viewportGeometry().size();
348  QPointF diff = pos - start;
349 
350  //reduce if it's within the viewport
351  if (qAbs(diff.x()) < threshold.width() ||
352  qAbs(diff.y()) < threshold.height())
353  duration /= 2;
354 
355  fixupAnimation.groupX->stop();
356  fixupAnimation.groupY->stop();
357  fixupAnimation.snapX->stop();
358  fixupAnimation.snapY->stop();
359 
360  directMoveAnimation->setStartValue(start);
361  directMoveAnimation->setEndValue(pos);
362  directMoveAnimation->setDuration(duration);
363  directMoveAnimation->start();
364  }
365 
366  void flick(QPropertyAnimation *anim,
367  qreal velocity,
368  qreal val,
369  qreal minExtent,
370  qreal maxExtent,
371  qreal size)
372  {
373  qreal deceleration = 500;
374  qreal maxDistance = -1;
375  qreal target = 0;
376  // -ve velocity means list is moving up
377  if (velocity > 0) {
378  if (val < minExtent)
379  maxDistance = qAbs(minExtent - val + (hasOvershoot?overShootDistance(velocity,size):0));
380  target = minExtent;
381  deceleration = -deceleration;
382  } else {
383  if (val > maxExtent)
384  maxDistance = qAbs(maxExtent - val) + (hasOvershoot?overShootDistance(velocity,size):0);
385  target = maxExtent;
386  }
387  if (maxDistance > 0) {
388  qreal v = velocity;
389  if (MaxVelocity != -1 && MaxVelocity < qAbs(v)) {
390  if (v < 0)
391  v = -MaxVelocity;
392  else
393  v = MaxVelocity;
394  }
395  qreal duration = qAbs(v / deceleration);
396  qreal diffY = v * duration + (0.5 * deceleration * duration * duration);
397  qreal startY = val;
398 
399  qreal endY = startY + diffY;
400 
401  if (velocity > 0) {
402  if (endY > target)
403  endY = startY + maxDistance;
404  } else {
405  if (endY < target)
406  endY = startY - maxDistance;
407  }
408  duration = qAbs((endY-startY)/ (-v/2));
409 
410  if (hasYProperty) {
411  startY = -startY;
412  endY = -endY;
413  }
414 
415 
416 #if DEBUG
417  qDebug()<<"XXX velocity = "<<v <<", target = "<< target
418  <<", maxDist = "<<maxDistance;
419  qDebug()<<"duration = "<<duration<<" secs, ("
420  << (duration * 1000) <<" msecs)";
421  qDebug()<<"startY = "<<startY;
422  qDebug()<<"endY = "<<endY;
423  qDebug()<<"overshoot = "<<overShootDistance(v, size);
424  qDebug()<<"avg velocity = "<< ((endY-startY)/duration);
425 #endif
426 
427  anim->setStartValue(startY);
428  anim->setEndValue(endY);
429  anim->setDuration(duration * 1000);
430  anim->start();
431  } else {
432  if (anim == flickAnimationX)
433  fixupX();
434  else
435  fixupY();
436  }
437  }
438  void flickX(qreal velocity)
439  {
440  flick(flickAnimationX, velocity, widgetX(), minXExtent(), maxXExtent(),
441  q->viewportGeometry().width());
442  }
443  void flickY(qreal velocity)
444  {
445  flick(flickAnimationY, velocity, widgetY(),minYExtent(), maxYExtent(),
446  q->viewportGeometry().height());
447  }
448  void fixup(QAnimationGroup *group,
449  QPropertyAnimation *start, QPropertyAnimation *end,
450  qreal val, qreal minExtent, qreal maxExtent)
451  {
452  if (val > minExtent || maxExtent > minExtent) {
453  if (!qFuzzyCompare(val, minExtent)) {
454  if (FixupDuration) {
455  //TODO: we should consider the case where there is one axis available not the other
456  if (hasXProperty && hasYProperty) {
457  val = -val;
458  minExtent = -minExtent;
459  }
460  qreal dist = minExtent - val;
461  start->setStartValue(val);
462  start->setEndValue(minExtent - dist/2);
463  end->setStartValue(minExtent - dist/2);
464  end->setEndValue(minExtent);
465  start->setDuration(FixupDuration/4);
466  end->setDuration(3*FixupDuration/4);
467  group->start();
468  } else {
469  QObject *obj = start->targetObject();
470  obj->setProperty(start->propertyName(), minExtent);
471  }
472  }
473  } else if (val < maxExtent) {
474  if (FixupDuration) {
475  if (hasXProperty && hasYProperty) {
476  val = -val;
477  maxExtent = -maxExtent;
478  }
479  qreal dist = maxExtent - val;
480  start->setStartValue(val);
481  start->setEndValue(maxExtent - dist/2);
482  end->setStartValue(maxExtent - dist/2);
483  end->setEndValue(maxExtent);
484  start->setDuration(FixupDuration/4);
485  end->setDuration(3*FixupDuration/4);
486  group->start();
487  } else {
488  QObject *obj = start->targetObject();
489  obj->setProperty(start->propertyName(), maxExtent);
490  }
491  } else if (end == fixupAnimation.endX && snapSize.width() > 1 &&
492  q->contentsSize().width() > q->viewportGeometry().width()) {
493  int target = snapSize.width() * round(val/snapSize.width());
494  fixupAnimation.snapX->setStartValue(val);
495  fixupAnimation.snapX->setEndValue(target);
496  fixupAnimation.snapX->setDuration(FixupDuration);
497  fixupAnimation.snapX->start();
498  } else if (end == fixupAnimation.endY && snapSize.height() > 1 &&
499  q->contentsSize().height() > q->viewportGeometry().height()) {
500  int target = snapSize.height() * round(val/snapSize.height());
501  fixupAnimation.snapY->setStartValue(val);
502  fixupAnimation.snapY->setEndValue(target);
503  fixupAnimation.snapY->setDuration(FixupDuration);
504  fixupAnimation.snapY->start();
505  }
506  }
507  void fixupX()
508  {
509  fixup(fixupAnimation.groupX, fixupAnimation.startX, fixupAnimation.endX,
510  widgetX(), minXExtent(), maxXExtent());
511  }
512  void fixupY()
513  {
514  fixup(fixupAnimation.groupY, fixupAnimation.startY, fixupAnimation.endY,
515  widgetY(), minYExtent(), maxYExtent());
516  }
517 
518  void makeRectVisible()
519  {
520  if (!widget) {
521  return;
522  }
523 
524  QRectF viewRect = scrollingWidget->boundingRect();
525  //ensure the rect is not outside the widget bounding rect
526  QRectF mappedRect = QRectF(QPointF(qBound((qreal)0.0, rectToBeVisible.x(), widget.data()->size().width() - rectToBeVisible.width()),
527  qBound((qreal)0.0, rectToBeVisible.y(), widget.data()->size().height() - rectToBeVisible.height())),
528  rectToBeVisible.size());
529  mappedRect = widget.data()->mapToItem(scrollingWidget, mappedRect).boundingRect();
530 
531  if (viewRect.contains(mappedRect)) {
532  return;
533  }
534 
535  QPointF delta(0, 0);
536 
537  if (mappedRect.top() < 0) {
538  delta.setY(-mappedRect.top());
539  } else if (mappedRect.bottom() > viewRect.bottom()) {
540  delta.setY(viewRect.bottom() - mappedRect.bottom());
541  }
542 
543  if (mappedRect.left() < 0) {
544  delta.setX(-mappedRect.left());
545  } else if (mappedRect.right() > viewRect.right()) {
546  delta.setX(viewRect.right() - mappedRect.right());
547  }
548 
549  animateMoveTo(q->scrollPosition() - delta);
550  }
551 
552  void makeItemVisible(QGraphicsItem *itemToBeVisible)
553  {
554  if (!widget) {
555  return;
556  }
557 
558  QRectF rect(widget.data()->mapFromScene(itemToBeVisible->scenePos()), itemToBeVisible->boundingRect().size());
559  rectToBeVisible = rect;
560 
561  makeRectVisible();
562  }
563 
564  void makeItemVisible()
565  {
566  if (widgetToBeVisible) {
567  makeItemVisible(widgetToBeVisible.data());
568  }
569  }
570 
571  void stopAnimations()
572  {
573  flickAnimationX->stop();
574  flickAnimationY->stop();
575  fixupAnimation.groupX->stop();
576  fixupAnimation.groupY->stop();
577  }
578 
579  void setWidgetX(qreal x)
580  {
581  if (hasXProperty) {
582  widget.data()->setProperty("scrollPositionX", -x);
583  } else
584  widget.data()->setX(x);
585  }
586  void setWidgetY(qreal y)
587  {
588  if (hasYProperty) {
589  widget.data()->setProperty("scrollPositionY", -y);
590  } else
591  widget.data()->setY(y);
592  }
593  qreal widgetX() const
594  {
595  if (hasXProperty) {
596  return -widget.data()->property("scrollPositionX").toReal();
597  } else
598  return widget.data()->x();
599  }
600  qreal widgetY() const
601  {
602  if (hasYProperty) {
603  return -widget.data()->property("scrollPositionY").toReal();
604  } else
605  return widget.data()->y();
606  }
607 
608  void handleKeyPressEvent(QKeyEvent *event)
609  {
610  if (!widget.data()) {
611  event->ignore();
612  return;
613  }
614 
615  QPointF start = q->scrollPosition();
616  QPointF end = start;
617 
618  qreal step = 100;
619 
620  switch (event->key()) {
621  case Qt::Key_Left:
622  if (canXFlick()) {
623  end += QPointF(-step, 0);
624  }
625  break;
626  case Qt::Key_Right:
627  if (canXFlick()) {
628  end += QPointF(step, 0);
629  }
630  break;
631  case Qt::Key_Up:
632  if (canYFlick()) {
633  end += QPointF(0, -step);
634  }
635  break;
636  case Qt::Key_Down:
637  if (canYFlick()) {
638  end += QPointF(0, step);
639  }
640  break;
641  default:
642  event->ignore();
643  return;
644  }
645 
646  fixupAnimation.groupX->stop();
647  fixupAnimation.groupY->stop();
648  fixupAnimation.snapX->stop();
649  fixupAnimation.snapY->stop();
650  directMoveAnimation->setStartValue(start);
651  directMoveAnimation->setEndValue(end);
652  directMoveAnimation->setDuration(200);
653  directMoveAnimation->start();
654  }
655 
656  void handleMousePressEvent(QGraphicsSceneMouseEvent *event)
657  {
658  lastPos = QPoint();
659  lastPosTime = QTime::currentTime();
660  pressPos = event->scenePos();
661  pressScrollPos = -q->scrollPosition();
662  pressTime = QTime::currentTime();
663  velocity = QPointF();
664  stopAnimations();
665  }
666 
667  void handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
668  {
669  if (lastPosTime.isNull())
670  return;
671  bool rejectY = false;
672  bool rejectX = false;
673  bool moved = false;
674 
675  if (canYFlick()) {
676  int dy = int(event->scenePos().y() - pressPos.y());
677  if (qAbs(dy) > KGlobalSettings::dndEventDelay() || elapsed(pressTime) > 200) {
678  qreal newY = dy + pressScrollPos.y();
679  const qreal minY = minYExtent();
680  const qreal maxY = maxYExtent();
681  if (newY > minY)
682  newY = minY + (newY - minY) / 2;
683  if (newY < maxY && maxY - minY <= 0)
684  newY = maxY + (newY - maxY) / 2;
685  if (!hasOvershoot && (newY > minY || newY < maxY)) {
686  if (newY > minY)
687  newY = minY;
688  else if (newY < maxY)
689  newY = maxY;
690  else
691  rejectY = true;
692  }
693  if (!rejectY && stealEvent) {
694  setWidgetY(qRound(newY));
695  moved = true;
696  }
697  if (qAbs(dy) > KGlobalSettings::dndEventDelay())
698  stealEvent = true;
699  }
700  }
701 
702  if (canXFlick()) {
703  int dx = int(event->scenePos().x() - pressPos.x());
704  if (qAbs(dx) > KGlobalSettings::dndEventDelay() || elapsed(pressTime) > 200) {
705  qreal newX = dx + pressScrollPos.x();
706  const qreal minX = minXExtent();
707  const qreal maxX = maxXExtent();
708  if (newX > minX)
709  newX = minX + (newX - minX) / 2;
710  if (newX < maxX && maxX - minX <= 0)
711  newX = maxX + (newX - maxX) / 2;
712  if (!hasOvershoot && (newX > minX || newX < maxX)) {
713  if (newX > minX)
714  newX = minX;
715  else if (newX < maxX)
716  newX = maxX;
717  else
718  rejectX = true;
719  }
720  if (!rejectX && stealEvent) {
721  setWidgetX(qRound(newX));
722  moved = true;
723  }
724 
725  if (qAbs(dx) > KGlobalSettings::dndEventDelay())
726  stealEvent = true;
727  }
728  }
729 
730  if (!lastPos.isNull()) {
731  qreal msecs = qreal(restart(lastPosTime));
732  qreal elapsed = msecs / 1000.;
733 #if IGNORE_SUSPICIOUS_MOVES
734  if (msecs > 3) {
735 #endif
736  if (elapsed <= 0)
737  elapsed = 1;
738  if (canYFlick()) {
739  qreal diff = event->scenePos().y() - lastPos.y();
740  // average to reduce the effect of spurious moves
741  velocity.setY( velocity.y() + (diff / elapsed) );
742  velocity.setY( velocity.y() / 2 );
743  }
744 
745  if (canXFlick()) {
746  qreal diff = event->scenePos().x() - lastPos.x();
747  // average to reduce the effect of spurious moves
748  velocity.setX( velocity.x() + (diff / elapsed) );
749  velocity.setX( velocity.x() / 2 );
750  }
751 #if IGNORE_SUSPICIOUS_MOVES
752  }
753 #endif
754  }
755 
756  if (rejectX) velocity.setX(0);
757  if (rejectY) velocity.setY(0);
758 
759  lastPos = event->scenePos();
760  }
761 
762  void handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
763  {
764  stealEvent = false;
765  if (lastPosTime.isNull())
766  return;
767 
768  if (elapsed(lastPosTime) > 100) {
769  // if we drag then pause before release we should not cause a flick.
770  velocity = QPointF();
771  }
772 
773  if (qAbs(velocity.y()) > 10 &&
774  qAbs(event->scenePos().y() - pressPos.y()) > FlickThreshold) {
775  qreal vVelocity = velocity.y();
776  // Minimum velocity to avoid annoyingly slow flicks.
777  if (qAbs(vVelocity) < MinimumFlickVelocity)
778  vVelocity = vVelocity < 0 ? -MinimumFlickVelocity : MinimumFlickVelocity;
779  flickY(vVelocity);
780  } else {
781  fixupY();
782  }
783 
784  if (qAbs(velocity.x()) > 10 &&
785  qAbs(event->scenePos().x() - pressPos.x()) > FlickThreshold) {
786  qreal hVelocity = velocity.x();
787  // Minimum velocity to avoid annoyingly slow flicks.
788  if (qAbs(hVelocity) < MinimumFlickVelocity)
789  hVelocity = hVelocity < 0 ? -MinimumFlickVelocity : MinimumFlickVelocity;
790  flickX(hVelocity);
791  } else {
792  fixupX();
793  }
794 
795  lastPosTime = QTime();
796  }
797 
798  void handleWheelEvent(QGraphicsSceneWheelEvent *event)
799  {
800  //only scroll when the animation is done, this avoids to receive too many events and getting mad when they arrive from a touchpad
801  if (!widget.data() || wheelTimer->isActive()) {
802  return;
803  }
804 
805  QPointF start = q->scrollPosition();
806  QPointF end = start;
807 
808  //At some point we should switch to
809  // step = QApplication::wheelScrollLines() *
810  // (event->delta()/120) *
811  // scrollBar->singleStep();
812  // which gives us exactly the number of lines to scroll but the issue
813  // is that at this point we don't have any clue what a "line" is and if
814  // we make it a pixel then scrolling by 3 (default) pixels will be
815  // very painful
816  qreal step = -event->delta()/3;
817 
818  //ifthe widget can scroll in a single axis and the wheel is the other one, scroll the other one
819  Qt::Orientation orientation = event->orientation();
820  if (orientation == Qt::Vertical) {
821  if (!canYFlick() && canXFlick()) {
822  end += QPointF(step, 0);
823  } else if (canYFlick()) {
824  end += QPointF(0, step);
825  } else {
826  return;
827  }
828  } else {
829  if (canYFlick() && !canXFlick()) {
830  end += QPointF(0, step);
831  } else if (canXFlick()) {
832  end += QPointF(step, 0);
833  } else {
834  return;
835  }
836  }
837 
838  fixupAnimation.groupX->stop();
839  fixupAnimation.groupY->stop();
840  fixupAnimation.snapX->stop();
841  fixupAnimation.snapY->stop();
842  directMoveAnimation->setStartValue(start);
843  directMoveAnimation->setEndValue(end);
844  directMoveAnimation->setDuration(200);
845  directMoveAnimation->start();
846  wheelTimer->start(50);
847  }
848 
849  qreal minXExtent() const
850  {
851  if (alignment & Qt::AlignLeft)
852  return 0;
853  else {
854  qreal vWidth = q->viewportGeometry().width();
855  qreal cWidth = q->contentsSize().width();
856  if (cWidth < vWidth) {
857  if (alignment & Qt::AlignRight)
858  return vWidth - cWidth;
859  else if (alignment & Qt::AlignHCenter)
860  return vWidth / 2 - cWidth / 2;
861  }
862  }
863 
864  return 0;
865  }
866 
867  qreal maxXExtent() const
868  {
869  return q->viewportGeometry().width() -
870  q->contentsSize().width();
871  }
872 
873  qreal minYExtent() const
874  {
875  if (alignment & Qt::AlignTop)
876  return 0;
877  else {
878  qreal vHeight = q->viewportGeometry().height();
879  qreal cHeight = q->contentsSize().height();
880  if (cHeight < vHeight) {
881  if (alignment & Qt::AlignBottom)
882  return vHeight - cHeight;
883  else if (alignment & Qt::AlignVCenter)
884  return vHeight / 2 - cHeight / 2;
885  }
886  }
887 
888  return 0;
889  }
890 
891  qreal maxYExtent() const
892  {
893  return q->viewportGeometry().height() -
894  q->contentsSize().height();
895  }
896 
897  bool canXFlick() const
898  {
899  //make the thing feel quite "fixed" don't permit to flick when the contents size is less than the viewport
900  return q->contentsSize().width() > q->viewportGeometry().width();
901  }
902 
903  bool canYFlick() const
904  {
905  return q->contentsSize().height() > q->viewportGeometry().height();
906  }
907 
908  int elapsed(const QTime &t) const
909  {
910  int n = t.msecsTo(QTime::currentTime());
911  if (n < 0) // passed midnight
912  n += 86400 * 1000;
913  return n;
914  }
915 
916  int restart(QTime &t) const
917  {
918  QTime time = QTime::currentTime();
919  int n = t.msecsTo(time);
920  if (n < 0) // passed midnight
921  n += 86400*1000;
922  t = time;
923  return n;
924  }
925 
926  void createFlickAnimations()
927  {
928  if (widget.data()) {
929  QString xProp = QString::fromLatin1("x");
930  QString yProp = QString::fromLatin1("y");
931 
932  if (hasXProperty)
933  xProp = QString::fromLatin1("scrollPositionX");
934  if (hasYProperty)
935  yProp = QString::fromLatin1("scrollPositionY");
936 
937  flickAnimationX = new QPropertyAnimation(widget.data(),
938  xProp.toLatin1(), widget.data());
939  flickAnimationY = new QPropertyAnimation(widget.data(),
940  yProp.toLatin1(), widget.data());
941  QObject::connect(flickAnimationX, SIGNAL(finished()),
942  q, SLOT(fixupX()));
943  QObject::connect(flickAnimationY, SIGNAL(finished()),
944  q, SLOT(fixupY()));
945 
946  QObject::connect(flickAnimationX,
947  SIGNAL(stateChanged(QAbstractAnimation::State,
948  QAbstractAnimation::State)),
949  q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
950  QAbstractAnimation::State)));
951  QObject::connect(flickAnimationY,
952  SIGNAL(stateChanged(QAbstractAnimation::State,
953  QAbstractAnimation::State)),
954  q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
955  QAbstractAnimation::State)));
956 
957  flickAnimationX->setEasingCurve(QEasingCurve::OutCirc);
958  flickAnimationY->setEasingCurve(QEasingCurve::OutCirc);
959 
960 
961  fixupAnimation.groupX = new QSequentialAnimationGroup(widget.data());
962  fixupAnimation.groupY = new QSequentialAnimationGroup(widget.data());
963  fixupAnimation.startX = new QPropertyAnimation(widget.data(),
964  xProp.toLatin1(), widget.data());
965  fixupAnimation.startY = new QPropertyAnimation(widget.data(),
966  yProp.toLatin1(), widget.data());
967  fixupAnimation.endX = new QPropertyAnimation(widget.data(),
968  xProp.toLatin1(), widget.data());
969  fixupAnimation.endY = new QPropertyAnimation(widget.data(),
970  yProp.toLatin1(), widget.data());
971  fixupAnimation.groupX->addAnimation(
972  fixupAnimation.startX);
973  fixupAnimation.groupY->addAnimation(
974  fixupAnimation.startY);
975  fixupAnimation.groupX->addAnimation(
976  fixupAnimation.endX);
977  fixupAnimation.groupY->addAnimation(
978  fixupAnimation.endY);
979 
980  fixupAnimation.startX->setEasingCurve(QEasingCurve::InQuad);
981  fixupAnimation.endX->setEasingCurve(QEasingCurve::OutQuint);
982  fixupAnimation.startY->setEasingCurve(QEasingCurve::InQuad);
983  fixupAnimation.endY->setEasingCurve(QEasingCurve::OutQuint);
984 
985  fixupAnimation.snapX = new QPropertyAnimation(widget.data(),
986  xProp.toLatin1(), widget.data());
987  fixupAnimation.snapY = new QPropertyAnimation(widget.data(),
988  yProp.toLatin1(), widget.data());
989  fixupAnimation.snapX->setEasingCurve(QEasingCurve::InOutQuad);
990  fixupAnimation.snapY->setEasingCurve(QEasingCurve::InOutQuad);
991 
992  QObject::connect(fixupAnimation.groupX,
993  SIGNAL(stateChanged(QAbstractAnimation::State,
994  QAbstractAnimation::State)),
995  q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
996  QAbstractAnimation::State)));
997  QObject::connect(fixupAnimation.groupY,
998  SIGNAL(stateChanged(QAbstractAnimation::State,
999  QAbstractAnimation::State)),
1000  q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
1001  QAbstractAnimation::State)));
1002 
1003  directMoveAnimation = new QPropertyAnimation(q,
1004  "scrollPosition",
1005  q);
1006  QObject::connect(directMoveAnimation, SIGNAL(finished()),
1007  q, SLOT(fixupX()));
1008  QObject::connect(directMoveAnimation, SIGNAL(finished()),
1009  q, SLOT(fixupY()));
1010  QObject::connect(directMoveAnimation,
1011  SIGNAL(stateChanged(QAbstractAnimation::State,
1012  QAbstractAnimation::State)),
1013  q, SIGNAL(scrollStateChanged(QAbstractAnimation::State,
1014  QAbstractAnimation::State)));
1015  directMoveAnimation->setEasingCurve(QEasingCurve::OutCirc);
1016  }
1017  }
1018 
1019  void deleteFlickAnimations()
1020  {
1021  if (flickAnimationX)
1022  flickAnimationX->stop();
1023  if (flickAnimationY)
1024  flickAnimationY->stop();
1025  delete flickAnimationX;
1026  delete flickAnimationY;
1027  delete fixupAnimation.groupX;
1028  delete fixupAnimation.groupY;
1029  delete directMoveAnimation;
1030  delete fixupAnimation.snapX;
1031  delete fixupAnimation.snapY;
1032  }
1033 
1034  void setScrollX()
1035  {
1036  if (horizontalScrollBarPolicy != Qt::ScrollBarAlwaysOff) {
1037  horizontalScrollBar->blockSignals(true);
1038  horizontalScrollBar->setValue(-widget.data()->pos().x()/10.);
1039  horizontalScrollBar->blockSignals(false);
1040  }
1041  }
1042 
1043  void setScrollY()
1044  {
1045  if (verticalScrollBarPolicy != Qt::ScrollBarAlwaysOff) {
1046  verticalScrollBar->blockSignals(true);
1047  verticalScrollBar->setValue(-widget.data()->pos().y()/10.);
1048  verticalScrollBar->blockSignals(false);
1049  }
1050  }
1051 
1052  ScrollWidget *q;
1053  QGraphicsWidget *scrollingWidget;
1054  QWeakPointer<QGraphicsWidget> widget;
1055  Plasma::Svg *borderSvg;
1056  Plasma::SvgWidget *topBorder;
1057  Plasma::SvgWidget *bottomBorder;
1058  Plasma::SvgWidget *leftBorder;
1059  Plasma::SvgWidget *rightBorder;
1060  QGraphicsGridLayout *layout;
1061  ScrollBar *verticalScrollBar;
1062  Qt::ScrollBarPolicy verticalScrollBarPolicy;
1063  ScrollBar *horizontalScrollBar;
1064  Qt::ScrollBarPolicy horizontalScrollBarPolicy;
1065  QString styleSheet;
1066  QWeakPointer<QGraphicsWidget> widgetToBeVisible;
1067  QRectF rectToBeVisible;
1068  QPointF dragHandleClicked;
1069  bool dragging;
1070  QTimer *adjustScrollbarsTimer;
1071  QTimer *wheelTimer;
1072 
1073  QPointF pressPos;
1074  QPointF pressScrollPos;
1075  QPointF velocity;
1076  QPointF lastPos;
1077  QTime pressTime;
1078  QTime lastPosTime;
1079  QPropertyAnimation *flickAnimationX;
1080  QPropertyAnimation *flickAnimationY;
1081  struct {
1082  QAnimationGroup *groupX;
1083  QPropertyAnimation *startX;
1084  QPropertyAnimation *endX;
1085 
1086  QAnimationGroup *groupY;
1087  QPropertyAnimation *startY;
1088  QPropertyAnimation *endY;
1089 
1090  QPropertyAnimation *snapX;
1091  QPropertyAnimation *snapY;
1092  } fixupAnimation;
1093  QPropertyAnimation *directMoveAnimation;
1094  QSizeF snapSize;
1095  bool stealEvent;
1096  bool hasOvershoot;
1097  bool overflowBordersVisible;
1098 
1099  Qt::Alignment alignment;
1100 
1101  Gesture multitouchGesture;
1102 
1103  bool hasContentsProperty;
1104  bool hasOffsetProperty;
1105  bool hasXProperty;
1106  bool hasYProperty;
1107 };
1108 
1109 
1110 ScrollWidget::ScrollWidget(QGraphicsItem *parent)
1111  : QGraphicsWidget(parent),
1112  d(new ScrollWidgetPrivate(this))
1113 {
1114  d->commonConstructor();
1115 }
1116 
1117 ScrollWidget::ScrollWidget(QGraphicsWidget *parent)
1118  : QGraphicsWidget(parent),
1119  d(new ScrollWidgetPrivate(this))
1120 {
1121  d->commonConstructor();
1122 }
1123 
1124 ScrollWidget::~ScrollWidget()
1125 {
1126  delete d;
1127 }
1128 
1129 void ScrollWidget::setWidget(QGraphicsWidget *widget)
1130 {
1131  if (d->widget && d->widget.data() != widget) {
1132  d->deleteFlickAnimations();
1133  d->widget.data()->removeEventFilter(this);
1134  delete d->widget.data();
1135  }
1136 
1137  d->widget = widget;
1138  //it's not good it's setting a size policy here, but it's done to be retrocompatible with older applications
1139  if (widget) {
1140  d->hasContentsProperty = widget->property("contentsSize").isValid();
1141  d->hasOffsetProperty = widget->property("scrollPosition").isValid();
1142  d->hasXProperty = widget->property("scrollPositionX").isValid();
1143  d->hasYProperty = widget->property("scrollPositionY").isValid();
1144  d->createFlickAnimations();
1145 
1146  connect(widget, SIGNAL(xChanged()), this, SLOT(setScrollX()));
1147  connect(widget, SIGNAL(yChanged()), this, SLOT(setScrollY()));
1148  widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
1149  widget->setParentItem(d->scrollingWidget);
1150  widget->setPos(d->minXExtent(), d->minYExtent());
1151  widget->installEventFilter(this);
1152  d->adjustScrollbarsTimer->start(200);
1153  }
1154 }
1155 
1156 QGraphicsWidget *ScrollWidget::widget() const
1157 {
1158  return d->widget.data();
1159 }
1160 
1161 
1162 void ScrollWidget::setHorizontalScrollBarPolicy(const Qt::ScrollBarPolicy policy)
1163 {
1164  d->horizontalScrollBarPolicy = policy;
1165 }
1166 
1167 
1168 Qt::ScrollBarPolicy ScrollWidget::horizontalScrollBarPolicy() const
1169 {
1170  return d->horizontalScrollBarPolicy;
1171 }
1172 
1173 
1174 void ScrollWidget::setVerticalScrollBarPolicy(const Qt::ScrollBarPolicy policy)
1175 {
1176  d->verticalScrollBarPolicy = policy;
1177 }
1178 
1179 Qt::ScrollBarPolicy ScrollWidget::verticalScrollBarPolicy() const
1180 {
1181  return d->verticalScrollBarPolicy;
1182 }
1183 
1184 bool ScrollWidget::overflowBordersVisible() const
1185 {
1186  return d->overflowBordersVisible;
1187 }
1188 
1189 void ScrollWidget::setOverflowBordersVisible(const bool visible)
1190 {
1191  if (d->overflowBordersVisible == visible) {
1192  return;
1193  }
1194 
1195  d->overflowBordersVisible = visible;
1196  d->adjustScrollbars();
1197 }
1198 
1199 void ScrollWidget::ensureRectVisible(const QRectF &rect)
1200 {
1201  if (!d->widget) {
1202  return;
1203  }
1204 
1205  d->rectToBeVisible = rect;
1206  d->makeRectVisible();
1207 }
1208 
1209 void ScrollWidget::ensureItemVisible(QGraphicsItem *item)
1210 {
1211  if (!d->widget || !item) {
1212  return;
1213  }
1214 
1215  QGraphicsItem *parentOfItem = item->parentItem();
1216  while (parentOfItem != d->widget.data()) {
1217  if (!parentOfItem) {
1218  return;
1219  }
1220 
1221  parentOfItem = parentOfItem->parentItem();
1222  }
1223 
1224  //since we can't ensure it'll stay alive we can delay only if it's a qgraphicswidget
1225  QGraphicsWidget *widget = qgraphicsitem_cast<QGraphicsWidget *>(item);
1226  if (widget) {
1227  d->widgetToBeVisible = widget;
1228 
1229  // We need to wait for the parent item to resize...
1230  QTimer::singleShot(0, this, SLOT(makeItemVisible()));
1231  } else {
1232  d->makeItemVisible(item);
1233  }
1234 }
1235 
1236 #ifndef KDE_NO_DEPRECATED
1237 void ScrollWidget::registerAsDragHandle(QGraphicsWidget *item)
1238 {
1239  Q_UNUSED(item);
1240  return;
1241 }
1242 #endif
1243 
1244 #ifndef KDE_NO_DEPRECATED
1245 void ScrollWidget::unregisterAsDragHandle(QGraphicsWidget *item)
1246 {
1247  Q_UNUSED(item);
1248  return;
1249 }
1250 #endif
1251 
1252 QRectF ScrollWidget::viewportGeometry() const
1253 {
1254  QRectF result;
1255  if (!d->widget) {
1256  return result;
1257  }
1258 
1259  return d->scrollingWidget->boundingRect();
1260 }
1261 
1262 QSizeF ScrollWidget::contentsSize() const
1263 {
1264  if (d->widget) {
1265  if (d->hasContentsProperty) {
1266  QVariant var = d->widget.data()->property("contentsSize");
1267  return var.toSizeF();
1268  } else
1269  return d->widget.data()->size();
1270  }
1271  return QSizeF();
1272 }
1273 
1274 void ScrollWidget::setScrollPosition(const QPointF &position)
1275 {
1276  if (d->widget) {
1277  if (d->hasOffsetProperty)
1278  d->widget.data()->setProperty("scrollPosition", position);
1279  else
1280  d->widget.data()->setPos(-position.toPoint());
1281  }
1282 }
1283 
1284 QPointF ScrollWidget::scrollPosition() const
1285 {
1286  if (d->widget) {
1287  if (d->hasOffsetProperty) {
1288  QVariant var = d->widget.data()->property("scrollPosition");
1289  return var.toPointF();
1290  } else {
1291  return -d->widget.data()->pos();
1292  }
1293  }
1294  return QPointF();
1295 }
1296 
1297 void ScrollWidget::setSnapSize(const QSizeF &size)
1298 {
1299  d->snapSize = size;
1300 }
1301 
1302 QSizeF ScrollWidget::snapSize() const
1303 {
1304  return d->snapSize;
1305 }
1306 
1307 void ScrollWidget::setStyleSheet(const QString &styleSheet)
1308 {
1309  d->styleSheet = styleSheet;
1310  d->verticalScrollBar->setStyleSheet(styleSheet);
1311  d->horizontalScrollBar->setStyleSheet(styleSheet);
1312 }
1313 
1314 QString ScrollWidget::styleSheet() const
1315 {
1316  return d->styleSheet;
1317 }
1318 
1319 QWidget *ScrollWidget::nativeWidget() const
1320 {
1321  return 0;
1322 }
1323 
1324 void ScrollWidget::focusInEvent(QFocusEvent *event)
1325 {
1326  Q_UNUSED(event)
1327 
1328  if (d->widget) {
1329  d->widget.data()->setFocus();
1330  }
1331 }
1332 
1333 
1334 void ScrollWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
1335 {
1336  if (!d->widget) {
1337  QGraphicsWidget::resizeEvent(event);
1338  return;
1339  }
1340 
1341  d->adjustScrollbarsTimer->start(200);
1342 
1343  //if topBorder exists bottomBorder too
1344  if (d->topBorder) {
1345  d->topBorder->resize(event->newSize().width(), d->topBorder->size().height());
1346  d->bottomBorder->resize(event->newSize().width(), d->bottomBorder->size().height());
1347  d->bottomBorder->setPos(0, event->newSize().height() - d->bottomBorder->size().height());
1348  }
1349  if (d->leftBorder) {
1350  d->leftBorder->resize(d->leftBorder->size().width(), event->newSize().height());
1351  d->rightBorder->resize(d->rightBorder->size().width(), event->newSize().height());
1352  d->rightBorder->setPos(event->newSize().width() - d->rightBorder->size().width(), 0);
1353  }
1354 
1355  QGraphicsWidget::resizeEvent(event);
1356 }
1357 
1358 void ScrollWidget::keyPressEvent(QKeyEvent *event)
1359 {
1360  d->handleKeyPressEvent(event);
1361 }
1362 
1363 void ScrollWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1364 {
1365  if (!d->widget) {
1366  return;
1367  }
1368 
1369  d->handleMouseMoveEvent(event);
1370  event->accept();
1371 
1372  return QGraphicsWidget::mouseMoveEvent(event);
1373 }
1374 
1375 void ScrollWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
1376 {
1377  if (!d->widget) {
1378  return;
1379  } else if (!d->canYFlick() && !d->canXFlick()) {
1380  event->ignore();
1381  return;
1382  }
1383 
1384  d->handleMousePressEvent(event);
1385 
1386  if (event->button() == Qt::LeftButton) {
1387  event->accept();
1388  } else {
1389  QGraphicsWidget::mousePressEvent(event);
1390  }
1391 }
1392 
1393 void ScrollWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1394 {
1395  if (!d->widget) {
1396  return;
1397  }
1398 
1399  d->handleMouseReleaseEvent(event);
1400  event->accept();
1401 }
1402 
1403 void ScrollWidget::wheelEvent(QGraphicsSceneWheelEvent *event)
1404 {
1405  if (!d->widget) {
1406  return;
1407  } else if (!d->canYFlick() && !d->canXFlick()) {
1408  event->ignore();
1409  return;
1410  }
1411  d->handleWheelEvent(event);
1412  event->accept();
1413 }
1414 
1415 bool ScrollWidget::eventFilter(QObject *watched, QEvent *event)
1416 {
1417  if (!d->widget) {
1418  return false;
1419  }
1420 
1421  if (watched == d->scrollingWidget && (event->type() == QEvent::GraphicsSceneResize ||
1422  event->type() == QEvent::Move)) {
1423  emit viewportGeometryChanged(viewportGeometry());
1424  } else if (watched == d->widget.data() && event->type() == QEvent::GraphicsSceneResize) {
1425  d->stopAnimations();
1426  d->adjustScrollbarsTimer->start(200);
1427  updateGeometry();
1428 
1429  QPointF newPos = d->widget.data()->pos();
1430  if (d->widget.data()->size().width() <= viewportGeometry().width()) {
1431  newPos.setX(d->minXExtent());
1432  }
1433  if (d->widget.data()->size().height() <= viewportGeometry().height()) {
1434  newPos.setY(d->minYExtent());
1435  }
1436  //check if the content is visible
1437  if (d->widget.data()->geometry().right() < 0) {
1438  newPos.setX(-d->widget.data()->geometry().width()+viewportGeometry().width());
1439  }
1440  if (d->widget.data()->geometry().bottom() < 0) {
1441  newPos.setY(-d->widget.data()->geometry().height()+viewportGeometry().height());
1442  }
1443  d->widget.data()->setPos(newPos);
1444 
1445  } else if (watched == d->widget.data() && event->type() == QEvent::GraphicsSceneMove) {
1446  d->horizontalScrollBar->blockSignals(true);
1447  d->verticalScrollBar->blockSignals(true);
1448  d->horizontalScrollBar->setValue(-d->widget.data()->pos().x()/10);
1449  d->verticalScrollBar->setValue(-d->widget.data()->pos().y()/10);
1450  d->horizontalScrollBar->blockSignals(false);
1451  d->verticalScrollBar->blockSignals(false);
1452  }
1453 
1454  return false;
1455 }
1456 
1457 QSizeF ScrollWidget::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
1458 {
1459  if (!d->widget || which == Qt::MaximumSize) {
1460  return QGraphicsWidget::sizeHint(which, constraint);
1461  //FIXME: it should ake the minimum hint of the contained widget, but the result is in a ridiculously big widget
1462  } else if (which == Qt::MinimumSize) {
1463  return QSizeF(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous);
1464  }
1465 
1466  QSizeF hint = d->widget.data()->effectiveSizeHint(which, constraint);
1467  if (d->horizontalScrollBar && d->horizontalScrollBar->isVisible()) {
1468  hint += QSize(0, d->horizontalScrollBar->size().height());
1469  }
1470  if (d->verticalScrollBar && d->verticalScrollBar->isVisible()) {
1471  hint += QSize(d->verticalScrollBar->size().width(), 0);
1472  }
1473 
1474  return hint;
1475 }
1476 
1477 
1478 bool ScrollWidget::sceneEventFilter(QGraphicsItem *i, QEvent *e)
1479 {
1480  //only the scrolling widget and its children
1481  if (!d->widget.data() ||
1482  (!d->scrollingWidget->isAncestorOf(i) && i != d->scrollingWidget) ||
1483  i == d->horizontalScrollBar || i == d->verticalScrollBar) {
1484  return false;
1485  }
1486 
1487  if (i->isWidget()) {
1488  Plasma::Label *label = dynamic_cast<Plasma::Label *>(static_cast<QGraphicsWidget *>(i));
1489  if (label && (label->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) {
1490  return false;
1491  }
1492 
1493  Plasma::TextEdit *textEdit = dynamic_cast<Plasma::TextEdit *>(static_cast<QGraphicsWidget *>(i));
1494  if (textEdit && (textEdit->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) {
1495  return false;
1496  }
1497 
1498  Plasma::TextBrowser *textBrowser= dynamic_cast<Plasma::TextBrowser *>(static_cast<QGraphicsWidget *>(i));
1499  if (textBrowser && (textBrowser->nativeWidget()->textInteractionFlags() & Qt::TextSelectableByMouse)) {
1500  return false;
1501  }
1502  }
1503 
1504  bool stealThisEvent = d->stealEvent;
1505  //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)
1506  stealThisEvent &= (e->type() == QEvent::GraphicsSceneMousePress ||
1507  e->type() == QEvent::GraphicsSceneMouseRelease);
1508 #if DEBUG
1509  qDebug()<<"sceneEventFilter = " <<i<<", "
1510  <<QTime::currentTime().toString(QString::fromLatin1("hh:mm:ss.zzz"));
1511 #endif
1512  switch (e->type()) {
1513  case QEvent::GraphicsSceneMousePress:
1514  d->handleMousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(e));
1515  break;
1516  case QEvent::GraphicsSceneMouseMove:
1517  d->handleMouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(e));
1518  break;
1519  case QEvent::GraphicsSceneMouseRelease:
1520  d->handleMouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(e));
1521  break;
1522 
1523  //Multitouch related events, we actually need only TouchUpdate
1524  case QEvent::TouchUpdate: {
1525  QList<QTouchEvent::TouchPoint> touchPoints = static_cast<QTouchEvent *>(e)->touchPoints();
1526  if (touchPoints.count() == 2) {
1527  const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
1528  const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
1529  const QLineF line0(touchPoint0.lastPos(), touchPoint1.lastPos());
1530  const QLineF line1(touchPoint0.pos(), touchPoint1.pos());
1531  const QLineF startLine(touchPoint0.startPos(), touchPoint1.startPos());
1532  const QPointF point = line1.pointAt(0.5);
1533  const QPointF lastPoint = line0.pointAt(0.5);
1534 
1535  if (d->multitouchGesture == ScrollWidgetPrivate::GestureNone) {
1536  d->multitouchGesture = ScrollWidgetPrivate::GestureUndefined;
1537  }
1538  if (d->multitouchGesture == ScrollWidgetPrivate::GestureUndefined) {
1539  const int zoomDistance = qAbs(line1.length() - startLine.length());
1540  const int dragDistance = (startLine.pointAt(0.5) - point).manhattanLength();
1541 
1542  if (zoomDistance - dragDistance > 30) {
1543  d->multitouchGesture = ScrollWidgetPrivate::GestureZoom;
1544  } else if (dragDistance - zoomDistance > 30) {
1545  d->multitouchGesture = ScrollWidgetPrivate::GestureScroll;
1546  }
1547  }
1548 
1549  if (d->multitouchGesture == ScrollWidgetPrivate::GestureScroll) {
1550  QGraphicsSceneMouseEvent fakeEvent;
1551  fakeEvent.setPos(point);
1552  fakeEvent.setLastPos(lastPoint);
1553  d->handleMouseMoveEvent(&fakeEvent);
1554  } else if (d->multitouchGesture == ScrollWidgetPrivate::GestureZoom) {
1555  if (d->widget && d->widget.data()->property("zoomFactor").isValid()) {
1556  qreal scaleFactor = 1;
1557  if (line0.length() > 0) {
1558  scaleFactor = line1.length() / line0.length();
1559  }
1560 
1561  qreal zoom = d->widget.data()->property("zoomFactor").toReal();
1562  d->widget.data()->setProperty("zoomFactor", zoom * scaleFactor);
1563  }
1564  }
1565  }
1566  break;
1567  }
1568  default:
1569  break;
1570  }
1571  if (stealThisEvent)
1572  return true;
1573  return QGraphicsWidget::sceneEventFilter(i, e);
1574 }
1575 
1576 void Plasma::ScrollWidget::setAlignment(Qt::Alignment align)
1577 {
1578  d->alignment = align;
1579  if (d->widget.data() &&
1580  d->widget.data()->isVisible()) {
1581  d->widget.data()->setPos(d->minXExtent(),
1582  d->minYExtent());
1583  }
1584 }
1585 
1586 Qt::Alignment Plasma::ScrollWidget::alignment() const
1587 {
1588  return d->alignment;
1589 }
1590 
1591 void ScrollWidget::setOverShoot(bool enable)
1592 {
1593  d->hasOvershoot = enable;
1594 }
1595 
1596 bool ScrollWidget::hasOverShoot() const
1597 {
1598  return d->hasOvershoot;
1599 }
1600 
1601 } // namespace Plasma
1602 
1603 
1604 #include <scrollwidget.moc>
1605 
Plasma::ScrollWidget::setAlignment
void setAlignment(Qt::Alignment align)
Sets the alignment for the inner widget.
Definition: scrollwidget.cpp:1576
Plasma::ScrollWidget::nativeWidget
QWidget * nativeWidget() const
Definition: scrollwidget.cpp:1319
MinimumFlickVelocity
static const qreal MinimumFlickVelocity
Definition: scrollwidget.cpp:82
Plasma::Vertical
The applet is constrained horizontally, but can expand vertically.
Definition: plasma.h:77
Plasma::ScrollWidget::setWidget
void setWidget(QGraphicsWidget *widget)
Sets the widget this ScrollWidget will contain ownership is transferred to this scrollwidget, if an old one was already in, it will be deleted.
Definition: scrollwidget.cpp:1129
Plasma::ScrollWidget::scrollPosition
QPointF scrollPosition() const
Plasma::ScrollWidget::snapSize
QSizeF snapSize() const
Plasma::ScrollWidget::mousePressEvent
void mousePressEvent(QGraphicsSceneMouseEvent *event)
Definition: scrollwidget.cpp:1375
textbrowser.h
Plasma::ScrollWidget::widget
QGraphicsWidget * widget() const
textedit.h
Plasma::TextBrowser::nativeWidget
KTextBrowser nativeWidget
Definition: textbrowser.h:49
Plasma::ScrollWidget::sceneEventFilter
bool sceneEventFilter(QGraphicsItem *i, QEvent *e)
Definition: scrollwidget.cpp:1478
QWidget
Plasma::Label::nativeWidget
QLabel nativeWidget
Definition: label.h:52
Plasma::ScrollWidget::sizeHint
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
Definition: scrollwidget.cpp:1457
Plasma::TextBrowser
Provides a plasma-themed KTextBrowser.
Definition: textbrowser.h:42
Plasma::ScrollWidget::registerAsDragHandle
Q_INVOKABLE void registerAsDragHandle(QGraphicsWidget *item)
Register an item as a drag handle, it means mouse events will pass trough it and will be possible to ...
Definition: scrollwidget.cpp:1237
Plasma::ScrollBar
Provides a plasma-themed QScrollBar.
Definition: scrollbar.h:39
Plasma::ScrollWidget::viewportGeometry
QRectF viewportGeometry() const
The geometry of the viewport.
QObject
Plasma::Horizontal
The applet is constrained vertically, but can expand horizontally.
Definition: plasma.h:75
Plasma::ScrollWidget::setScrollPosition
void setScrollPosition(const QPointF &position)
Sets the position of the internal widget relative to this widget.
Definition: scrollwidget.cpp:1274
Plasma::ScrollWidget::ensureItemVisible
Q_INVOKABLE void ensureItemVisible(QGraphicsItem *item)
Scroll the view until the given item is visible.
Definition: scrollwidget.cpp:1209
Plasma::ScrollWidget::alignment
Qt::Alignment alignment() const
Plasma::ScrollWidget::setOverflowBordersVisible
void setOverflowBordersVisible(const bool visible)
Sets whether borders should be shown when the inner widget is bigger than the viewport.
Definition: scrollwidget.cpp:1189
Plasma::ScrollWidget::focusInEvent
void focusInEvent(QFocusEvent *event)
Definition: scrollwidget.cpp:1324
Plasma::ScrollWidget::setSnapSize
void setSnapSize(const QSizeF &size)
Set the nap size of the kinetic scrolling: the scrolling will always stop at multiples of that size...
Definition: scrollwidget.cpp:1297
MaxVelocity
static const qreal MaxVelocity
Definition: scrollwidget.cpp:83
Plasma::Label
Provides a plasma-themed QLabel.
Definition: label.h:40
Plasma::ScrollWidget::keyPressEvent
void keyPressEvent(QKeyEvent *event)
Definition: scrollwidget.cpp:1358
Plasma::ScrollWidget::unregisterAsDragHandle
Q_INVOKABLE void unregisterAsDragHandle(QGraphicsWidget *item)
Unregister the given item as drag handle (if it was registered)
Definition: scrollwidget.cpp:1245
Plasma::ScrollWidget::ScrollWidget
ScrollWidget(QGraphicsWidget *parent=0)
Constructs a new ScrollWidget.
Definition: scrollwidget.cpp:1117
Plasma::ScrollWidget::overflowBordersVisible
bool overflowBordersVisible() const
Plasma::ScrollWidget::mouseReleaseEvent
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
Definition: scrollwidget.cpp:1393
Plasma::ScrollWidget::wheelEvent
void wheelEvent(QGraphicsSceneWheelEvent *event)
Definition: scrollwidget.cpp:1403
scrollwidget.h
FlickThreshold
static const int FlickThreshold
Definition: scrollwidget.cpp:79
Plasma::ScrollWidget::ensureRectVisible
Q_INVOKABLE void ensureRectVisible(const QRectF &rect)
Scroll the view until the given rectangle is visible.
Definition: scrollwidget.cpp:1199
svgwidget.h
Plasma::ScrollWidget::hasOverShoot
bool hasOverShoot() const
Definition: scrollwidget.cpp:1596
Plasma::ScrollWidget::eventFilter
bool eventFilter(QObject *watched, QEvent *event)
Definition: scrollwidget.cpp:1415
Plasma::ScrollWidget::styleSheet
QString styleSheet() const
Plasma::ScrollWidget::~ScrollWidget
~ScrollWidget()
Definition: scrollwidget.cpp:1124
Plasma::ScrollWidget::viewportGeometryChanged
void viewportGeometryChanged(const QRectF &geomety)
The viewport geomety changed, for instance due a widget resize.
Plasma::ScrollWidget::contentsSize
QSizeF contentsSize() const
Plasma::ScrollWidget::setHorizontalScrollBarPolicy
void setHorizontalScrollBarPolicy(const Qt::ScrollBarPolicy policy)
Sets the horizontal scrollbar policy.
Definition: scrollwidget.cpp:1162
Plasma::TextEdit
Provides a plasma-themed KTextEdit.
Definition: textedit.h:40
Plasma::ScrollWidget::setVerticalScrollBarPolicy
void setVerticalScrollBarPolicy(const Qt::ScrollBarPolicy policy)
Sets the vertical scrollbar policy.
Definition: scrollwidget.cpp:1174
Plasma::ScrollWidget::verticalScrollBarPolicy
Qt::ScrollBarPolicy verticalScrollBarPolicy() const
label.h
scrollbar.h
FixupDuration
static const qreal FixupDuration
Definition: scrollwidget.cpp:87
Plasma::ScrollWidget::setStyleSheet
void setStyleSheet(const QString &stylesheet)
Sets the stylesheet used to control the visual display of this ScrollWidget.
Definition: scrollwidget.cpp:1307
Plasma::TextEdit::nativeWidget
KTextEdit nativeWidget
Definition: textedit.h:47
animator.h
Plasma::ScrollWidget::setOverShoot
void setOverShoot(bool enable)
Tells the scrollwidget whether the widget can scroll a little beyond its boundaries and then automati...
Definition: scrollwidget.cpp:1591
Plasma::Svg
A theme aware image-centric SVG class.
Definition: svg.h:56
svg.h
Plasma::ScrollWidget::resizeEvent
void resizeEvent(QGraphicsSceneResizeEvent *event)
Definition: scrollwidget.cpp:1334
Plasma::SvgWidget
Definition: svgwidget.h:39
Plasma::ScrollWidget::horizontalScrollBarPolicy
Qt::ScrollBarPolicy horizontalScrollBarPolicy() const
QGraphicsWidget
Plasma::ScrollWidget::mouseMoveEvent
void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
Definition: scrollwidget.cpp:1363
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:48:34 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal