File indexing completed on 2024-04-28 04:20:27

0001 
0002 /*
0003    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
0004    All rights reserved.
0005 
0006    Redistribution and use in source and binary forms, with or without
0007    modification, are permitted provided that the following conditions
0008    are met:
0009 
0010    1. Redistributions of source code must retain the above copyright
0011       notice, this list of conditions and the following disclaimer.
0012    2. Redistributions in binary form must reproduce the above copyright
0013       notice, this list of conditions and the following disclaimer in the
0014       documentation and/or other materials provided with the distribution.
0015 
0016    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0017    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0018    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0019    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0020    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0021    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0022    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0023    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0024    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0025    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0026 */
0027 
0028 //
0029 // Tool reaction to view mouse input.
0030 //
0031 
0032 #define DEBUG_KP_TOOL 0
0033 
0034 #include "tools/kpTool.h"
0035 #include "kpToolPrivate.h"
0036 
0037 #include "kpLogCategories.h"
0038 
0039 #include "environments/tools/kpToolEnvironment.h"
0040 #include "views/kpView.h"
0041 #include "views/manager/kpViewManager.h"
0042 
0043 #include <QMouseEvent>
0044 #include <QApplication>
0045 #include <QClipboard>
0046 
0047 //---------------------------------------------------------------------
0048 
0049 // HITODO: If you press a mouse button and move it out of the view _really_ fast
0050 //         and let go of the mouse button outside of the view, a mouseRelease
0051 //         event will not be generated, so the tool will still be in drawing mode
0052 //         (this is especially noticeable with the spraycan).
0053 //
0054 //         When you move the mouse back into the view, it will still continue
0055 //         continue drawing even though no mouse button is held down.
0056 //
0057 //         It is somewhat hard to reproduce so the best way is to position the
0058 //         mouse close to an edge of the view.  If you do it right, no mouseMoveEvent
0059 //         is generated at _all_, until you move it back into the view.
0060 void kpTool::mousePressEvent (QMouseEvent *e)
0061 {
0062 #if DEBUG_KP_TOOL && 1
0063     qCDebug(kpLogTools) << "kpTool::mousePressEvent pos=" << e->pos ()
0064                << " button=" << (int) e->button ()
0065                << " stateAfter: buttons=" << (int *) (int) e->buttons ()
0066                << " modifiers=" << (int *) (int) e->modifiers ()
0067                << " beganDraw=" << d->beganDraw << endl;
0068 #endif
0069 
0070     if (e->button () == Qt::MiddleButton)
0071     {
0072         const QString text = QApplication::clipboard ()->text (QClipboard::Selection);
0073     #if DEBUG_KP_TOOL && 1
0074         qCDebug(kpLogTools) << "\tMMB pasteText='" << text << "'";
0075     #endif
0076         if (!text.isEmpty ())
0077         {
0078             if (hasBegunShape ())
0079             {
0080             #if DEBUG_KP_TOOL && 1
0081                 qCDebug(kpLogTools) << "\t\thasBegunShape - end";
0082             #endif
0083                 endShapeInternal (d->currentPoint, normalizedRect ());
0084             }
0085 
0086             if (viewUnderCursor ())
0087             {
0088                 d->environ->pasteTextAt (text,
0089                     viewUnderCursor ()->transformViewToDoc (e->pos ()),
0090                     true/*adjust topLeft so that cursor isn't
0091                           on top of resize handle*/);
0092             }
0093 
0094             return;
0095         }
0096     }
0097 
0098     int mb = mouseButton (e->buttons ());
0099 #if DEBUG_KP_TOOL && 1
0100     qCDebug(kpLogTools) << "\tmb=" << mb << " d->beganDraw=" << d->beganDraw;
0101 #endif
0102 
0103     if (mb == -1 && !d->beganDraw)
0104     {
0105         // Ignore mouse press.
0106         return;
0107     }
0108 
0109     if (d->beganDraw)
0110     {
0111         if (mb == -1 || mb != d->mouseButton)
0112         {
0113         #if DEBUG_KP_TOOL && 1
0114             qCDebug(kpLogTools) << "\tCancelling operation as " << mb << " == -1 or != " << d->mouseButton;
0115         #endif
0116 
0117             kpView *view = viewUnderStartPoint ();
0118             Q_ASSERT (view);
0119 
0120             // if we get a mousePressEvent when we're drawing, then the other
0121             // mouse button must have been pressed
0122             d->currentPoint = view->transformViewToDoc (e->pos ());
0123             d->currentViewPoint = e->pos ();
0124             cancelShapeInternal ();
0125         }
0126 
0127         return;
0128     }
0129 
0130     kpView *view = viewUnderCursor ();
0131     Q_ASSERT (view);
0132 
0133 #if DEBUG_KP_TOOL && 1
0134     if (view) {
0135         qCDebug(kpLogTools) << "\tview=" << view->objectName ();
0136     }
0137 #endif
0138 
0139     // let user know what mouse button is being used for entire draw
0140     d->mouseButton = mouseButton (e->buttons ());
0141     d->shiftPressed = (e->modifiers () & Qt::ShiftModifier);
0142     d->controlPressed = (e->modifiers () & Qt::ControlModifier);
0143     d->altPressed = (e->modifiers () & Qt::AltModifier);
0144     d->startPoint = d->currentPoint = view->transformViewToDoc (e->pos ());
0145     d->currentViewPoint = e->pos ();
0146     d->viewUnderStartPoint = view;
0147     d->lastPoint = QPoint (-1, -1);
0148 
0149 #if DEBUG_KP_TOOL && 1
0150     qCDebug(kpLogTools) << "\tBeginning draw @ " << d->currentPoint;
0151 #endif
0152 
0153     beginDrawInternal ();
0154 
0155     draw (d->currentPoint, d->lastPoint, QRect (d->currentPoint, d->currentPoint));
0156     d->lastPoint = d->currentPoint;
0157 }
0158 
0159 //---------------------------------------------------------------------
0160 
0161 // OPT: If the mouse is moving in terms of view pixels, it still might
0162 //      not be moving in terms of document pixels (when zoomed in).
0163 //
0164 //      So we should detect this and not call draw() or hover().
0165 //
0166 //      However, kpToolSelection needs hover() to be called on all view
0167 //      point changes, not just document points, since the selection resize
0168 //      handles may be smaller than document points.  Also, I wonder if
0169 //      selections' accidental drag detection feature cares?
0170 void kpTool::mouseMoveEvent (QMouseEvent *e)
0171 {
0172 #if DEBUG_KP_TOOL && 0
0173     qCDebug(kpLogTools) << "kpTool::mouseMoveEvent pos=" << e->pos ()
0174                << " stateAfter: buttons=" << (int *) (int) e->buttons ()
0175                << " modifiers=" << (int *) (int) e->modifiers ();
0176     kpView *v0 = viewUnderCursor (),
0177            *v1 = viewManager ()->viewUnderCursor (true/*use Qt*/),
0178            *v2 = viewUnderStartPoint ();
0179     qCDebug(kpLogTools) << "\tviewUnderCursor=" << (v0 ? v0->objectName () : "(none)")
0180                << " viewUnderCursorQt=" << (v1 ? v1->objectName () : "(none)")
0181                << " viewUnderStartPoint=" << (v2 ? v2->objectName () : "(none)");
0182     qCDebug(kpLogTools) << "\tfocusWidget=" << kapp->focusWidget ();
0183     qCDebug(kpLogTools) << "\tbeganDraw=" << d->beganDraw;
0184 #endif
0185 
0186     d->shiftPressed = (e->modifiers () & Qt::ShiftModifier);
0187     d->controlPressed = (e->modifiers () & Qt::ControlModifier);
0188     d->altPressed = (e->modifiers () & Qt::AltModifier);
0189 
0190     if (d->beganDraw)
0191     {
0192         kpView *view = viewUnderStartPoint ();
0193         Q_ASSERT (view);
0194 
0195         d->currentPoint = view->transformViewToDoc (e->pos ());
0196         d->currentViewPoint = e->pos ();
0197 
0198     #if DEBUG_KP_TOOL && 0
0199         qCDebug(kpLogTools) << "\tDraw!";
0200     #endif
0201 
0202         bool dragScrolled = false;
0203         movedAndAboutToDraw (d->currentPoint, d->lastPoint, view->zoomLevelX (), &dragScrolled);
0204 
0205         if (dragScrolled)
0206         {
0207             d->currentPoint = calculateCurrentPoint ();
0208             d->currentViewPoint = calculateCurrentPoint (false/*view point*/);
0209 
0210             // Scrollview has scrolled contents and has scheduled an update
0211             // for the newly exposed region.  If draw() schedules an update
0212             // as well (instead of immediately updating), the scrollview's
0213             // update will be executed first and it'll only update part of
0214             // the screen resulting in ugly tearing of the viewManager's
0215             // tempImage.
0216             viewManager ()->setFastUpdates ();
0217         }
0218 
0219         drawInternal ();
0220 
0221         if (dragScrolled) {
0222             viewManager ()->restoreFastUpdates ();
0223         }
0224 
0225         d->lastPoint = d->currentPoint;
0226     }
0227     else
0228     {
0229         kpView *view = viewUnderCursor ();
0230         if (!view)  // possible if cancelShape()'ed but still holding down initial mousebtn
0231         {
0232             d->currentPoint = KP_INVALID_POINT;
0233             d->currentViewPoint = KP_INVALID_POINT;
0234             return;
0235         }
0236 
0237         d->currentPoint = view->transformViewToDoc (e->pos ());
0238         d->currentViewPoint = e->pos ();
0239         hover (d->currentPoint);
0240     }
0241 }
0242 
0243 //---------------------------------------------------------------------
0244 
0245 void kpTool::mouseReleaseEvent (QMouseEvent *e)
0246 {
0247 #if DEBUG_KP_TOOL && 1
0248     qCDebug(kpLogTools) << "kpTool::mouseReleaseEvent pos=" << e->pos ()
0249                << " button=" << (int) e->button ()
0250                << " stateAfter: buttons=" << (int *) (int) e->buttons ()
0251                << " modifiers=" << (int *) (int) e->modifiers ()
0252                << " beganDraw=" << d->beganDraw;
0253 #endif
0254 
0255     // Have _not_ already cancelShape()'ed by pressing other mouse button?
0256     // (e.g. you can cancel a line dragged out with the LMB, by pressing
0257     //       the RMB)
0258     if (d->beganDraw)
0259     {
0260         kpView *view = viewUnderStartPoint ();
0261         Q_ASSERT (view);
0262 
0263         d->currentPoint = view->transformViewToDoc (e->pos ());
0264         d->currentViewPoint = e->pos ();
0265 
0266         drawInternal ();
0267 
0268         endDrawInternal (d->currentPoint, normalizedRect ());
0269     }
0270 
0271     if ((e->buttons () & Qt::MouseButtonMask) == 0)
0272     {
0273         releasedAllButtons ();
0274     }
0275 }
0276 
0277 //---------------------------------------------------------------------
0278 
0279 void kpTool::wheelEvent (QWheelEvent *e)
0280 {
0281 #if DEBUG_KP_TOOL
0282     qCDebug(kpLogTools) << "kpTool::wheelEvent() modifiers=" << (int *) (int) e->modifiers ()
0283                << " hasBegunDraw=" << hasBegunDraw ()
0284                << " delta=" << e->delta ();
0285 #endif
0286 
0287     e->ignore ();
0288 
0289     // If CTRL not pressed, bye.
0290     if ((e->modifiers () & Qt::ControlModifier) == 0)
0291     {
0292     #if DEBUG_KP_TOOL
0293         qCDebug(kpLogTools) << "\tno CTRL -> bye";
0294     #endif
0295         return;
0296     }
0297 
0298     // If drawing, bye; don't care if a shape in progress though.
0299     if (hasBegunDraw ())
0300     {
0301     #if DEBUG_KP_TOOL
0302         qCDebug(kpLogTools) << "\thasBegunDraw() -> bye";
0303     #endif
0304         return;
0305     }
0306 
0307 
0308     // Zoom in/out depending on wheel direction.
0309 
0310     // Moved wheel away from user?
0311     if (e->angleDelta().y() > 0)
0312     {
0313     #if DEBUG_KP_TOOL
0314         qCDebug(kpLogTools) << "\tzoom in";
0315     #endif
0316         d->environ->zoomIn (true/*center under cursor*/);
0317         e->accept ();
0318     }
0319     // Moved wheel towards user?
0320     else if (e->angleDelta().y() < 0)
0321     {
0322     #if DEBUG_KP_TOOL
0323         qCDebug(kpLogTools) << "\tzoom out";
0324     #endif
0325     #if 1
0326         d->environ->zoomOut (true/*center under cursor - make zoom in/out
0327                                    stay under same doc pos*/);
0328     #else
0329         d->environ->zoomOut (false/*don't center under cursor - as is
0330                                     confusing behaviour when zooming
0331                                     out*/);
0332     #endif
0333         e->accept ();
0334     }
0335 }
0336 
0337 //---------------------------------------------------------------------