diff options
Diffstat (limited to 'kolourpaint/kpviewscrollablecontainer.cpp')
-rw-r--r-- | kolourpaint/kpviewscrollablecontainer.cpp | 1390 |
1 files changed, 1390 insertions, 0 deletions
diff --git a/kolourpaint/kpviewscrollablecontainer.cpp b/kolourpaint/kpviewscrollablecontainer.cpp new file mode 100644 index 00000000..637f71b7 --- /dev/null +++ b/kolourpaint/kpviewscrollablecontainer.cpp @@ -0,0 +1,1390 @@ + +/* + Copyright (c) 2003,2004,2005 Clarence Dang <[email protected]> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define DEBUG_KP_VIEW_SCROLLABLE_CONTAINER 0 + +#include <kpviewscrollablecontainer.h> + +#include <qcursor.h> +#include <qpainter.h> +#include <qpen.h> +#include <qpixmap.h> +#include <qtimer.h> + +#include <kdebug.h> +#include <klocale.h> + +#include <kpdefs.h> +#include <kppixmapfx.h> +#include <kpview.h> +#include <kpwidgetmapper.h> + + +// (Pulled from out of Thurston's hat) +static const int DragScrollLeftTopMargin = 0; +static const int DragScrollRightBottomMargin = 16; // scrollbarish +static const int DragScrollInterval = 1000 / 10; +static const int DragScrollInitialInterval = DragScrollInterval * 2; +static const int DragScrollNumPixels = 5; +static const int DragDistanceFromRectMaxFor1stMultiplier = 50; +static const int DragDistanceFromRectMaxFor2ndMultiplier = 100; + +static const int GripSize = 7; +static const int GripHandleSize = 7; + + +kpGrip::kpGrip (GripType type, + QWidget *parent, const char *name) + : QWidget (parent, name), + m_type (type), + m_startPoint (KP_INVALID_POINT), + m_currentPoint (KP_INVALID_POINT), + m_shouldReleaseMouseButtons (false) +{ + setCursor (cursorForType (m_type)); + + setMouseTracking (true); // mouseMoveEvent's even when no mousebtn down + + updatePixmap (); +} + +kpGrip::~kpGrip () +{ +} + + +// public +kpGrip::GripType kpGrip::type () const +{ + return m_type; +} + + +// public static +const QCursor &kpGrip::cursorForType (GripType type) +{ + switch (type) + { + case Bottom: + return Qt::sizeVerCursor; + break; // one day you'll forget + + case Right: + return Qt::sizeHorCursor; + break; // one day you'll forget + + case BottomRight: + return Qt::sizeFDiagCursor; + break; // one day you'll forget + } + + return Qt::arrowCursor; +} + + +// public +QRect kpGrip::hotRect (bool toGlobal) const +{ + QRect ret; + + switch (m_type) + { + case Bottom: + { + const int handleX = (width () - GripHandleSize) / 2; + ret = QRect (handleX, 0, + GripHandleSize, height ()); + break; + } + case Right: + { + const int handleY = (height () - GripHandleSize) / 2; + ret = QRect (0, handleY, + width (), GripHandleSize); + break; + } + case BottomRight: + // pixmap all opaque + ret = rect (); + break; + + default: + return QRect (); + } + + return (toGlobal ? QRect (mapToGlobal (ret.topLeft ()), + mapToGlobal (ret.bottomRight ())) + : ret); +} + + +// public +bool kpGrip::isDrawing () const +{ + return (m_startPoint != KP_INVALID_POINT); +} + + +// public +QString kpGrip::haventBegunDrawUserMessage () const +{ + return i18n ("Left drag the handle to resize the image."); +} + + +// public +QString kpGrip::userMessage () const +{ + return m_userMessage; +} + +// public +void kpGrip::setUserMessage (const QString &message) +{ + // Don't do NOP checking here since another grip might have changed + // the message so an apparent NOP for this grip is not a NOP in the + // global sense (kpViewScrollableContainer::slotGripStatusMessageChanged()). + + m_userMessage = message; + emit statusMessageChanged (message); +} + + +// protected +void kpGrip::updatePixmap () +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpGrip::updatePixmap() rect=" << rect () << endl; +#endif + if (width () <= 0 || height () <= 0) + return; + + QPixmap pixmap (width (), height ()); + pixmap.fill (colorGroup ().highlight ()); + kpPixmapFX::ensureTransparentAt (&pixmap, pixmap.rect ()); + const QRect hr = hotRect (); +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "\thotRect=" << hr << endl; +#endif + if (hr.isValid ()) + kpPixmapFX::ensureOpaqueAt (&pixmap, hr); + + setBackgroundPixmap (pixmap); + if (pixmap.mask ()) + setMask (*pixmap.mask ()); +} + + +// protected +void kpGrip::cancel () +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpGrip::cancel()" << endl; +#endif + if (m_currentPoint == KP_INVALID_POINT) + return; + + m_startPoint = KP_INVALID_POINT; + m_currentPoint = KP_INVALID_POINT; + + setUserMessage (i18n ("Resize Image: Let go of all the mouse buttons.")); + setCursor (Qt::arrowCursor); + m_shouldReleaseMouseButtons = true; + + releaseKeyboard (); + emit cancelledDraw (); +} + + +// protected virtual [base QWidget] +void kpGrip::keyReleaseEvent (QKeyEvent *e) +{ + if (m_startPoint != KP_INVALID_POINT && + e->key () == Qt::Key_Escape) + { + cancel (); + } +} + +// protected virtual [base QWidget] +void kpGrip::mousePressEvent (QMouseEvent *e) +{ + if (m_startPoint == KP_INVALID_POINT && + (e->stateAfter () & Qt::MouseButtonMask) == Qt::LeftButton) + { + m_startPoint = e->pos (); + m_currentPoint = e->pos (); + emit beganDraw (); + grabKeyboard (); + + setUserMessage (i18n ("Resize Image: Right click to cancel.")); + setCursor (cursorForType (m_type)); + } + else + { + if (m_startPoint != KP_INVALID_POINT) + cancel (); + } +} + +// public +QPoint kpGrip::viewDeltaPoint () const +{ + if (m_startPoint == KP_INVALID_POINT) + return KP_INVALID_POINT; + + const QPoint point = mapFromGlobal (QCursor::pos ()); + + // TODO: this is getting out of sync with m_currentPoint + + return QPoint (((m_type & Right) ? point.x () - m_startPoint.x () : 0), + ((m_type & Bottom) ? point.y () - m_startPoint.y () : 0)); + +} + +// public +void kpGrip::mouseMovedTo (const QPoint &point, bool dueToDragScroll) +{ + if (m_startPoint == KP_INVALID_POINT) + return; + + m_currentPoint = point; + + emit continuedDraw (((m_type & Right) ? point.x () - m_startPoint.x () : 0), + ((m_type & Bottom) ? point.y () - m_startPoint.y () : 0), + dueToDragScroll); +} + +// protected virtual [base QWidget] +void kpGrip::mouseMoveEvent (QMouseEvent *e) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpGrip::mouseMoveEvent() m_startPoint=" << m_startPoint + << " stateAfter=" << e->stateAfter () + << endl; +#endif + + if (m_startPoint == KP_INVALID_POINT) + { + if ((e->stateAfter () & Qt::MouseButtonMask) == 0) + setUserMessage (haventBegunDrawUserMessage ()); + return; + } + + mouseMovedTo (e->pos (), false/*not due to drag scroll*/); +} + +// protected virtual [base QWidget] +void kpGrip::mouseReleaseEvent (QMouseEvent *e) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpGrip::mouseReleaseEvent() m_startPoint=" << m_startPoint + << " stateAfter=" << e->stateAfter () + << endl; +#endif + + if (m_startPoint != KP_INVALID_POINT) + { + const int dx = m_currentPoint.x () - m_startPoint.x (), + dy = m_currentPoint.y () - m_startPoint.y (); + + m_currentPoint = KP_INVALID_POINT; + m_startPoint = KP_INVALID_POINT; + + releaseKeyboard (); + emit endedDraw ((m_type & Right) ? dx : 0, + (m_type & Bottom) ? dy : 0); + } + + if ((e->stateAfter () & Qt::MouseButtonMask) == 0) + { + m_shouldReleaseMouseButtons = false; + setUserMessage (QString::null); + setCursor (cursorForType (m_type)); + + releaseKeyboard (); + emit releasedAllButtons (); + } +} + + +// protected virtual [base QWidget] +void kpGrip::resizeEvent (QResizeEvent *) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpGrip::resizeEvent()" << endl; +#endif + updatePixmap (); +} + + +// protected virtual [base QWidget] +void kpGrip::enterEvent (QEvent * /*e*/) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpGrip::enterEvent()" + << " m_startPoint=" << m_startPoint + << " shouldReleaseMouseButtons=" + << m_shouldReleaseMouseButtons << endl; +#endif + + if (m_startPoint == KP_INVALID_POINT && + !m_shouldReleaseMouseButtons) + { + #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "\tsending message" << endl; + #endif + setUserMessage (haventBegunDrawUserMessage ()); + } +} + +// protected virtual [base QWidget] +void kpGrip::leaveEvent (QEvent * /*e*/) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpGrip::leaveEvent()" + << " m_startPoint=" << m_startPoint + << " shouldReleaseMouseButtons=" + << m_shouldReleaseMouseButtons << endl; +#endif + if (m_startPoint == KP_INVALID_POINT && + !m_shouldReleaseMouseButtons) + { + setUserMessage (QString::null); + } +} + + +// protected virtual [base QWidget] +void kpGrip::paintEvent (QPaintEvent *e) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0 + kdDebug () << "kpGrip::paintEvent(" << e->rect () << ")" << endl; +#endif + QWidget::paintEvent (e); +} + + +// TODO: Are we checking for m_view == 0 often enough? +kpViewScrollableContainer::kpViewScrollableContainer (kpMainWindow *parent, + const char *name) + : QScrollView ((QWidget *) parent, name, Qt::WStaticContents | Qt::WNoAutoErase), + m_mainWindow (parent), + m_contentsXSoon (-1), m_contentsYSoon (-1), + m_view (0), + m_bottomGrip (new kpGrip (kpGrip::Bottom, viewport (), "Bottom Grip")), + m_rightGrip (new kpGrip (kpGrip::Right, viewport (), "Right Grip")), + m_bottomRightGrip (new kpGrip (kpGrip::BottomRight, viewport (), "BottomRight Grip")), + m_docResizingGrip (0), + m_dragScrollTimer (new QTimer (this)), + m_zoomLevel (100), + m_scrollTimerRunOnce (false), + m_resizeRoundedLastViewX (-1), m_resizeRoundedLastViewY (-1), + m_resizeRoundedLastViewDX (0), m_resizeRoundedLastViewDY (0), + m_haveMovedFromOriginalDocSize (false) + +{ + m_bottomGrip->setFixedHeight (GripSize); + m_bottomGrip->hide (); + addChild (m_bottomGrip); + connectGripSignals (m_bottomGrip); + + m_rightGrip->setFixedWidth (GripSize); + m_rightGrip->hide (); + addChild (m_rightGrip); + connectGripSignals (m_rightGrip); + + m_bottomRightGrip->setFixedSize (GripSize, GripSize); + m_bottomRightGrip->hide (); + addChild (m_bottomRightGrip); + connectGripSignals (m_bottomRightGrip); + + + connect (this, SIGNAL (contentsMoving (int, int)), + this, SLOT (slotContentsMoving (int, int))); + + connect (m_dragScrollTimer, SIGNAL (timeout ()), + this, SLOT (slotDragScroll ())); +} + +kpViewScrollableContainer::~kpViewScrollableContainer () +{ +} + + +// public +int kpViewScrollableContainer::contentsXSoon () +{ + if (m_contentsXSoon < 0) + return contentsX (); + else + return m_contentsXSoon; +} + +// public +int kpViewScrollableContainer::contentsYSoon () +{ + if (m_contentsYSoon < 0) + return contentsY (); + else + return m_contentsYSoon; +} + + +// protected +void kpViewScrollableContainer::connectGripSignals (kpGrip *grip) +{ + connect (grip, SIGNAL (beganDraw ()), + this, SLOT (slotGripBeganDraw ())); + connect (grip, SIGNAL (continuedDraw (int, int, bool)), + this, SLOT (slotGripContinuedDraw (int, int, bool))); + connect (grip, SIGNAL (cancelledDraw ()), + this, SLOT (slotGripCancelledDraw ())); + connect (grip, SIGNAL (endedDraw (int, int)), + this, SLOT (slotGripEndedDraw (int, int))); + + connect (grip, SIGNAL (statusMessageChanged (const QString &)), + this, SLOT (slotGripStatusMessageChanged (const QString &))); + + connect (grip, SIGNAL (releasedAllButtons ()), + this, SLOT (recalculateStatusMessage ())); +} + + +// public +QSize kpViewScrollableContainer::newDocSize () const +{ + return newDocSize (m_resizeRoundedLastViewDX, + m_resizeRoundedLastViewDY); +} + +// public +bool kpViewScrollableContainer::haveMovedFromOriginalDocSize () const +{ + return m_haveMovedFromOriginalDocSize; +} + +// public +QString kpViewScrollableContainer::statusMessage () const +{ + return m_gripStatusMessage; +} + +// public +void kpViewScrollableContainer::clearStatusMessage () +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1 + kdDebug () << "kpViewScrollableContainer::clearStatusMessage()" << endl; +#endif + m_bottomRightGrip->setUserMessage (QString::null); + m_bottomGrip->setUserMessage (QString::null); + m_rightGrip->setUserMessage (QString::null); +} + + +// protected +QSize kpViewScrollableContainer::newDocSize (int viewDX, int viewDY) const +{ + if (!m_view) + return QSize (); + + if (!docResizingGrip ()) + return QSize (); + + const int docX = (int) m_view->transformViewToDocX (m_view->width () + viewDX); + const int docY = (int) m_view->transformViewToDocY (m_view->height () + viewDY); + + return QSize (QMAX (1, docX), QMAX (1, docY)); +} + + +// protected +void kpViewScrollableContainer::calculateDocResizingGrip () +{ + if (m_bottomRightGrip->isDrawing ()) + m_docResizingGrip = m_bottomRightGrip; + else if (m_bottomGrip->isDrawing ()) + m_docResizingGrip = m_bottomGrip; + else if (m_rightGrip->isDrawing ()) + m_docResizingGrip = m_rightGrip; + else + m_docResizingGrip = 0; +} + +// protected +kpGrip *kpViewScrollableContainer::docResizingGrip () const +{ + return m_docResizingGrip; +} + + +// protected +int kpViewScrollableContainer::bottomResizeLineWidth () const +{ + if (!docResizingGrip ()) + return -1; + + if (!m_view) + return -1; + + if (docResizingGrip ()->type () & kpGrip::Bottom) + return QMAX (m_view->zoomLevelY () / 100, 1); + else + return 1; +} + +// protected +int kpViewScrollableContainer::rightResizeLineWidth () const +{ + if (!docResizingGrip ()) + return -1; + + if (!m_view) + return -1; + + if (docResizingGrip ()->type () & kpGrip::Right) + return QMAX (m_view->zoomLevelX () / 100, 1); + else + return 1; +} + + +// protected +QRect kpViewScrollableContainer::bottomResizeLineRect () const +{ + if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0) + return QRect (); + + return QRect (QPoint (0, + m_resizeRoundedLastViewY), + QPoint (m_resizeRoundedLastViewX - 1, + m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1)); +} + +// protected +QRect kpViewScrollableContainer::rightResizeLineRect () const +{ + if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0) + return QRect (); + + return QRect (QPoint (m_resizeRoundedLastViewX, + 0), + QPoint (m_resizeRoundedLastViewX + rightResizeLineWidth () - 1, + m_resizeRoundedLastViewY - 1)); +} + +// protected +QRect kpViewScrollableContainer::bottomRightResizeLineRect () const +{ + if (m_resizeRoundedLastViewX < 0 || m_resizeRoundedLastViewY < 0) + return QRect (); + + return QRect (QPoint (m_resizeRoundedLastViewX, + m_resizeRoundedLastViewY), + QPoint (m_resizeRoundedLastViewX + rightResizeLineWidth () - 1, + m_resizeRoundedLastViewY + bottomResizeLineWidth () - 1)); +} + + +// TODO: are these 2 correct? Remember that viewport()->x() == 1, viewport()->y() == 1 + +// protected +QPoint kpViewScrollableContainer::mapViewToViewport (const QPoint &viewPoint) +{ + return viewPoint - QPoint (contentsX (), contentsY ()); +} + +// protected +QRect kpViewScrollableContainer::mapViewToViewport (const QRect &viewRect) +{ + if (!viewRect.isValid ()) + return QRect (); + + QRect ret = viewRect; + ret.moveBy (-contentsX (), -contentsY ()); + return ret; +} + + +// protected +QRect kpViewScrollableContainer::mapViewportToGlobal (const QRect &viewportRect) +{ + return kpWidgetMapper::toGlobal (viewport (), viewportRect); +} + +// protected +QRect kpViewScrollableContainer::mapViewToGlobal (const QRect &viewRect) +{ + return mapViewportToGlobal (mapViewToViewport (viewRect)); +} + + +// protected +void kpViewScrollableContainer::repaintWidgetAtResizeLineViewRect ( + QWidget *widget, const QRect &resizeLineViewRect) +{ + const QRect resizeLineGlobalRect = mapViewToGlobal (resizeLineViewRect); + const QRect widgetGlobalRect = kpWidgetMapper::toGlobal (widget, + widget->rect ()); + + const QRect redrawGlobalRect = + resizeLineGlobalRect.intersect (widgetGlobalRect); + + const QRect redrawWidgetRect = + kpWidgetMapper::fromGlobal (widget, redrawGlobalRect); + + + if (redrawWidgetRect.isValid ()) + { + // TODO: should be "!widget->testWFlags (Qt::WRepaintNoErase)" + // but for some reason, doesn't work for viewport(). + const bool erase = !dynamic_cast <kpView *> (widget); + widget->repaint (redrawWidgetRect, erase); + } +} + +// protected +void kpViewScrollableContainer::repaintWidgetAtResizeLines (QWidget *widget) +{ + repaintWidgetAtResizeLineViewRect (widget, rightResizeLineRect ()); + repaintWidgetAtResizeLineViewRect (widget, bottomResizeLineRect ()); + repaintWidgetAtResizeLineViewRect (widget, bottomRightResizeLineRect ()); +} + +// protected +void kpViewScrollableContainer::eraseResizeLines () +{ + if (m_resizeRoundedLastViewX >= 0 && m_resizeRoundedLastViewY >= 0) + { + repaintWidgetAtResizeLines (viewport ()); + repaintWidgetAtResizeLines (m_view); + + repaintWidgetAtResizeLines (m_bottomGrip); + repaintWidgetAtResizeLines (m_rightGrip); + repaintWidgetAtResizeLines (m_bottomRightGrip); + } +} + + +// protected +void kpViewScrollableContainer::drawResizeLines () +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0 + kdDebug () << "kpViewScrollableContainer::drawResizeLines()" + << " lastViewX=" << m_resizeRoundedLastViewX + << " lastViewY=" << m_resizeRoundedLastViewY + << endl; +#endif + + + QPainter p (viewport (), true/*unclipped*/); + p.setRasterOp (Qt::NotROP); + + const QRect rightRect = rightResizeLineRect (); + if (rightRect.isValid ()) + p.fillRect (mapViewToViewport (rightRect), Qt::white); + + const QRect bottomRect = bottomResizeLineRect (); + if (bottomRect.isValid ()) + p.fillRect (mapViewToViewport (bottomRect), Qt::white); + + const QRect bottomRightRect = bottomRightResizeLineRect (); + if (bottomRightRect.isValid ()) + p.fillRect (mapViewToViewport (bottomRightRect), Qt::white); + + p.end (); +} + + +// protected +void kpViewScrollableContainer::updateResizeLines (int viewX, int viewY, + int viewDX, int viewDY) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0 + kdDebug () << "kpViewScrollableContainer::updateResizeLines(" + << viewX << "," << viewY << ")" + << " oldViewX=" << m_resizeRoundedLastViewX + << " oldViewY=" << m_resizeRoundedLastViewY + << " viewDX=" << viewDX + << " viewDY=" << viewDY + << endl; +#endif + + eraseResizeLines (); + + + if (viewX >= 0 && viewY >= 0) + { + m_resizeRoundedLastViewX = (int) m_view->transformDocToViewX ((int) m_view->transformViewToDocX (viewX)); + m_resizeRoundedLastViewY = (int) m_view->transformDocToViewY ((int) m_view->transformViewToDocY (viewY)); + + m_resizeRoundedLastViewDX = viewDX; + m_resizeRoundedLastViewDY = viewDY; + } + else + { + m_resizeRoundedLastViewX = -1; + m_resizeRoundedLastViewY = -1; + + m_resizeRoundedLastViewDX = 0; + m_resizeRoundedLastViewDY = 0; + } + + // TODO: This is suboptimal since if another window pops up on top of + // KolourPaint then disappears, the lines are not redrawn + // (although this doesn't happen very frequently since we grab the + // keyboard and mouse when resizing): + // + // e.g. sleep 5 && gedit & sleep 10 && killall gedit + // + // Should be done in the paintEvent's of every child of the + // scrollview. + drawResizeLines (); +} + + +// protected slot +void kpViewScrollableContainer::slotGripBeganDraw () +{ + if (!m_view) + return; + + calculateDocResizingGrip (); + + m_haveMovedFromOriginalDocSize = false; + + updateResizeLines (m_view->width (), m_view->height (), + 0/*viewDX*/, 0/*viewDY*/); + + emit beganDocResize (); +} + +// protected slot +void kpViewScrollableContainer::slotGripContinuedDraw (int inViewDX, int inViewDY, + bool dueToDragScroll) +{ + int viewDX = inViewDX, + viewDY = inViewDY; + +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::slotGripContinuedDraw(" + << viewDX << "," << viewDY << ") size=" + << newDocSize (viewDX, viewDY) + << " dueToDragScroll=" << dueToDragScroll + << endl; +#endif + + if (!m_view) + return; + + if (!dueToDragScroll && + beginDragScroll (QPoint (), QPoint (), m_view->zoomLevelX ())) + { + const QPoint newViewDeltaPoint = docResizingGrip ()->viewDeltaPoint (); + viewDX = newViewDeltaPoint.x (); + viewDY = newViewDeltaPoint.y (); + #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "\tdrag scrolled - new view delta point=" + << newViewDeltaPoint + << endl; + #endif + } + + m_haveMovedFromOriginalDocSize = true; + + updateResizeLines (QMAX (1, QMAX (m_view->width () + viewDX, (int) m_view->transformDocToViewX (1))), + QMAX (1, QMAX (m_view->height () + viewDY, (int) m_view->transformDocToViewY (1))), + viewDX, viewDY); + + emit continuedDocResize (newDocSize ()); +} + +// protected slot +void kpViewScrollableContainer::slotGripCancelledDraw () +{ + m_haveMovedFromOriginalDocSize = false; + + updateResizeLines (-1, -1, 0, 0); + + calculateDocResizingGrip (); + + emit cancelledDocResize (); + + endDragScroll (); +} + +// protected slot +void kpViewScrollableContainer::slotGripEndedDraw (int viewDX, int viewDY) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::slotGripEndedDraw(" + << viewDX << "," << viewDY << ") size=" + << newDocSize (viewDX, viewDY) + << endl; +#endif + + if (!m_view) + return; + + const QSize newSize = newDocSize (viewDX, viewDY); + + m_haveMovedFromOriginalDocSize = false; + + // must erase lines before view size changes + updateResizeLines (-1, -1, 0, 0); + + calculateDocResizingGrip (); + + emit endedDocResize (newSize); + + endDragScroll (); +} + + +// protected slot +void kpViewScrollableContainer::slotGripStatusMessageChanged (const QString &string) +{ + if (string == m_gripStatusMessage) + return; + + m_gripStatusMessage = string; + emit statusMessageChanged (string); +} + + +// public slot +void kpViewScrollableContainer::recalculateStatusMessage () +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollabelContainer::recalculateStatusMessage()" << endl; + kdDebug () << "\tQCursor::pos=" << QCursor::pos () + << " global visibleRect=" + << kpWidgetMapper::toGlobal (this, + QRect (0, 0, visibleWidth (), visibleHeight ())) + << " brGrip.hotRect=" << m_bottomRightGrip->hotRect (true) + << " bGrip.hotRect=" << m_bottomGrip->hotRect (true) + << " rGrip.hotRect=" << m_rightGrip->hotRect (true) + << endl; +#endif + + // HACK: After dragging to a new size, handles move so that they are now + // under the mouse pointer but no mouseMoveEvent() is generated for + // any grip. This also handles the case of cancelling over any + // grip. + // + if (kpWidgetMapper::toGlobal (this, + QRect (0, 0, visibleWidth (), visibleHeight ())) + .contains (QCursor::pos ())) + { + if (m_bottomRightGrip->isShown () && + m_bottomRightGrip->hotRect (true/*to global*/) + .contains (QCursor::pos ())) + { + m_bottomRightGrip->setUserMessage (i18n ("Left drag the handle to resize the image.")); + } + else if (m_bottomGrip->isShown () && + m_bottomGrip->hotRect (true/*to global*/) + .contains (QCursor::pos ())) + { + m_bottomGrip->setUserMessage (i18n ("Left drag the handle to resize the image.")); + } + else if (m_rightGrip->isShown () && + m_rightGrip->hotRect (true/*to global*/) + .contains (QCursor::pos ())) + { + m_rightGrip->setUserMessage (i18n ("Left drag the handle to resize the image.")); + } + else + { + clearStatusMessage (); + } + } + else + { + clearStatusMessage (); + } +} + + +// protected slot +void kpViewScrollableContainer::slotContentsMoving (int x, int y) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::slotContentsMoving(" + << x << "," << y << ")" + << " contentsX=" << contentsX () + << " contentsY=" << contentsY () << endl; +#endif + + m_contentsXSoon = x, m_contentsYSoon = y; + emit contentsMovingSoon (m_contentsXSoon, m_contentsYSoon); + + // Reduce flicker - don't let QScrollView scroll to-be-erased lines + eraseResizeLines (); + + QTimer::singleShot (0, this, SLOT (slotContentsMoved ())); +} + +// protected slot +void kpViewScrollableContainer::slotContentsMoved () +{ + m_contentsXSoon = m_contentsYSoon = -1; + + kpGrip *grip = docResizingGrip (); +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::slotContentsMoved()" + << " grip=" << grip + << " contentsX=" << contentsX () + << " contentsY=" << contentsY () << endl; +#endif + if (!grip) + return; + + grip->mouseMovedTo (grip->mapFromGlobal (QCursor::pos ()), + true/*moved due to drag scroll*/); +} + + +// protected +void kpViewScrollableContainer::disconnectViewSignals () +{ + disconnect (m_view, SIGNAL (sizeChanged (const QSize &)), + this, SLOT (updateGrips ())); + disconnect (m_view, SIGNAL (destroyed ()), + this, SLOT (slotViewDestroyed ())); +} + +// protected +void kpViewScrollableContainer::connectViewSignals () +{ + connect (m_view, SIGNAL (sizeChanged (const QSize &)), + this, SLOT (updateGrips ())); + connect (m_view, SIGNAL (destroyed ()), + this, SLOT (slotViewDestroyed ())); +} + + +// public virtual [base QScrollView] +void kpViewScrollableContainer::addChild (QWidget *widget, int x, int y) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::addChild(" << widget + << "," << x << "," << y << endl; +#endif + + QScrollView::addChild (widget, x, y); + + kpView *view = dynamic_cast <kpView *> (widget); +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "\tcast to kpView: " << view << endl; +#endif + if (view) + { + setView (view); + } +} + + +// public +kpView *kpViewScrollableContainer::view () const +{ + return m_view; +} + +// public +void kpViewScrollableContainer::setView (kpView *view) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::setView(" << view << ")" << endl; +#endif + + if (m_view == view) + return; + + if (m_view) + { + disconnectViewSignals (); + } + + m_view = view; + + updateGrips (); + + if (m_view) + { + connectViewSignals (); + } +} + + +// public slot +void kpViewScrollableContainer::updateGrips () +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::updateGrips() m_view=" + << m_view << endl; +#endif + + if (m_view) + { + m_bottomGrip->setFixedWidth (m_view->width ()); + moveChild (m_bottomGrip, 0, m_view->height ()); + + m_rightGrip->setFixedHeight (m_view->height ()); + moveChild (m_rightGrip, m_view->width (), 0); + + moveChild (m_bottomRightGrip, m_view->width (), m_view->height ()); + } + + m_bottomGrip->setShown (bool (m_view)); + m_rightGrip->setShown (bool (m_view)); + m_bottomRightGrip->setShown (bool (m_view)); + +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "\tcontentsRect=" << contentsRect () + << " visibleRect=" << visibleRect () + << " viewportRect=" << viewport ()->rect () + << endl; +#endif + + if (m_view) + { + resizeContents (m_view->width () + m_rightGrip->width (), + m_view->height () + m_bottomGrip->height ()); + } + else + { + resizeContents (0, 0); + } + + recalculateStatusMessage (); +} + +// protected slot +void kpViewScrollableContainer::slotViewDestroyed () +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::slotViewDestroyed() m_view=" + << m_view << endl; +#endif + + m_view = 0; + updateGrips (); +} + + +// public slot +bool kpViewScrollableContainer::beginDragScroll (const QPoint &/*docPoint*/, + const QPoint &/*lastDocPoint*/, + int zoomLevel, + bool *didSomething) +{ + if (didSomething) + *didSomething = false; + + m_zoomLevel = zoomLevel; + + const QPoint p = mapFromGlobal (QCursor::pos ()); + +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::beginDragScroll() p=" << p + << " dragScrollTimerRunOnce=" << m_scrollTimerRunOnce + << endl; +#endif + + bool stopDragScroll = true; + bool scrolled = false; + + if (!noDragScrollRect ().contains (p)) + { + if (m_dragScrollTimer->isActive ()) + { + if (m_scrollTimerRunOnce) + { + scrolled = slotDragScroll (); + } + } + else + { + m_scrollTimerRunOnce = false; + m_dragScrollTimer->start (DragScrollInitialInterval); + } + + stopDragScroll = false; + } + + if (stopDragScroll) + m_dragScrollTimer->stop (); + + if (didSomething) + *didSomething = scrolled; + + return scrolled; +} + +// public slot +bool kpViewScrollableContainer::beginDragScroll (const QPoint &docPoint, + const QPoint &lastDocPoint, + int zoomLevel) +{ + return beginDragScroll (docPoint, lastDocPoint, zoomLevel, + 0/*don't want scrolled notification*/); +} + + +// public slot +bool kpViewScrollableContainer::endDragScroll () +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::endDragScroll()" << endl; +#endif + + if (m_dragScrollTimer->isActive ()) + { + m_dragScrollTimer->stop (); + return true; + } + else + { + return false; + } +} + + +static const int distanceFromRectToMultiplier (int dist) +{ + if (dist < 0) + return 0; + else if (dist < DragDistanceFromRectMaxFor1stMultiplier) + return 1; + else if (dist < DragDistanceFromRectMaxFor2ndMultiplier) + return 2; + else + return 4; +} + + +// protected slot +bool kpViewScrollableContainer::slotDragScroll (bool *didSomething) +{ + bool scrolled = false; + + if (didSomething) + *didSomething = false; + + + const QRect rect = noDragScrollRect (); + const QPoint pos = mapFromGlobal (QCursor::pos ()); + +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::slotDragScroll()" + << " noDragScrollRect=" << rect + << " pos=" << pos + << " contentsX=" << contentsX () + << " contentsY=" << contentsY () << endl; +#endif + + int dx = 0, dy = 0; + int dxMultiplier = 0, dyMultiplier = 0; + + if (pos.x () < rect.left ()) + { + dx = -DragScrollNumPixels; + dxMultiplier = distanceFromRectToMultiplier (rect.left () - pos.x ()); + } + else if (pos.x () > rect.right ()) + { + dx = +DragScrollNumPixels; + dxMultiplier = distanceFromRectToMultiplier (pos.x () - rect.right ()); + } + + if (pos.y () < rect.top ()) + { + dy = -DragScrollNumPixels; + dyMultiplier = distanceFromRectToMultiplier (rect.top () - pos.y ()); + } + else if (pos.y () > rect.bottom ()) + { + dy = +DragScrollNumPixels; + dyMultiplier = distanceFromRectToMultiplier (pos.y () - rect.bottom ()); + } + +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0 + kdDebug () << "kpViewScrollableContainer::slotDragScroll()" + << " dx=" << dx << " * " << dxMultiplier + << " dy=" << dy << " * " << dyMultiplier + << " zoomLevel=" << m_zoomLevel + << endl; +#endif + + dx *= dxMultiplier;// * QMAX (1, m_zoomLevel / 100); + dy *= dyMultiplier;// * QMAX (1, m_zoomLevel / 100); + + if (dx || dy) + { + const int oldContentsX = contentsX (), + oldContentsY = contentsY (); + + scrollBy (dx, dy); + + #if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 1 + kdDebug () << "\tafter scrollBy():" + << " contentsX=" << contentsX () + << " contentsY=" << contentsY () << endl; + #endif + + scrolled = (oldContentsX != contentsX () || + oldContentsY != contentsY ()); + + if (scrolled) + { + QRegion region = QRect (contentsX (), contentsY (), + visibleWidth (), visibleHeight ()); + region -= QRect (oldContentsX, oldContentsY, + visibleWidth (), visibleHeight ()); + + // Repaint newly exposed region immediately to reduce tearing + // of scrollView. + m_view->repaint (region, false/*no erase*/); + } + } + + + m_dragScrollTimer->changeInterval (DragScrollInterval); + m_scrollTimerRunOnce = true; + + + if (didSomething) + *didSomething = scrolled; + + return scrolled; +} + +// protected virtual [base QScrollView] +void kpViewScrollableContainer::contentsDragMoveEvent (QDragMoveEvent *e) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::contentsDragMoveEvent" + << e->pos () + << endl; +#endif + + QScrollView::contentsDragMoveEvent (e); +} + +// protected slot +bool kpViewScrollableContainer::slotDragScroll () +{ + return slotDragScroll (0/*don't want scrolled notification*/); +} + + +// protected virtual [base QScrollView] +void kpViewScrollableContainer::contentsMouseMoveEvent (QMouseEvent *e) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::contentsMouseMoveEvent" + << e->pos () + << endl; +#endif + + QScrollView::contentsMouseMoveEvent (e); +} + +// protected virtual [base QScrollView] +void kpViewScrollableContainer::mouseMoveEvent (QMouseEvent *e) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::mouseMoveEvent" + << e->pos () + << endl; +#endif + + QScrollView::mouseMoveEvent (e); +} + + +// protected virtual [base QScrollView] +void kpViewScrollableContainer::contentsWheelEvent (QWheelEvent *e) +{ + e->ignore (); + + if (m_view) + m_view->wheelEvent (e); + + if (!e->isAccepted ()) + QScrollView::contentsWheelEvent (e); +} + + +QRect kpViewScrollableContainer::noDragScrollRect () const +{ + return QRect (DragScrollLeftTopMargin, DragScrollLeftTopMargin, + width () - DragScrollLeftTopMargin - DragScrollRightBottomMargin, + height () - DragScrollLeftTopMargin - DragScrollRightBottomMargin); +} + +// protected virtual [base QScrollView] +bool kpViewScrollableContainer::eventFilter (QObject *watchedObject, QEvent *event) +{ + return QScrollView::eventFilter (watchedObject, event); +} + +// protected virtual [base QScrollView] +void kpViewScrollableContainer::viewportPaintEvent (QPaintEvent *e) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER + kdDebug () << "kpViewScrollableContainer::viewportPaintEvent(" + << e->rect () + << ")" << endl; +#endif + + QScrollView::viewportPaintEvent (e); +} + +// protected virtual [base QFrame] +void kpViewScrollableContainer::paintEvent (QPaintEvent *e) +{ +#if DEBUG_KP_VIEW_SCROLLABLE_CONTAINER && 0 + kdDebug () << "kpViewScrollableContainer::paintEvent(" + << e->rect () + << ")" << endl; +#endif + + QScrollView::paintEvent (e); +} + +// protected virtual [base QScrollView] +void kpViewScrollableContainer::resizeEvent (QResizeEvent *e) +{ + QScrollView::resizeEvent (e); + + emit resized (); +} + + +#include <kpviewscrollablecontainer.moc> |