make sure to attach to the document also when a resize event is received prior of...
[kdegraphics.git] / kolourpaint / kpViewScrollableContainer.cpp
blob5d4f6d3cb4cff97858f1cb94020026f12c246293
2 /*
3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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.
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.
28 #define DEBUG_KP_VIEW_SCROLLABLE_CONTAINER 0
30 #include <kpViewScrollableContainer.h>
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>
40 #include <kdebug.h>
41 #include <klocale.h>
43 #include <kpDefs.h>
44 #include <kpPixmapFX.h>
45 #include <kpView.h>
46 #include <kpWidgetMapper.h>
49 // (Pulled from out of Thurston's hat)
50 static const int DragScrollLeftTopMargin = 0;
51 static const int DragScrollRightBottomMargin = 16; // scrollbarish
52 static const int DragScrollInterval = 150;
53 static const int DragScrollInitialInterval = DragScrollInterval * 2;
54 static const int DragScrollNumPixels = 10;
55 static const int DragDistanceFromRectMaxFor1stMultiplier = 50;
56 static const int DragDistanceFromRectMaxFor2ndMultiplier = 100;
58 static const int GripHandleSize = 7;
60 // public static
61 const int kpGrip::Size = 7;
64 kpGrip::kpGrip (GripType type, QWidget *parent)
65 : QLabel (parent),
66 m_type (type),
67 m_startPoint (KP_INVALID_POINT),
68 m_currentPoint (KP_INVALID_POINT),
69 m_shouldReleaseMouseButtons (false)
71 setCursor (cursorForType (m_type));
73 setMouseTracking (true); // mouseMoveEvent's even when no mousebtn down
75 updatePixmap ();
78 kpGrip::~kpGrip ()
83 // public
84 kpGrip::GripType kpGrip::type () const
86 return m_type;
90 // public static
91 QCursor kpGrip::cursorForType (GripType type)
93 switch (type)
95 case kpGrip::Bottom:
96 return Qt::SizeVerCursor;
97 break; // one day you'll forget
99 case kpGrip::Right:
100 return Qt::SizeHorCursor;
101 break; // one day you'll forget
103 case kpGrip::BottomRight:
104 return Qt::SizeFDiagCursor;
105 break; // one day you'll forget
108 return Qt::ArrowCursor;
112 // public
113 QRect kpGrip::hotRect (bool toGlobal) const
115 QRect ret;
117 switch (m_type)
119 case kpGrip::Bottom:
121 const int handleX = (width () - GripHandleSize) / 2;
122 ret = QRect (handleX, 0,
123 GripHandleSize, height ());
124 break;
126 case kpGrip::Right:
128 const int handleY = (height () - GripHandleSize) / 2;
129 ret = QRect (0, handleY,
130 width (), GripHandleSize);
131 break;
133 case kpGrip::BottomRight:
134 // pixmap all opaque
135 ret = rect ();
136 break;
138 default:
139 return QRect ();
142 return (toGlobal ? QRect (mapToGlobal (ret.topLeft ()),
143 mapToGlobal (ret.bottomRight ()))
144 : ret);
148 // public
149 bool kpGrip::isDrawing () const
151 return (m_startPoint != KP_INVALID_POINT);
155 // public
156 QString kpGrip::haventBegunDrawUserMessage () const
158 return i18n ("Left drag the handle to resize the image.");
162 // public
163 QString kpGrip::userMessage () const
165 return m_userMessage;
168 // public
169 void kpGrip::setUserMessage (const QString &message)
171 // Don't do NOP checking here since another grip might have changed
172 // the message so an apparent NOP for this grip is not a NOP in the
173 // global sense (kpViewScrollableContainer::slotGripStatusMessageChanged()).
175 m_userMessage = message;
176 emit statusMessageChanged (message);
180 // protected
181 void kpGrip::updatePixmap ()
183 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
184 kDebug () << "kpGrip::updatePixmap() rect=" << rect ();
185 #endif
186 if (width () <= 0 || height () <= 0)
187 return;
189 QPixmap pixmap (width (), height ());
190 kpPixmapFX::ensureTransparentAt (&pixmap, pixmap.rect ());
192 const QRect hr = hotRect ();
193 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
194 kDebug () << "\thotRect=" << hr;
195 #endif
196 if (hr.isValid ())
198 const QColor col = palette ().color (QPalette::Highlight);
199 if (col.alpha () > 0)
201 // Note that we nuke the color's alpha (QColor::rgb()) since
202 // kpPixmapFX methods can't handle it.
203 kpPixmapFX::fillRect (&pixmap,
204 hr.x (), hr.y (), hr.width (), hr.height (),
205 kpColor (col.rgb ()));
207 else
209 // You have a crazy color palette with a transparent highlight
210 // color. Our pixmap is fully transparent already so nothing
211 // needs to be done.
215 setPixmap (pixmap);
216 setMask (pixmap.mask ());
220 // protected
221 void kpGrip::cancel ()
223 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
224 kDebug () << "kpGrip::cancel()";
225 #endif
226 if (m_currentPoint == KP_INVALID_POINT)
227 return;
229 m_startPoint = KP_INVALID_POINT;
230 m_currentPoint = KP_INVALID_POINT;
232 setUserMessage (i18n ("Resize Image: Let go of all the mouse buttons."));
233 setCursor (Qt::ArrowCursor);
234 m_shouldReleaseMouseButtons = true;
236 releaseKeyboard ();
237 emit cancelledDraw ();
241 // protected virtual [base QWidget]
242 void kpGrip::keyReleaseEvent (QKeyEvent *e)
244 if (m_startPoint != KP_INVALID_POINT &&
245 e->key () == Qt::Key_Escape)
247 cancel ();
251 // protected virtual [base QWidget]
252 void kpGrip::mousePressEvent (QMouseEvent *e)
254 if (m_startPoint == KP_INVALID_POINT &&
255 (e->buttons () & Qt::MouseButtonMask) == Qt::LeftButton)
257 m_startPoint = e->pos ();
258 m_currentPoint = e->pos ();
259 emit beganDraw ();
260 grabKeyboard ();
262 setUserMessage (i18n ("Resize Image: Right click to cancel."));
263 setCursor (cursorForType (m_type));
265 else
267 if (m_startPoint != KP_INVALID_POINT)
268 cancel ();
272 // public
273 QPoint kpGrip::viewDeltaPoint () const
275 if (m_startPoint == KP_INVALID_POINT)
276 return KP_INVALID_POINT;
278 const QPoint point = mapFromGlobal (QCursor::pos ());
280 // TODO: this is getting out of sync with m_currentPoint
282 return QPoint (((m_type & kpGrip::Right) ? point.x () - m_startPoint.x () : 0),
283 ((m_type & kpGrip::Bottom) ? point.y () - m_startPoint.y () : 0));
287 // public
288 void kpGrip::mouseMovedTo (const QPoint &point, bool dueToDragScroll)
290 if (m_startPoint == KP_INVALID_POINT)
291 return;
293 m_currentPoint = point;
295 emit continuedDraw (((m_type & kpGrip::Right) ? point.x () - m_startPoint.x () : 0),
296 ((m_type & kpGrip::Bottom) ? point.y () - m_startPoint.y () : 0),
297 dueToDragScroll);
300 // protected virtual [base QWidget]
301 void kpGrip::mouseMoveEvent (QMouseEvent *e)
303 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
304 kDebug () << "kpGrip::mouseMoveEvent() m_startPoint=" << m_startPoint
305 << " stateAfter: buttons=" << (int *) (int) e->buttons ()
306 << endl;
307 #endif
309 if (m_startPoint == KP_INVALID_POINT)
311 if ((e->buttons () & Qt::MouseButtonMask) == 0)
312 setUserMessage (haventBegunDrawUserMessage ());
313 return;
316 mouseMovedTo (e->pos (), false/*not due to drag scroll*/);
319 // protected virtual [base QWidget]
320 void kpGrip::mouseReleaseEvent (QMouseEvent *e)
322 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
323 kDebug () << "kpGrip::mouseReleaseEvent() m_startPoint=" << m_startPoint
324 << " stateAfter: buttons=" << (int *) (int) e->buttons ()
325 << endl;
326 #endif
328 if (m_startPoint != KP_INVALID_POINT)
330 const int dx = m_currentPoint.x () - m_startPoint.x (),
331 dy = m_currentPoint.y () - m_startPoint.y ();
333 m_currentPoint = KP_INVALID_POINT;
334 m_startPoint = KP_INVALID_POINT;
336 releaseKeyboard ();
337 emit endedDraw ((m_type & kpGrip::Right) ? dx : 0,
338 (m_type & kpGrip::Bottom) ? dy : 0);
341 if ((e->buttons () & Qt::MouseButtonMask) == 0)
343 m_shouldReleaseMouseButtons = false;
344 setUserMessage (QString::null); //krazy:exclude=nullstrassigment for old broken gcc
345 setCursor (cursorForType (m_type));
347 releaseKeyboard ();
348 emit releasedAllButtons ();
353 // protected virtual [base QWidget]
354 void kpGrip::resizeEvent (QResizeEvent *)
356 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
357 kDebug () << "kpGrip::resizeEvent()";
358 #endif
359 updatePixmap ();
363 // protected virtual [base QWidget]
364 void kpGrip::enterEvent (QEvent * /*e*/)
366 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
367 kDebug () << "kpGrip::enterEvent()"
368 << " m_startPoint=" << m_startPoint
369 << " shouldReleaseMouseButtons="
370 << m_shouldReleaseMouseButtons << endl;
371 #endif
373 if (m_startPoint == KP_INVALID_POINT &&
374 !m_shouldReleaseMouseButtons)
376 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
377 kDebug () << "\tsending message";
378 #endif
379 setUserMessage (haventBegunDrawUserMessage ());
383 // protected virtual [base QWidget]
384 void kpGrip::leaveEvent (QEvent * /*e*/)
386 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
387 kDebug () << "kpGrip::leaveEvent()"
388 << " m_startPoint=" << m_startPoint
389 << " shouldReleaseMouseButtons="
390 << m_shouldReleaseMouseButtons << endl;
391 #endif
392 if (m_startPoint == KP_INVALID_POINT &&
393 !m_shouldReleaseMouseButtons)
395 setUserMessage (QString::null); //krazy:exclude=nullstrassigment for old broken gcc
400 // protected virtual [base QWidget]
401 void kpGrip::paintEvent (QPaintEvent *e)
403 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0
404 kDebug () << "kpGrip::paintEvent(" << e->rect () << ")";
405 #endif
406 QLabel::paintEvent (e);
410 // TODO: Are we checking for m_view == 0 often enough? Also an issue in KDE 3.
411 kpViewScrollableContainer::kpViewScrollableContainer (kpMainWindow *parent)
412 : Q3ScrollView ((QWidget *) parent),
413 m_mainWindow (parent),
414 m_contentsXSoon (-1), m_contentsYSoon (-1),
415 m_view (0),
416 m_bottomGrip (new kpGrip (kpGrip::Bottom, viewport ())),
417 m_rightGrip (new kpGrip (kpGrip::Right, viewport ())),
418 m_bottomRightGrip (new kpGrip (kpGrip::BottomRight, viewport ())),
419 m_docResizingGrip (0),
420 m_dragScrollTimer (new QTimer (this)),
421 m_zoomLevel (100),
422 m_scrollTimerRunOnce (false),
423 m_resizeRoundedLastViewX (-1), m_resizeRoundedLastViewY (-1),
424 m_resizeRoundedLastViewDX (0), m_resizeRoundedLastViewDY (0),
425 m_haveMovedFromOriginalDocSize (false)
428 m_bottomGrip->setObjectName ("Bottom Grip");
429 m_rightGrip->setObjectName ("Right Grip");
430 m_bottomRightGrip->setObjectName ("BottomRight Grip");
432 // HITODO: drawResizeLines() uses this feature -- it therefore
433 // flickers and only works on X11. Fix drawResizeLines()
434 // to remove the need for these attributes.
435 viewport ()->setAttribute (Qt::WA_PaintOutsidePaintEvent);
436 viewport ()->setAttribute (Qt::WA_PaintUnclipped);
438 m_bottomGrip->setFixedHeight (kpGrip::Size);
439 m_bottomGrip->hide ();
440 addChild (m_bottomGrip);
441 connectGripSignals (m_bottomGrip);
443 m_rightGrip->setFixedWidth (kpGrip::Size);
444 m_rightGrip->hide ();
445 addChild (m_rightGrip);
446 connectGripSignals (m_rightGrip);
448 m_bottomRightGrip->setFixedSize (kpGrip::Size, kpGrip::Size);
449 m_bottomRightGrip->hide ();
450 addChild (m_bottomRightGrip);
451 connectGripSignals (m_bottomRightGrip);
454 connect (this, SIGNAL (contentsMoving (int, int)),
455 this, SLOT (slotContentsMoving (int, int)));
457 connect (m_dragScrollTimer, SIGNAL (timeout ()),
458 this, SLOT (slotDragScroll ()));
461 kpViewScrollableContainer::~kpViewScrollableContainer ()
466 // public
467 int kpViewScrollableContainer::contentsXSoon ()
469 if (m_contentsXSoon < 0)
470 return contentsX ();
471 else
472 return m_contentsXSoon;
475 // public
476 int kpViewScrollableContainer::contentsYSoon ()
478 if (m_contentsYSoon < 0)
479 return contentsY ();
480 else
481 return m_contentsYSoon;
485 // protected
486 void kpViewScrollableContainer::connectGripSignals (kpGrip *grip)
488 connect (grip, SIGNAL (beganDraw ()),
489 this, SLOT (slotGripBeganDraw ()));
490 connect (grip, SIGNAL (continuedDraw (int, int, bool)),
491 this, SLOT (slotGripContinuedDraw (int, int, bool)));
492 connect (grip, SIGNAL (cancelledDraw ()),
493 this, SLOT (slotGripCancelledDraw ()));
494 connect (grip, SIGNAL (endedDraw (int, int)),
495 this, SLOT (slotGripEndedDraw (int, int)));
497 connect (grip, SIGNAL (statusMessageChanged (const QString &)),
498 this, SLOT (slotGripStatusMessageChanged (const QString &)));
500 connect (grip, SIGNAL (releasedAllButtons ()),
501 this, SLOT (recalculateStatusMessage ()));
505 // public
506 QSize kpViewScrollableContainer::newDocSize () const
508 return newDocSize (m_resizeRoundedLastViewDX,
509 m_resizeRoundedLastViewDY);
512 // public
513 bool kpViewScrollableContainer::haveMovedFromOriginalDocSize () const
515 return m_haveMovedFromOriginalDocSize;
518 // public
519 QString kpViewScrollableContainer::statusMessage () const
521 return m_gripStatusMessage;
524 // public
525 void kpViewScrollableContainer::clearStatusMessage ()
527 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1
528 kDebug () << "kpViewScrollableContainer::clearStatusMessage()";
529 #endif
530 m_bottomRightGrip->setUserMessage (QString::null); //krazy:exclude=nullstrassigment for old broken gcc
531 m_bottomGrip->setUserMessage (QString::null); //krazy:exclude=nullstrassigment for old broken gcc
532 m_rightGrip->setUserMessage (QString::null); //krazy:exclude=nullstrassigment for old broken gcc
535 // protected
536 QSize kpViewScrollableContainer::newDocSize (int viewDX, int viewDY) const
538 if (!m_view)
539 return QSize ();
541 if (!docResizingGrip ())
542 return QSize ();
544 const int docX = (int) m_view->transformViewToDocX (m_view->width () + viewDX);
545 const int docY = (int) m_view->transformViewToDocY (m_view->height () + viewDY);
547 return QSize (qMax (1, docX), qMax (1, docY));
551 // protected
552 void kpViewScrollableContainer::calculateDocResizingGrip ()
554 if (m_bottomRightGrip->isDrawing ())
555 m_docResizingGrip = m_bottomRightGrip;
556 else if (m_bottomGrip->isDrawing ())
557 m_docResizingGrip = m_bottomGrip;
558 else if (m_rightGrip->isDrawing ())
559 m_docResizingGrip = m_rightGrip;
560 else
561 m_docResizingGrip = 0;
564 // protected
565 kpGrip *kpViewScrollableContainer::docResizingGrip () const
567 return m_docResizingGrip;
571 // protected
572 int kpViewScrollableContainer::bottomResizeLineWidth () const
574 if (!docResizingGrip ())
575 return -1;
577 if (!m_view)
578 return -1;
580 if (docResizingGrip ()->type () & kpGrip::Bottom)
581 return qMax (m_view->zoomLevelY () / 100, 1);
582 else
583 return 1;
586 // protected
587 int kpViewScrollableContainer::rightResizeLineWidth () const
589 if (!docResizingGrip ())
590 return -1;
592 if (!m_view)
593 return -1;
595 if (docResizingGrip ()->type () & kpGrip::Right)
596 return qMax (m_view->zoomLevelX () / 100, 1);
597 else
598 return 1;
602 // protected
603 QRect kpViewScrollableContainer::bottomResizeLineRect () const
605 if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0)
606 return QRect ();
608 QRect visibleArea = QRect(QPoint(contentsX(),contentsY()), viewport()->size());
610 return QRect (QPoint (0,
611 m_resizeRoundedLastViewY),
612 QPoint (m_resizeRoundedLastViewX - 1,
613 m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1)).intersected(visibleArea);
616 // protected
617 QRect kpViewScrollableContainer::rightResizeLineRect () const
619 if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0)
620 return QRect ();
622 QRect visibleArea = QRect(QPoint(contentsX(),contentsY()), viewport()->size());
624 return QRect (QPoint (m_resizeRoundedLastViewX,
626 QPoint (m_resizeRoundedLastViewX + rightResizeLineWidth () - 1,
627 m_resizeRoundedLastViewY - 1)).intersected(visibleArea);
630 // protected
631 QRect kpViewScrollableContainer::bottomRightResizeLineRect () const
633 if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0)
634 return QRect ();
636 QRect visibleArea = QRect(QPoint(contentsX(),contentsY()), viewport()->size());
638 return QRect (QPoint (m_resizeRoundedLastViewX,
639 m_resizeRoundedLastViewY),
640 QPoint (m_resizeRoundedLastViewX + rightResizeLineWidth () - 1,
641 m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1)).intersected(visibleArea);
645 // TODO: are these 2 correct? Remember that viewport()->x() == 1, viewport()->y() == 1
647 // protected
648 QPoint kpViewScrollableContainer::mapViewToViewport (const QPoint &viewPoint)
650 return viewPoint - QPoint (contentsX (), contentsY ());
653 // protected
654 QRect kpViewScrollableContainer::mapViewToViewport (const QRect &viewRect)
656 if (!viewRect.isValid ())
657 return QRect ();
659 QRect ret = viewRect;
660 ret.translate (-contentsX (), -contentsY ());
661 return ret;
665 // protected
666 QRect kpViewScrollableContainer::mapViewportToGlobal (const QRect &viewportRect)
668 return kpWidgetMapper::toGlobal (viewport (), viewportRect);
671 // protected
672 QRect kpViewScrollableContainer::mapViewToGlobal (const QRect &viewRect)
674 return mapViewportToGlobal (mapViewToViewport (viewRect));
678 // protected
679 void kpViewScrollableContainer::repaintWidgetAtResizeLineViewRect (
680 QWidget *widget, const QRect &resizeLineViewRect)
682 const QRect resizeLineGlobalRect = mapViewToGlobal (resizeLineViewRect);
683 const QRect widgetGlobalRect = kpWidgetMapper::toGlobal (widget,
684 widget->rect ());
686 const QRect redrawGlobalRect =
687 resizeLineGlobalRect.intersect (widgetGlobalRect);
689 const QRect redrawWidgetRect =
690 kpWidgetMapper::fromGlobal (widget, redrawGlobalRect);
693 if (redrawWidgetRect.isValid ())
695 widget->repaint (redrawWidgetRect);
699 // protected
700 void kpViewScrollableContainer::repaintWidgetAtResizeLines (QWidget *widget)
702 repaintWidgetAtResizeLineViewRect (widget, rightResizeLineRect ());
703 repaintWidgetAtResizeLineViewRect (widget, bottomResizeLineRect ());
704 repaintWidgetAtResizeLineViewRect (widget, bottomRightResizeLineRect ());
707 // protected
708 void kpViewScrollableContainer::eraseResizeLines ()
710 if (m_resizeRoundedLastViewX >= 0 && m_resizeRoundedLastViewY >= 0)
712 repaintWidgetAtResizeLines (viewport ());
713 repaintWidgetAtResizeLines (m_view);
715 repaintWidgetAtResizeLines (m_bottomGrip);
716 repaintWidgetAtResizeLines (m_rightGrip);
717 repaintWidgetAtResizeLines (m_bottomRightGrip);
722 // protected
723 void kpViewScrollableContainer::drawResizeLines ()
725 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0
726 kDebug () << "kpViewScrollableContainer::drawResizeLines()"
727 << " lastViewX=" << m_resizeRoundedLastViewX
728 << " lastViewY=" << m_resizeRoundedLastViewY
729 << endl;
730 #endif
733 // TODO: If XRENDER is disabled, this painting with Qt::WA_PaintUnclipped
734 // seems to trigger a harmless, asynchronous error:
736 // X Error: RenderBadPicture (invalid Picture parameter) 180
737 // Extension: 153 (RENDER)
738 // Minor opcode: 5 (RenderChangePicture)
739 // Resource id: 0x0
741 // I don't know what code is _directly_ triggering that error -- running
742 // KolourPaint with "-sync" doesn't seem to make the error synchronous.
744 // The API doc for Qt::WA_PaintUnclipped says "This flag is only
745 // supported for widgets for which the WA_PaintOnScreen". We don't
746 // actually enable "WA_PaintOnScreen" (which looks quite scary), so that
747 // might be part of the cause.
748 #define FILL_NOT_RECT(rect) \
749 kpPixmapFX::widgetFillNOTRect (viewport (), \
750 rect.x (), rect.y (), rect.width (), rect.height (), \
751 kpColor::Black/*1st hint color if "Raster NOT" not supported*/, \
752 kpColor::White/*2nd hint color if "Raster NOT" not supported*/)
754 const QRect rightRect = rightResizeLineRect ();
755 if (rightRect.isValid ())
756 FILL_NOT_RECT (mapViewToViewport (rightRect));
758 const QRect bottomRect = bottomResizeLineRect ();
759 if (bottomRect.isValid ())
760 FILL_NOT_RECT (mapViewToViewport (bottomRect));
762 const QRect bottomRightRect = bottomRightResizeLineRect ();
763 if (bottomRightRect.isValid ())
764 FILL_NOT_RECT (mapViewToViewport (bottomRightRect));
766 #undef FILL_NOT_RECT
770 // protected
771 void kpViewScrollableContainer::updateResizeLines (int viewX, int viewY,
772 int viewDX, int viewDY)
774 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0
775 kDebug () << "kpViewScrollableContainer::updateResizeLines("
776 << viewX << "," << viewY << ")"
777 << " oldViewX=" << m_resizeRoundedLastViewX
778 << " oldViewY=" << m_resizeRoundedLastViewY
779 << " viewDX=" << viewDX
780 << " viewDY=" << viewDY
781 << endl;
782 #endif
784 eraseResizeLines ();
787 if (viewX >= 0 && viewY >= 0)
789 m_resizeRoundedLastViewX = (int) m_view->transformDocToViewX ((int) m_view->transformViewToDocX (viewX));
790 m_resizeRoundedLastViewY = (int) m_view->transformDocToViewY ((int) m_view->transformViewToDocY (viewY));
792 m_resizeRoundedLastViewDX = viewDX;
793 m_resizeRoundedLastViewDY = viewDY;
795 else
797 m_resizeRoundedLastViewX = -1;
798 m_resizeRoundedLastViewY = -1;
800 m_resizeRoundedLastViewDX = 0;
801 m_resizeRoundedLastViewDY = 0;
804 // TODO: This is suboptimal since if another window pops up on top of
805 // KolourPaint then disappears, the lines are not redrawn
806 // (although this doesn't happen very frequently since we grab the
807 // keyboard and mouse when resizing):
809 // e.g. sleep 5 && gedit & sleep 10 && killall gedit
811 // Should be done in the paintEvent's of every child of the
812 // scrollview.
813 drawResizeLines ();
817 // protected slot
818 void kpViewScrollableContainer::slotGripBeganDraw ()
820 if (!m_view)
821 return;
823 calculateDocResizingGrip ();
825 m_haveMovedFromOriginalDocSize = false;
827 updateResizeLines (m_view->width (), m_view->height (),
828 0/*viewDX*/, 0/*viewDY*/);
830 emit beganDocResize ();
833 // protected slot
834 void kpViewScrollableContainer::slotGripContinuedDraw (int inViewDX, int inViewDY,
835 bool dueToDragScroll)
837 int viewDX = inViewDX,
838 viewDY = inViewDY;
840 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
841 kDebug () << "kpViewScrollableContainer::slotGripContinuedDraw("
842 << viewDX << "," << viewDY << ") size="
843 << newDocSize (viewDX, viewDY)
844 << " dueToDragScroll=" << dueToDragScroll
845 << endl;
846 #endif
848 if (!m_view)
849 return;
851 if (!dueToDragScroll &&
852 beginDragScroll (QPoint (), QPoint (), m_view->zoomLevelX ()))
854 const QPoint newViewDeltaPoint = docResizingGrip ()->viewDeltaPoint ();
855 viewDX = newViewDeltaPoint.x ();
856 viewDY = newViewDeltaPoint.y ();
857 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
858 kDebug () << "\tdrag scrolled - new view delta point="
859 << newViewDeltaPoint
860 << endl;
861 #endif
864 m_haveMovedFromOriginalDocSize = true;
866 updateResizeLines (qMax (1, qMax (m_view->width () + viewDX, (int) m_view->transformDocToViewX (1))),
867 qMax (1, qMax (m_view->height () + viewDY, (int) m_view->transformDocToViewY (1))),
868 viewDX, viewDY);
870 emit continuedDocResize (newDocSize ());
873 // protected slot
874 void kpViewScrollableContainer::slotGripCancelledDraw ()
876 m_haveMovedFromOriginalDocSize = false;
878 updateResizeLines (-1, -1, 0, 0);
880 calculateDocResizingGrip ();
882 emit cancelledDocResize ();
884 endDragScroll ();
887 // protected slot
888 void kpViewScrollableContainer::slotGripEndedDraw (int viewDX, int viewDY)
890 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
891 kDebug () << "kpViewScrollableContainer::slotGripEndedDraw("
892 << viewDX << "," << viewDY << ") size="
893 << newDocSize (viewDX, viewDY)
894 << endl;
895 #endif
897 if (!m_view)
898 return;
900 const QSize newSize = newDocSize (viewDX, viewDY);
902 m_haveMovedFromOriginalDocSize = false;
904 // must erase lines before view size changes
905 updateResizeLines (-1, -1, 0, 0);
907 calculateDocResizingGrip ();
909 emit endedDocResize (newSize);
911 endDragScroll ();
915 // protected slot
916 void kpViewScrollableContainer::slotGripStatusMessageChanged (const QString &string)
918 if (string == m_gripStatusMessage)
919 return;
921 m_gripStatusMessage = string;
922 emit statusMessageChanged (string);
926 // public slot
927 void kpViewScrollableContainer::recalculateStatusMessage ()
929 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
930 kDebug () << "kpViewScrollabelContainer::recalculateStatusMessage()";
931 kDebug () << "\tQCursor::pos=" << QCursor::pos ()
932 << " global visibleRect="
933 << kpWidgetMapper::toGlobal (this,
934 QRect (0, 0, visibleWidth (), visibleHeight ()))
935 << " brGrip.hotRect=" << m_bottomRightGrip->hotRect (true)
936 << " bGrip.hotRect=" << m_bottomGrip->hotRect (true)
937 << " rGrip.hotRect=" << m_rightGrip->hotRect (true)
938 << endl;
939 #endif
941 // HACK: After dragging to a new size, handles move so that they are now
942 // under the mouse pointer but no mouseMoveEvent() is generated for
943 // any grip. This also handles the case of canceling over any
944 // grip.
946 if (kpWidgetMapper::toGlobal (this,
947 QRect (0, 0, visibleWidth (), visibleHeight ()))
948 .contains (QCursor::pos ()))
950 if (!m_bottomRightGrip->isHidden () &&
951 m_bottomRightGrip->hotRect (true/*to global*/)
952 .contains (QCursor::pos ()))
954 m_bottomRightGrip->setUserMessage (i18n ("Left drag the handle to resize the image."));
956 else if (!m_bottomGrip->isHidden () &&
957 m_bottomGrip->hotRect (true/*to global*/)
958 .contains (QCursor::pos ()))
960 m_bottomGrip->setUserMessage (i18n ("Left drag the handle to resize the image."));
962 else if (!m_rightGrip->isHidden () &&
963 m_rightGrip->hotRect (true/*to global*/)
964 .contains (QCursor::pos ()))
966 m_rightGrip->setUserMessage (i18n ("Left drag the handle to resize the image."));
968 else
970 clearStatusMessage ();
973 else
975 clearStatusMessage ();
980 // protected slot
981 void kpViewScrollableContainer::slotContentsMoving (int x, int y)
983 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
984 kDebug () << "kpViewScrollableContainer::slotContentsMoving("
985 << x << "," << y << ")"
986 << " contentsX=" << contentsX ()
987 << " contentsY=" << contentsY () << endl;
988 #endif
990 m_contentsXSoon = x, m_contentsYSoon = y;
991 emit contentsMovingSoon (m_contentsXSoon, m_contentsYSoon);
993 // Reduce flicker - don't let QScrollView scroll to-be-erased lines
994 eraseResizeLines ();
996 QTimer::singleShot (0, this, SLOT (slotContentsMoved ()));
999 // protected slot
1000 void kpViewScrollableContainer::slotContentsMoved ()
1002 m_contentsXSoon = m_contentsYSoon = -1;
1004 kpGrip *grip = docResizingGrip ();
1005 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1006 kDebug () << "kpViewScrollableContainer::slotContentsMoved()"
1007 << " grip=" << grip
1008 << " contentsX=" << contentsX ()
1009 << " contentsY=" << contentsY () << endl;
1010 #endif
1011 if (!grip)
1012 return;
1014 grip->mouseMovedTo (grip->mapFromGlobal (QCursor::pos ()),
1015 true/*moved due to drag scroll*/);
1019 // protected
1020 void kpViewScrollableContainer::disconnectViewSignals ()
1022 disconnect (m_view, SIGNAL (sizeChanged (const QSize &)),
1023 this, SLOT (updateGrips ()));
1024 disconnect (m_view, SIGNAL (destroyed ()),
1025 this, SLOT (slotViewDestroyed ()));
1028 // protected
1029 void kpViewScrollableContainer::connectViewSignals ()
1031 connect (m_view, SIGNAL (sizeChanged (const QSize &)),
1032 this, SLOT (updateGrips ()));
1033 connect (m_view, SIGNAL (destroyed ()),
1034 this, SLOT (slotViewDestroyed ()));
1038 // public virtual [base QScrollView]
1039 void kpViewScrollableContainer::addChild (QWidget *widget, int x, int y)
1041 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1042 kDebug () << "kpViewScrollableContainer::addChild(" << widget
1043 << "," << x << "," << y << endl;
1044 #endif
1046 Q3ScrollView::addChild (widget, x, y);
1048 kpView *view = dynamic_cast <kpView *> (widget);
1049 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1050 kDebug () << "\tcast to kpView: " << view;
1051 #endif
1052 if (view)
1054 setView (view);
1059 // public
1060 kpView *kpViewScrollableContainer::view () const
1062 return m_view;
1065 // public
1066 void kpViewScrollableContainer::setView (kpView *view)
1068 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1069 kDebug () << "kpViewScrollableContainer::setView(" << view << ")";
1070 #endif
1072 if (m_view == view)
1073 return;
1075 if (m_view)
1077 disconnectViewSignals ();
1080 m_view = view;
1082 updateGrips ();
1084 if (m_view)
1086 connectViewSignals ();
1091 // public slot
1092 void kpViewScrollableContainer::updateGrips ()
1094 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1095 kDebug () << "kpViewScrollableContainer::updateGrips() m_view="
1096 << m_view
1097 << " (size=" << (m_view ? m_view->size () : QSize ()) << ")"
1098 << endl;
1099 #endif
1101 if (m_view)
1103 m_bottomGrip->setFixedWidth (m_view->width ());
1104 moveChild (m_bottomGrip, 0, m_view->height ());
1106 m_rightGrip->setFixedHeight (m_view->height ());
1107 moveChild (m_rightGrip, m_view->width (), 0);
1109 moveChild (m_bottomRightGrip, m_view->width (), m_view->height ());
1110 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1111 kDebug () << "\tbottomRightGrip=" << m_bottomRightGrip->pos ();
1112 #endif
1115 m_bottomGrip->setHidden (m_view == 0);
1116 m_rightGrip->setHidden (m_view == 0);
1117 m_bottomRightGrip->setHidden (m_view == 0);
1119 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1120 kDebug () << "\tcontentsRect=" << contentsRect ()
1121 << " visibleRect=" << visibleRect ()
1122 << " viewportRect=" << viewport ()->rect ()
1123 << endl;
1124 #endif
1126 if (m_view)
1128 resizeContents (m_view->width () + m_rightGrip->width (),
1129 m_view->height () + m_bottomGrip->height ());
1131 else
1133 resizeContents (0, 0);
1136 recalculateStatusMessage ();
1139 // protected slot
1140 void kpViewScrollableContainer::slotViewDestroyed ()
1142 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1143 kDebug () << "kpViewScrollableContainer::slotViewDestroyed() m_view="
1144 << m_view << endl;
1145 #endif
1147 m_view = 0;
1148 updateGrips ();
1152 // public slot
1153 bool kpViewScrollableContainer::beginDragScroll (const QPoint &/*docPoint*/,
1154 const QPoint &/*lastDocPoint*/,
1155 int zoomLevel,
1156 bool *didSomething)
1158 if (didSomething)
1159 *didSomething = false;
1161 m_zoomLevel = zoomLevel;
1163 const QPoint p = mapFromGlobal (QCursor::pos ());
1165 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1166 kDebug () << "kpViewScrollableContainer::beginDragScroll() p=" << p
1167 << " dragScrollTimerRunOnce=" << m_scrollTimerRunOnce
1168 << endl;
1169 #endif
1171 bool stopDragScroll = true;
1172 bool scrolled = false;
1174 if (!noDragScrollRect ().contains (p))
1176 if (m_dragScrollTimer->isActive ())
1178 if (m_scrollTimerRunOnce)
1180 scrolled = slotDragScroll ();
1183 else
1185 m_scrollTimerRunOnce = false;
1186 m_dragScrollTimer->start (DragScrollInitialInterval);
1189 stopDragScroll = false;
1192 if (stopDragScroll)
1193 m_dragScrollTimer->stop ();
1195 if (didSomething)
1196 *didSomething = scrolled;
1198 return scrolled;
1201 // public slot
1202 bool kpViewScrollableContainer::beginDragScroll (const QPoint &docPoint,
1203 const QPoint &lastDocPoint,
1204 int zoomLevel)
1206 return beginDragScroll (docPoint, lastDocPoint, zoomLevel,
1207 0/*don't want scrolled notification*/);
1211 // public slot
1212 bool kpViewScrollableContainer::endDragScroll ()
1214 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1215 kDebug () << "kpViewScrollableContainer::endDragScroll()";
1216 #endif
1218 if (m_dragScrollTimer->isActive ())
1220 m_dragScrollTimer->stop ();
1221 return true;
1223 else
1225 return false;
1230 static const int distanceFromRectToMultiplier (int dist)
1232 if (dist < 0)
1233 return 0;
1234 else if (dist < DragDistanceFromRectMaxFor1stMultiplier)
1235 return 1;
1236 else if (dist < DragDistanceFromRectMaxFor2ndMultiplier)
1237 return 2;
1238 else
1239 return 4;
1243 // protected slot
1244 bool kpViewScrollableContainer::slotDragScroll (bool *didSomething)
1246 bool scrolled = false;
1248 if (didSomething)
1249 *didSomething = false;
1252 const QRect rect = noDragScrollRect ();
1253 const QPoint pos = mapFromGlobal (QCursor::pos ());
1255 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1256 for (int i = 0; i < 10; i++)
1257 kDebug () << QString ();
1259 kDebug () << "kpViewScrollableContainer::slotDragScroll()"
1260 << " noDragScrollRect=" << rect
1261 << " pos=" << pos
1262 << " contentsX=" << contentsX ()
1263 << " contentsY=" << contentsY () << endl;
1264 #endif
1266 int dx = 0, dy = 0;
1267 int dxMultiplier = 0, dyMultiplier = 0;
1269 if (pos.x () < rect.left ())
1271 dx = -DragScrollNumPixels;
1272 dxMultiplier = distanceFromRectToMultiplier (rect.left () - pos.x ());
1274 else if (pos.x () > rect.right ())
1276 dx = +DragScrollNumPixels;
1277 dxMultiplier = distanceFromRectToMultiplier (pos.x () - rect.right ());
1280 if (pos.y () < rect.top ())
1282 dy = -DragScrollNumPixels;
1283 dyMultiplier = distanceFromRectToMultiplier (rect.top () - pos.y ());
1285 else if (pos.y () > rect.bottom ())
1287 dy = +DragScrollNumPixels;
1288 dyMultiplier = distanceFromRectToMultiplier (pos.y () - rect.bottom ());
1291 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1
1292 kDebug () << "kpViewScrollableContainer::slotDragScroll()"
1293 << " dx=" << dx << " * " << dxMultiplier
1294 << " dy=" << dy << " * " << dyMultiplier
1295 << " zoomLevel=" << m_zoomLevel
1296 << endl;
1297 #endif
1299 dx *= dxMultiplier;// * qMax (1, m_zoomLevel / 100);
1300 dy *= dyMultiplier;// * qMax (1, m_zoomLevel / 100);
1302 if (dx || dy)
1304 const int oldContentsX = contentsX (),
1305 oldContentsY = contentsY ();
1307 scrollBy (dx, dy);
1309 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1
1310 kDebug () << "\tafter scrollBy():"
1311 << " contentsX=" << contentsX ()
1312 << " contentsY=" << contentsY () << endl;
1313 #endif
1315 scrolled = (oldContentsX != contentsX () ||
1316 oldContentsY != contentsY ());
1318 if (scrolled)
1320 QRegion region = QRect (contentsX (), contentsY (),
1321 visibleWidth (), visibleHeight ());
1322 region -= QRect (oldContentsX, oldContentsY,
1323 visibleWidth (), visibleHeight ());
1325 // Repaint newly exposed region immediately to reduce tearing
1326 // of scrollView.
1327 #if 1
1328 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1
1329 kDebug () << "\t\tscrolled - repaint new region:" << region;
1330 #endif
1331 m_view->repaint (region);
1332 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1
1333 kDebug () << "\t\tscrolled - repainted";
1334 #endif
1335 #endif
1340 m_dragScrollTimer->start (DragScrollInterval);
1341 m_scrollTimerRunOnce = true;
1344 if (didSomething)
1345 *didSomething = scrolled;
1348 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1
1349 for (int i = 0; i < 10; i++)
1350 kDebug () << QString ();
1351 #endif
1353 return scrolled;
1356 // protected virtual [base QScrollView]
1357 void kpViewScrollableContainer::contentsDragMoveEvent (QDragMoveEvent *e)
1359 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1360 kDebug () << "kpViewScrollableContainer::contentsDragMoveEvent"
1361 << e->pos ()
1362 << endl;
1363 #endif
1365 Q3ScrollView::contentsDragMoveEvent (e);
1368 // protected slot
1369 bool kpViewScrollableContainer::slotDragScroll ()
1371 return slotDragScroll (0/*don't want scrolled notification*/);
1375 // protected virtual [base QScrollView]
1376 void kpViewScrollableContainer::contentsMouseMoveEvent (QMouseEvent *e)
1378 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1379 kDebug () << "kpViewScrollableContainer::contentsMouseMoveEvent"
1380 << e->pos ()
1381 << endl;
1382 #endif
1384 Q3ScrollView::contentsMouseMoveEvent (e);
1387 // protected virtual [base QScrollView]
1388 void kpViewScrollableContainer::mouseMoveEvent (QMouseEvent *e)
1390 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1391 kDebug () << "kpViewScrollableContainer::mouseMoveEvent"
1392 << e->pos ()
1393 << endl;
1394 #endif
1396 Q3ScrollView::mouseMoveEvent (e);
1400 // protected virtual [base QScrollView]
1401 void kpViewScrollableContainer::contentsWheelEvent (QWheelEvent *e)
1403 e->ignore ();
1405 if (m_view)
1406 m_view->wheelEvent (e);
1408 if (!e->isAccepted ())
1409 Q3ScrollView::contentsWheelEvent (e);
1413 QRect kpViewScrollableContainer::noDragScrollRect () const
1415 return QRect (DragScrollLeftTopMargin, DragScrollLeftTopMargin,
1416 width () - DragScrollLeftTopMargin - DragScrollRightBottomMargin,
1417 height () - DragScrollLeftTopMargin - DragScrollRightBottomMargin);
1420 // protected virtual [base QScrollView]
1421 bool kpViewScrollableContainer::eventFilter (QObject *watchedObject, QEvent *event)
1423 return Q3ScrollView::eventFilter (watchedObject, event);
1426 // protected virtual [base QScrollView]
1427 void kpViewScrollableContainer::viewportPaintEvent (QPaintEvent *e)
1429 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1430 kDebug () << "kpViewScrollableContainer::viewportPaintEvent("
1431 << e->rect ()
1432 << ")" << endl;
1433 #endif
1435 Q3ScrollView::viewportPaintEvent (e);
1437 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1438 kDebug () << "done";
1439 #endif
1442 // protected virtual [base QFrame]
1443 void kpViewScrollableContainer::paintEvent (QPaintEvent *e)
1445 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1446 kDebug () << "kpViewScrollableContainer::paintEvent("
1447 << e->rect ()
1448 << ")" << endl;
1449 #endif
1451 Q3ScrollView::paintEvent (e);
1453 #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER
1454 kDebug () << "done";
1455 #endif
1458 // protected virtual [base QScrollView]
1459 void kpViewScrollableContainer::resizeEvent (QResizeEvent *e)
1461 Q3ScrollView::resizeEvent (e);
1463 emit resized ();
1467 #include <kpViewScrollableContainer.moc>