1/*
2 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
3 Copyright (c) 2011 Martin Koller <kollix@aon.at>
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#define DEBUG_KP_VIEW_SCROLLABLE_CONTAINER 0
29
30#include <kpViewScrollableContainer.h>
31
32#include <qbitmap.h>
33#include <qcursor.h>
34#include <qevent.h>
35#include <qpainter.h>
36#include <qpen.h>
37#include <qpixmap.h>
38#include <qtimer.h>
39#include <QScrollBar>
40
41#include <kdebug.h>
42#include <klocale.h>
43
44#include <kpDefs.h>
45#include <kpPixmapFX.h>
46#include <kpView.h>
47#include <kpWidgetMapper.h>
48
49//---------------------------------------------------------------------
50
51// (Pulled from out of Thurston's hat)
52static const int DragScrollLeftTopMargin = 0;
53static const int DragScrollRightBottomMargin = 16; // scrollbarish
54static const int DragScrollInterval = 150;
55static const int DragScrollInitialInterval = DragScrollInterval * 2;
56static const int DragScrollNumPixels = 10;
57static const int DragDistanceFromRectMaxFor1stMultiplier = 50;
58static const int DragDistanceFromRectMaxFor2ndMultiplier = 100;
59
60//---------------------------------------------------------------------
61//---------------------------------------------------------------------
62// a transparent widget above all others in the viewport used only while resizing the document
63// to be able to show the resize lines above everything else
64
65class kpOverlay : public QWidget
66{
67 public:
68 kpOverlay(QWidget *parent, kpViewScrollableContainer *container)
69 : QWidget(parent), m_container(container)
70 {
71 }
72
73 virtual void paintEvent(QPaintEvent *)
74 {
75 m_container->drawResizeLines();
76 }
77
78 private:
79 kpViewScrollableContainer *m_container;
80
81};
82
83//---------------------------------------------------------------------
84
85const int kpGrip::Size = 5;
86
87//---------------------------------------------------------------------
88
89kpGrip::kpGrip (GripType type, QWidget *parent)
90 : QWidget(parent),
91 m_type (type),
92 m_startPoint (KP_INVALID_POINT),
93 m_currentPoint (KP_INVALID_POINT),
94 m_shouldReleaseMouseButtons (false)
95{
96 setCursor(cursorForType(m_type));
97
98 setMouseTracking(true); // mouseMoveEvent's even when no mousebtn down
99
100 setAutoFillBackground(true);
101 setBackgroundRole(QPalette::Highlight);
102
103 setFixedSize(kpGrip::Size, kpGrip::Size);
104}
105
106//---------------------------------------------------------------------
107
108// public
109kpGrip::GripType kpGrip::type () const
110{
111 return m_type;
112}
113
114//---------------------------------------------------------------------
115
116// public static
117QCursor kpGrip::cursorForType (GripType type)
118{
119 switch (type)
120 {
121 case kpGrip::Bottom:
122 return Qt::SizeVerCursor;
123 break; // one day you'll forget
124
125 case kpGrip::Right:
126 return Qt::SizeHorCursor;
127 break; // one day you'll forget
128
129 case kpGrip::BottomRight:
130 return Qt::SizeFDiagCursor;
131 break; // one day you'll forget
132 }
133
134 return Qt::ArrowCursor;
135}
136
137//---------------------------------------------------------------------
138
139// public
140bool kpGrip::containsCursor()
141{
142 return isVisible() &&
143 QRect(mapToGlobal(rect().topLeft()),
144 mapToGlobal(rect().bottomRight())).contains(QCursor::pos());
145}
146
147//---------------------------------------------------------------------
148
149// public
150bool kpGrip::isDrawing () const
151{
152 return (m_startPoint != KP_INVALID_POINT);
153}
154
155//---------------------------------------------------------------------
156
157// public
158QString kpGrip::haventBegunDrawUserMessage () const
159{
160 return i18n ("Left drag the handle to resize the image.");
161}
162
163//---------------------------------------------------------------------
164
165// public
166QString kpGrip::userMessage () const
167{
168 return m_userMessage;
169}
170
171//---------------------------------------------------------------------
172
173// public
174void kpGrip::setUserMessage (const QString &message)
175{
176 // Don't do NOP checking here since another grip might have changed
177 // the message so an apparent NOP for this grip is not a NOP in the
178 // global sense (kpViewScrollableContainer::slotGripStatusMessageChanged()).
179
180 m_userMessage = message;
181 emit statusMessageChanged (message);
182}
183
184//---------------------------------------------------------------------
185
186// protected
187void kpGrip::cancel ()
188{
189#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
190 kDebug () << "kpGrip::cancel()";
191#endif
192 if (m_currentPoint == KP_INVALID_POINT)
193 return;
194
195 m_startPoint = KP_INVALID_POINT;
196 m_currentPoint = KP_INVALID_POINT;
197
198 setUserMessage (i18n ("Resize Image: Let go of all the mouse buttons."));
199 setCursor (Qt::ArrowCursor);
200 m_shouldReleaseMouseButtons = true;
201
202 releaseKeyboard ();
203 emit cancelledDraw ();
204}
205
206//---------------------------------------------------------------------
207
208// protected virtual [base QWidget]
209void kpGrip::keyReleaseEvent (QKeyEvent *e)
210{
211 if (m_startPoint != KP_INVALID_POINT &&
212 e->key () == Qt::Key_Escape)
213 {
214 cancel ();
215 }
216}
217
218//---------------------------------------------------------------------
219
220// protected virtual [base QWidget]
221void kpGrip::mousePressEvent (QMouseEvent *e)
222{
223 if (m_startPoint == KP_INVALID_POINT &&
224 (e->buttons () & Qt::MouseButtonMask) == Qt::LeftButton)
225 {
226 m_startPoint = e->pos ();
227 m_currentPoint = e->pos ();
228 emit beganDraw ();
229 grabKeyboard ();
230
231 setUserMessage (i18n ("Resize Image: Right click to cancel."));
232 setCursor (cursorForType (m_type));
233 }
234 else
235 {
236 if (m_startPoint != KP_INVALID_POINT)
237 cancel ();
238 }
239}
240
241//---------------------------------------------------------------------
242
243// public
244QPoint kpGrip::viewDeltaPoint () const
245{
246 if (m_startPoint == KP_INVALID_POINT)
247 return KP_INVALID_POINT;
248
249 const QPoint point = mapFromGlobal (QCursor::pos ());
250
251 // TODO: this is getting out of sync with m_currentPoint
252
253 return QPoint (((m_type & kpGrip::Right) ? point.x () - m_startPoint.x () : 0),
254 ((m_type & kpGrip::Bottom) ? point.y () - m_startPoint.y () : 0));
255
256}
257
258//---------------------------------------------------------------------
259
260// public
261void kpGrip::mouseMovedTo (const QPoint &point, bool dueToDragScroll)
262{
263 if (m_startPoint == KP_INVALID_POINT)
264 return;
265
266 m_currentPoint = point;
267
268 emit continuedDraw (((m_type & kpGrip::Right) ? point.x () - m_startPoint.x () : 0),
269 ((m_type & kpGrip::Bottom) ? point.y () - m_startPoint.y () : 0),
270 dueToDragScroll);
271}
272
273//---------------------------------------------------------------------
274
275// protected virtual [base QWidget]
276void kpGrip::mouseMoveEvent (QMouseEvent *e)
277{
278#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
279 kDebug () << "kpGrip::mouseMoveEvent() m_startPoint=" << m_startPoint
280 << " stateAfter: buttons=" << (int *) (int) e->buttons ()
281 << endl;
282#endif
283
284 if (m_startPoint == KP_INVALID_POINT)
285 {
286 if ((e->buttons () & Qt::MouseButtonMask) == 0)
287 setUserMessage (haventBegunDrawUserMessage ());
288 return;
289 }
290
291 mouseMovedTo (e->pos (), false/*not due to drag scroll*/);
292}
293
294//---------------------------------------------------------------------
295
296// protected virtual [base QWidget]
297void kpGrip::mouseReleaseEvent (QMouseEvent *e)
298{
299#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
300 kDebug () << "kpGrip::mouseReleaseEvent() m_startPoint=" << m_startPoint
301 << " stateAfter: buttons=" << (int *) (int) e->buttons ()
302 << endl;
303#endif
304
305 if (m_startPoint != KP_INVALID_POINT)
306 {
307 const int dx = m_currentPoint.x () - m_startPoint.x (),
308 dy = m_currentPoint.y () - m_startPoint.y ();
309
310 m_currentPoint = KP_INVALID_POINT;
311 m_startPoint = KP_INVALID_POINT;
312
313 releaseKeyboard ();
314 emit endedDraw ((m_type & kpGrip::Right) ? dx : 0,
315 (m_type & kpGrip::Bottom) ? dy : 0);
316 }
317
318 if ((e->buttons () & Qt::MouseButtonMask) == 0)
319 {
320 m_shouldReleaseMouseButtons = false;
321 setUserMessage(QString());
322 setCursor (cursorForType (m_type));
323
324 releaseKeyboard ();
325 emit releasedAllButtons ();
326 }
327}
328
329//---------------------------------------------------------------------
330
331// protected virtual [base QWidget]
332void kpGrip::enterEvent (QEvent * /*e*/)
333{
334#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
335 kDebug () << "kpGrip::enterEvent()"
336 << " m_startPoint=" << m_startPoint
337 << " shouldReleaseMouseButtons="
338 << m_shouldReleaseMouseButtons << endl;
339#endif
340
341 if (m_startPoint == KP_INVALID_POINT &&
342 !m_shouldReleaseMouseButtons)
343 {
344 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
345 kDebug () << "\tsending message";
346 #endif
347 setUserMessage (haventBegunDrawUserMessage ());
348 }
349}
350
351//---------------------------------------------------------------------
352
353// protected virtual [base QWidget]
354void kpGrip::leaveEvent (QEvent * /*e*/)
355{
356#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
357 kDebug () << "kpGrip::leaveEvent()"
358 << " m_startPoint=" << m_startPoint
359 << " shouldReleaseMouseButtons="
360 << m_shouldReleaseMouseButtons << endl;
361#endif
362 if (m_startPoint == KP_INVALID_POINT &&
363 !m_shouldReleaseMouseButtons)
364 {
365 setUserMessage(QString());
366 }
367}
368
369//---------------------------------------------------------------------
370//---------------------------------------------------------------------
371//---------------------------------------------------------------------
372
373// TODO: Are we checking for m_view == 0 often enough? Also an issue in KDE 3.
374kpViewScrollableContainer::kpViewScrollableContainer(QWidget *parent)
375 : QScrollArea(parent),
376 m_view(0), m_overlay(new kpOverlay(viewport(), this)),
377 m_docResizingGrip (0),
378 m_dragScrollTimer (new QTimer (this)),
379 m_zoomLevel (100),
380 m_scrollTimerRunOnce (false),
381 m_resizeRoundedLastViewX (-1), m_resizeRoundedLastViewY (-1),
382 m_resizeRoundedLastViewDX (0), m_resizeRoundedLastViewDY (0),
383 m_haveMovedFromOriginalDocSize (false)
384
385{
386 // the base widget holding the documents view plus the resize grips
387 setWidget(new QWidget(viewport()));
388
389 m_bottomGrip = new kpGrip(kpGrip::Bottom, widget());
390 m_rightGrip = new kpGrip(kpGrip::Right, widget());
391 m_bottomRightGrip = new kpGrip(kpGrip::BottomRight, widget());
392
393 m_bottomGrip->setObjectName(QLatin1String("Bottom Grip"));
394 m_rightGrip->setObjectName(QLatin1String("Right Grip"));
395 m_bottomRightGrip->setObjectName(QLatin1String("BottomRight Grip"));
396
397 m_bottomGrip->hide ();
398 connectGripSignals (m_bottomGrip);
399
400 m_rightGrip->hide ();
401 connectGripSignals (m_rightGrip);
402
403 m_bottomRightGrip->hide ();
404 connectGripSignals (m_bottomRightGrip);
405
406
407 connect (horizontalScrollBar(), SIGNAL(valueChanged(int)),
408 this, SLOT(slotContentsMoved()));
409
410 connect (verticalScrollBar(), SIGNAL(valueChanged(int)),
411 this, SLOT(slotContentsMoved()));
412
413 connect (m_dragScrollTimer, SIGNAL (timeout ()),
414 this, SLOT (slotDragScroll ()));
415
416 m_overlay->hide();
417}
418
419//---------------------------------------------------------------------
420
421// protected
422void kpViewScrollableContainer::connectGripSignals (kpGrip *grip)
423{
424 connect (grip, SIGNAL (beganDraw ()),
425 this, SLOT (slotGripBeganDraw ()));
426 connect (grip, SIGNAL (continuedDraw (int, int, bool)),
427 this, SLOT (slotGripContinuedDraw (int, int, bool)));
428 connect (grip, SIGNAL (cancelledDraw ()),
429 this, SLOT (slotGripCancelledDraw ()));
430 connect (grip, SIGNAL (endedDraw (int, int)),
431 this, SLOT (slotGripEndedDraw (int, int)));
432
433 connect (grip, SIGNAL (statusMessageChanged (const QString &)),
434 this, SLOT (slotGripStatusMessageChanged (const QString &)));
435
436 connect (grip, SIGNAL (releasedAllButtons ()),
437 this, SLOT (recalculateStatusMessage ()));
438}
439
440//---------------------------------------------------------------------
441
442// public
443QSize kpViewScrollableContainer::newDocSize () const
444{
445 return newDocSize (m_resizeRoundedLastViewDX,
446 m_resizeRoundedLastViewDY);
447}
448
449//---------------------------------------------------------------------
450
451// public
452bool kpViewScrollableContainer::haveMovedFromOriginalDocSize () const
453{
454 return m_haveMovedFromOriginalDocSize;
455}
456
457//---------------------------------------------------------------------
458
459// public
460QString kpViewScrollableContainer::statusMessage () const
461{
462 return m_gripStatusMessage;
463}
464
465//---------------------------------------------------------------------
466
467// public
468void kpViewScrollableContainer::clearStatusMessage ()
469{
470#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1
471 kDebug () << "kpViewScrollableContainer::clearStatusMessage()";
472#endif
473 m_bottomRightGrip->setUserMessage(QString());
474 m_bottomGrip->setUserMessage(QString());
475 m_rightGrip->setUserMessage(QString());
476}
477
478//---------------------------------------------------------------------
479
480// protected
481QSize kpViewScrollableContainer::newDocSize (int viewDX, int viewDY) const
482{
483 if (!m_view)
484 return QSize ();
485
486 if (!docResizingGrip ())
487 return QSize ();
488
489 const int docX = (int) m_view->transformViewToDocX (m_view->width () + viewDX);
490 const int docY = (int) m_view->transformViewToDocY (m_view->height () + viewDY);
491
492 return QSize (qMax (1, docX), qMax (1, docY));
493}
494
495//---------------------------------------------------------------------
496
497// protected
498void kpViewScrollableContainer::calculateDocResizingGrip ()
499{
500 if (m_bottomRightGrip->isDrawing ())
501 m_docResizingGrip = m_bottomRightGrip;
502 else if (m_bottomGrip->isDrawing ())
503 m_docResizingGrip = m_bottomGrip;
504 else if (m_rightGrip->isDrawing ())
505 m_docResizingGrip = m_rightGrip;
506 else
507 m_docResizingGrip = 0;
508}
509
510//---------------------------------------------------------------------
511
512// protected
513kpGrip *kpViewScrollableContainer::docResizingGrip () const
514{
515 return m_docResizingGrip;
516}
517
518//---------------------------------------------------------------------
519
520// protected
521int kpViewScrollableContainer::bottomResizeLineWidth () const
522{
523 if (!docResizingGrip ())
524 return -1;
525
526 if (!m_view)
527 return -1;
528
529 if (docResizingGrip ()->type () & kpGrip::Bottom)
530 return qMax (m_view->zoomLevelY () / 100, 1);
531 else
532 return 1;
533}
534
535//---------------------------------------------------------------------
536
537// protected
538int kpViewScrollableContainer::rightResizeLineWidth () const
539{
540 if (!docResizingGrip ())
541 return -1;
542
543 if (!m_view)
544 return -1;
545
546 if (docResizingGrip ()->type () & kpGrip::Right)
547 return qMax (m_view->zoomLevelX () / 100, 1);
548 else
549 return 1;
550}
551
552//---------------------------------------------------------------------
553
554// protected
555QRect kpViewScrollableContainer::bottomResizeLineRect () const
556{
557 if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0)
558 return QRect ();
559
560 QRect visibleArea = QRect(QPoint(horizontalScrollBar()->value(),verticalScrollBar()->value()), viewport()->size());
561
562 return QRect (QPoint (0,
563 m_resizeRoundedLastViewY),
564 QPoint (m_resizeRoundedLastViewX - 1,
565 m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1)).intersected(visibleArea);
566}
567
568//---------------------------------------------------------------------
569
570// protected
571QRect kpViewScrollableContainer::rightResizeLineRect () const
572{
573 if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0)
574 return QRect ();
575
576 QRect visibleArea = QRect(QPoint(horizontalScrollBar()->value(),verticalScrollBar()->value()), viewport()->size());
577
578 return QRect (QPoint (m_resizeRoundedLastViewX,
579 0),
580 QPoint (m_resizeRoundedLastViewX + rightResizeLineWidth () - 1,
581 m_resizeRoundedLastViewY - 1)).intersected(visibleArea);
582}
583
584//---------------------------------------------------------------------
585
586// protected
587QRect kpViewScrollableContainer::bottomRightResizeLineRect () const
588{
589 if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0)
590 return QRect ();
591
592 QRect visibleArea = QRect(QPoint(horizontalScrollBar()->value(),verticalScrollBar()->value()), viewport()->size());
593
594 return QRect (QPoint (m_resizeRoundedLastViewX,
595 m_resizeRoundedLastViewY),
596 QPoint (m_resizeRoundedLastViewX + rightResizeLineWidth () - 1,
597 m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1)).intersected(visibleArea);
598}
599
600//---------------------------------------------------------------------
601
602// private
603QRect kpViewScrollableContainer::mapViewToViewport (const QRect &viewRect)
604{
605 if (!viewRect.isValid ())
606 return QRect ();
607
608 QRect ret = viewRect;
609 ret.translate (-horizontalScrollBar()->value() - viewport()->x(), -verticalScrollBar()->value() - viewport()->y());
610 return ret;
611}
612
613//---------------------------------------------------------------------
614
615void kpViewScrollableContainer::drawResizeLines ()
616{
617 static const char *stipple[] =
618 {
619 "8 8 2 1",
620 ". c #000000",
621 "# c #ffffff",
622 "....####",
623 "....####",
624 "....####",
625 "....####",
626 "####....",
627 "####....",
628 "####....",
629 "####...."
630 };
631
632 QPainter p(m_overlay);
633 p.setBackground(QPixmap(stipple));
634
635 const QRect rightRect = rightResizeLineRect();
636 if ( rightRect.isValid() )
637 {
638 QRect rect = mapViewToViewport(rightRect);
639 p.setBrushOrigin(rect.x(), rect.y());
640 p.eraseRect(rect);
641 }
642
643 const QRect bottomRect = bottomResizeLineRect();
644 if ( bottomRect.isValid() )
645 {
646 QRect rect = mapViewToViewport(bottomRect);
647 p.setBrushOrigin(rect.x(), rect.y());
648 p.eraseRect(rect);
649 }
650
651 const QRect bottomRightRect = bottomRightResizeLineRect ();
652 if ( bottomRightRect.isValid() )
653 {
654 QRect rect = mapViewToViewport(bottomRightRect);
655 p.setBrushOrigin(rect.x(), rect.y());
656 p.eraseRect(rect);
657 }
658}
659
660//---------------------------------------------------------------------
661
662// protected
663void kpViewScrollableContainer::updateResizeLines (int viewX, int viewY,
664 int viewDX, int viewDY)
665{
666#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0
667 kDebug () << "kpViewScrollableContainer::updateResizeLines("
668 << viewX << "," << viewY << ")"
669 << " oldViewX=" << m_resizeRoundedLastViewX
670 << " oldViewY=" << m_resizeRoundedLastViewY
671 << " viewDX=" << viewDX
672 << " viewDY=" << viewDY
673 << endl;
674#endif
675
676
677 if (viewX >= 0 && viewY >= 0)
678 {
679 m_resizeRoundedLastViewX = (int) m_view->transformDocToViewX ((int) m_view->transformViewToDocX (viewX));
680 m_resizeRoundedLastViewY = (int) m_view->transformDocToViewY ((int) m_view->transformViewToDocY (viewY));
681
682 m_resizeRoundedLastViewDX = viewDX;
683 m_resizeRoundedLastViewDY = viewDY;
684 }
685 else
686 {
687 m_resizeRoundedLastViewX = -1;
688 m_resizeRoundedLastViewY = -1;
689
690 m_resizeRoundedLastViewDX = 0;
691 m_resizeRoundedLastViewDY = 0;
692 }
693
694 m_overlay->update();
695}
696
697//---------------------------------------------------------------------
698
699// protected slot
700void kpViewScrollableContainer::slotGripBeganDraw ()
701{
702 if (!m_view)
703 return;
704
705 m_overlay->resize(viewport()->size()); // make it cover whole viewport
706 m_overlay->move(viewport()->pos());
707 m_overlay->show();
708 m_overlay->raise(); // make it top-most
709
710 calculateDocResizingGrip ();
711
712 m_haveMovedFromOriginalDocSize = false;
713
714 updateResizeLines (m_view->width (), m_view->height (),
715 0/*viewDX*/, 0/*viewDY*/);
716
717 emit beganDocResize ();
718}
719
720//---------------------------------------------------------------------
721
722// protected slot
723void kpViewScrollableContainer::slotGripContinuedDraw (int inViewDX, int inViewDY,
724 bool dueToDragScroll)
725{
726 int viewDX = inViewDX,
727 viewDY = inViewDY;
728
729#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
730 kDebug () << "kpViewScrollableContainer::slotGripContinuedDraw("
731 << viewDX << "," << viewDY << ") size="
732 << newDocSize (viewDX, viewDY)
733 << " dueToDragScroll=" << dueToDragScroll
734 << endl;
735#endif
736
737 if (!m_view)
738 return;
739
740 if (!dueToDragScroll &&
741 beginDragScroll(m_view->zoomLevelX ()))
742 {
743 const QPoint newViewDeltaPoint = docResizingGrip ()->viewDeltaPoint ();
744 viewDX = newViewDeltaPoint.x ();
745 viewDY = newViewDeltaPoint.y ();
746 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
747 kDebug () << "\tdrag scrolled - new view delta point="
748 << newViewDeltaPoint
749 << endl;
750 #endif
751 }
752
753 m_haveMovedFromOriginalDocSize = true;
754
755 updateResizeLines (qMax (1, qMax (m_view->width () + viewDX, (int) m_view->transformDocToViewX (1))),
756 qMax (1, qMax (m_view->height () + viewDY, (int) m_view->transformDocToViewY (1))),
757 viewDX, viewDY);
758
759 emit continuedDocResize (newDocSize ());
760}
761
762//---------------------------------------------------------------------
763
764// protected slot
765void kpViewScrollableContainer::slotGripCancelledDraw ()
766{
767 m_haveMovedFromOriginalDocSize = false;
768
769 updateResizeLines (-1, -1, 0, 0);
770
771 calculateDocResizingGrip ();
772
773 emit cancelledDocResize ();
774
775 endDragScroll ();
776
777 m_overlay->hide();
778}
779
780//---------------------------------------------------------------------
781
782// protected slot
783void kpViewScrollableContainer::slotGripEndedDraw (int viewDX, int viewDY)
784{
785#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
786 kDebug () << "kpViewScrollableContainer::slotGripEndedDraw("
787 << viewDX << "," << viewDY << ") size="
788 << newDocSize (viewDX, viewDY)
789 << endl;
790#endif
791
792 if (!m_view)
793 return;
794
795 const QSize newSize = newDocSize (viewDX, viewDY);
796
797 m_haveMovedFromOriginalDocSize = false;
798
799 // must erase lines before view size changes
800 updateResizeLines (-1, -1, 0, 0);
801
802 calculateDocResizingGrip ();
803
804 emit endedDocResize (newSize);
805
806 endDragScroll ();
807
808 m_overlay->hide();
809}
810
811//---------------------------------------------------------------------
812
813// protected slot
814void kpViewScrollableContainer::slotGripStatusMessageChanged (const QString &string)
815{
816 if (string == m_gripStatusMessage)
817 return;
818
819 m_gripStatusMessage = string;
820 emit statusMessageChanged (string);
821}
822
823//---------------------------------------------------------------------
824
825// public slot
826void kpViewScrollableContainer::recalculateStatusMessage ()
827{
828#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
829 kDebug () << "kpViewScrollabelContainer::recalculateStatusMessage()";
830 kDebug () << "\tQCursor::pos=" << QCursor::pos ()
831 << " global visibleRect="
832 << kpWidgetMapper::toGlobal (this,
833 QRect(0, 0, viewport->width(), viewport->height()))
834 << endl;
835#endif
836
837 // HACK: After dragging to a new size, handles move so that they are now
838 // under the mouse pointer but no mouseMoveEvent() is generated for
839 // any grip. This also handles the case of canceling over any
840 // grip.
841 //
842 if (kpWidgetMapper::toGlobal (this,
843 QRect(0, 0, viewport()->width(), viewport()->height()))
844 .contains (QCursor::pos ()))
845 {
846 if ( m_bottomRightGrip->containsCursor() )
847 {
848 m_bottomRightGrip->setUserMessage (i18n ("Left drag the handle to resize the image."));
849 }
850 else if ( m_bottomGrip->containsCursor() )
851 {
852 m_bottomGrip->setUserMessage (i18n ("Left drag the handle to resize the image."));
853 }
854 else if ( m_rightGrip->containsCursor() )
855 {
856 m_rightGrip->setUserMessage (i18n ("Left drag the handle to resize the image."));
857 }
858 else
859 {
860 clearStatusMessage ();
861 }
862 }
863 else
864 {
865 clearStatusMessage ();
866 }
867}
868
869//---------------------------------------------------------------------
870
871// protected slot
872void kpViewScrollableContainer::slotContentsMoved ()
873{
874 kpGrip *grip = docResizingGrip ();
875 if (grip)
876 {
877 grip->mouseMovedTo (grip->mapFromGlobal (QCursor::pos ()),
878 true/*moved due to drag scroll*/);
879 }
880
881 m_overlay->move(viewport()->pos());
882 m_overlay->update();
883
884 emit contentsMoved();
885}
886
887//---------------------------------------------------------------------
888
889// protected
890void kpViewScrollableContainer::disconnectViewSignals ()
891{
892 disconnect (m_view, SIGNAL (sizeChanged (const QSize &)),
893 this, SLOT (updateGrips ()));
894 disconnect (m_view, SIGNAL (destroyed ()),
895 this, SLOT (slotViewDestroyed ()));
896}
897
898//---------------------------------------------------------------------
899
900// protected
901void kpViewScrollableContainer::connectViewSignals ()
902{
903 connect (m_view, SIGNAL (sizeChanged (const QSize &)),
904 this, SLOT (updateGrips ()));
905 connect (m_view, SIGNAL (destroyed ()),
906 this, SLOT (slotViewDestroyed ()));
907}
908
909//---------------------------------------------------------------------
910
911// public
912kpView *kpViewScrollableContainer::view () const
913{
914 return m_view;
915}
916
917//---------------------------------------------------------------------
918
919// public
920void kpViewScrollableContainer::setView (kpView *view)
921{
922 if (m_view == view)
923 return;
924
925 if (m_view)
926 {
927 disconnectViewSignals ();
928 }
929
930 m_view = view;
931
932 if ( m_view )
933 {
934 m_view->setParent(widget());
935 m_view->show();
936 }
937
938 updateGrips ();
939
940 if (m_view)
941 {
942 connectViewSignals ();
943 }
944}
945
946//---------------------------------------------------------------------
947
948// public slot
949void kpViewScrollableContainer::updateGrips ()
950{
951 if (m_view)
952 {
953 widget()->resize(m_view->size() + m_bottomRightGrip->size());
954
955 // to make the grip more easily "touchable" make it as high as the view
956 m_rightGrip->setFixedHeight(m_view->height());
957 m_rightGrip->move(m_view->width(), 0);
958
959 // to make the grip more easily "touchable" make it as wide as the view
960 m_bottomGrip->setFixedWidth(m_view->width());
961 m_bottomGrip->move(0, m_view->height ());
962
963 m_bottomRightGrip->move(m_view->width(), m_view->height());
964 }
965
966 m_bottomGrip->setHidden (m_view == 0);
967 m_rightGrip->setHidden (m_view == 0);
968 m_bottomRightGrip->setHidden (m_view == 0);
969
970 recalculateStatusMessage ();
971}
972
973//---------------------------------------------------------------------
974
975// protected slot
976void kpViewScrollableContainer::slotViewDestroyed ()
977{
978 m_view = 0;
979 updateGrips ();
980}
981
982//---------------------------------------------------------------------
983
984// public slot
985bool kpViewScrollableContainer::beginDragScroll(int zoomLevel, bool *didSomething)
986{
987 if (didSomething)
988 *didSomething = false;
989
990 m_zoomLevel = zoomLevel;
991
992 const QPoint p = mapFromGlobal (QCursor::pos ());
993
994 bool stopDragScroll = true;
995 bool scrolled = false;
996
997 if (!noDragScrollRect ().contains (p))
998 {
999 if (m_dragScrollTimer->isActive ())
1000 {
1001 if (m_scrollTimerRunOnce)
1002 {
1003 scrolled = slotDragScroll ();
1004 }
1005 }
1006 else
1007 {
1008 m_scrollTimerRunOnce = false;
1009 m_dragScrollTimer->start (DragScrollInitialInterval);
1010 }
1011
1012 stopDragScroll = false;
1013 }
1014
1015 if (stopDragScroll)
1016 m_dragScrollTimer->stop ();
1017
1018 if (didSomething)
1019 *didSomething = scrolled;
1020
1021 return scrolled;
1022}
1023
1024//---------------------------------------------------------------------
1025
1026// public slot
1027bool kpViewScrollableContainer::beginDragScroll(int zoomLevel)
1028{
1029 return beginDragScroll(zoomLevel,
1030 0/*don't want scrolled notification*/);
1031}
1032
1033//---------------------------------------------------------------------
1034
1035// public slot
1036bool kpViewScrollableContainer::endDragScroll ()
1037{
1038 if (m_dragScrollTimer->isActive ())
1039 {
1040 m_dragScrollTimer->stop ();
1041 return true;
1042 }
1043 else
1044 {
1045 return false;
1046 }
1047}
1048
1049//---------------------------------------------------------------------
1050
1051static int distanceFromRectToMultiplier (int dist)
1052{
1053 if (dist < 0)
1054 return 0;
1055 else if (dist < DragDistanceFromRectMaxFor1stMultiplier)
1056 return 1;
1057 else if (dist < DragDistanceFromRectMaxFor2ndMultiplier)
1058 return 2;
1059 else
1060 return 4;
1061}
1062
1063//---------------------------------------------------------------------
1064
1065// protected slot
1066bool kpViewScrollableContainer::slotDragScroll (bool *didSomething)
1067{
1068 bool scrolled = false;
1069
1070 if (didSomething)
1071 *didSomething = false;
1072
1073
1074 const QRect rect = noDragScrollRect ();
1075 const QPoint pos = mapFromGlobal (QCursor::pos ());
1076
1077 int dx = 0, dy = 0;
1078 int dxMultiplier = 0, dyMultiplier = 0;
1079
1080 if (pos.x () < rect.left ())
1081 {
1082 dx = -DragScrollNumPixels;
1083 dxMultiplier = distanceFromRectToMultiplier (rect.left () - pos.x ());
1084 }
1085 else if (pos.x () > rect.right ())
1086 {
1087 dx = +DragScrollNumPixels;
1088 dxMultiplier = distanceFromRectToMultiplier (pos.x () - rect.right ());
1089 }
1090
1091 if (pos.y () < rect.top ())
1092 {
1093 dy = -DragScrollNumPixels;
1094 dyMultiplier = distanceFromRectToMultiplier (rect.top () - pos.y ());
1095 }
1096 else if (pos.y () > rect.bottom ())
1097 {
1098 dy = +DragScrollNumPixels;
1099 dyMultiplier = distanceFromRectToMultiplier (pos.y () - rect.bottom ());
1100 }
1101
1102 dx *= dxMultiplier;// * qMax (1, m_zoomLevel / 100);
1103 dy *= dyMultiplier;// * qMax (1, m_zoomLevel / 100);
1104
1105 if (dx || dy)
1106 {
1107 const int oldContentsX = horizontalScrollBar()->value (),
1108 oldContentsY = verticalScrollBar()->value ();
1109
1110 horizontalScrollBar()->setValue(oldContentsX + dx);
1111 verticalScrollBar()->setValue(oldContentsY + dy);
1112
1113 scrolled = (oldContentsX != horizontalScrollBar()->value () ||
1114 oldContentsY != verticalScrollBar()->value ());
1115
1116 if (scrolled)
1117 {
1118 QRegion region = QRect (horizontalScrollBar()->value (), verticalScrollBar()->value (),
1119 viewport()->width(), viewport()->height());
1120 region -= QRect (oldContentsX, oldContentsY,
1121 viewport()->width(), viewport()->height());
1122
1123 // Repaint newly exposed region immediately to reduce tearing
1124 // of scrollView.
1125 m_view->repaint (region);
1126 }
1127 }
1128
1129 m_dragScrollTimer->start (DragScrollInterval);
1130 m_scrollTimerRunOnce = true;
1131
1132 if (didSomething)
1133 *didSomething = scrolled;
1134
1135 return scrolled;
1136}
1137
1138//---------------------------------------------------------------------
1139
1140// protected slot
1141bool kpViewScrollableContainer::slotDragScroll ()
1142{
1143 return slotDragScroll (0/*don't want scrolled notification*/);
1144}
1145
1146//---------------------------------------------------------------------
1147
1148// protected virtual
1149void kpViewScrollableContainer::wheelEvent (QWheelEvent *e)
1150{
1151 e->ignore ();
1152
1153 if (m_view)
1154 m_view->wheelEvent (e);
1155
1156 if ( !e->isAccepted() )
1157 QScrollArea::wheelEvent(e);
1158}
1159
1160//---------------------------------------------------------------------
1161
1162QRect kpViewScrollableContainer::noDragScrollRect () const
1163{
1164 return QRect (DragScrollLeftTopMargin, DragScrollLeftTopMargin,
1165 width () - DragScrollLeftTopMargin - DragScrollRightBottomMargin,
1166 height () - DragScrollLeftTopMargin - DragScrollRightBottomMargin);
1167}
1168
1169//---------------------------------------------------------------------
1170
1171// protected virtual [base QScrollView]
1172void kpViewScrollableContainer::resizeEvent (QResizeEvent *e)
1173{
1174 QScrollArea::resizeEvent (e);
1175
1176 emit resized ();
1177}
1178
1179//---------------------------------------------------------------------
1180
1181#include <kpViewScrollableContainer.moc>
1182