File indexing completed on 2024-05-19 04:23:06

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 #ifndef kpAbstractSelectionTool_H
0030 #define kpAbstractSelectionTool_H
0031 
0032 
0033 #include "tools/kpTool.h"
0034 
0035 #include <QVariant>
0036 
0037 
0038 class QKeyEvent;
0039 class QPoint;
0040 class QRect;
0041 
0042 class kpAbstractSelection;
0043 class kpAbstractSelectionContentCommand;
0044 class kpCommand;
0045 class kpToolSelectionEnvironment;
0046 
0047 
0048 // The abstract base for all selection tools.
0049 //
0050 //
0051 // This provides methods to:
0052 //
0053 //   1. handle selection commands and the command history.
0054 //
0055 //   2. implement the kpTool drawing methods.
0056 //
0057 //   3. implement the "Create", "Move" and "Resize/Scale" selection
0058 //      draw types.
0059 //
0060 // "Drags" that consist of a single click generally have no effect in
0061 // order to prevent accidentally creating a 1x1 selection, doing a NOP move
0062 // or doing a NOP resize/scale.  However, doing a bigger drag and then
0063 // dragging the mouse back to create a 1x1 selection or the NOP
0064 // effects are allowed and are recorded in the command history.
0065 //
0066 // Additionally, the "Create" draw type is fitted with "accidental drag
0067 // detection" which will not create a selection in response in small and
0068 // quick drags.
0069 //
0070 //
0071 // The internal architecture is as follows and is of interest for subclasses
0072 // and for changing the existing implementation of the above selection
0073 // draw types:
0074 //
0075 // beginDraw() initiates the action by determining the current draw type
0076 // by calling the virtual calculateDrawType().  Later, all of this class'
0077 // implementations of kpTool drawing methods (e.g. beginDraw(), draw(),
0078 // endShape() etc.) call the virtual operation(), which is passed:
0079 //
0080 //   1. the current draw type (e.g. "Create" or "Move")
0081 //
0082 //   2. the operation, corresponding to the calling method (e.g.
0083 //      "BeginDraw" of called by beginDraw()).
0084 //
0085 // [Note: the documentation in these source files sometimes uses "operation"
0086 //        where "draw type" is meant and vice-versa]
0087 //
0088 // Note that these kpTool drawing methods do some other work before and after
0089 // calling operation().
0090 //
0091 // The default implementation of operation() is to dispatch the operation
0092 // to draw-type-specific methods e.g. createOperation() handles all
0093 // operations for the "Create" draw type.  createOperation() will then
0094 // call the method that corresponds to the operation e.g. beginDrawCreate()
0095 // corresponds to the "BeginDraw" operation.
0096 //
0097 // For each draw type, all methods are grouped in a single source file
0098 // e.g. kpAbstractSelectionTool_Create.cpp.
0099 //
0100 // To introduce a custom draw type, not implemented by code in this
0101 // class (e.g. "SelectText"), you must:
0102 //
0103 //   1. Add it to "enum DrawType" below.
0104 //
0105 //   2. Override calculateDrawType() to determine the situations in which
0106 //      the new draw type is active.
0107 //
0108 //   3. Override operation() to catch all situations in which the new draw
0109 //      type is being used, and to implement the appropriate logic.
0110 //
0111 class kpAbstractSelectionTool : public kpTool
0112 {
0113 Q_OBJECT
0114 
0115 public:
0116     kpAbstractSelectionTool (const QString &text, const QString &description,
0117         int key,
0118         kpToolSelectionEnvironment *environ, QObject *parent,
0119         const QString &name);
0120 
0121     ~kpAbstractSelectionTool () override;
0122 
0123 
0124     // Inform kpTool to call draw() when CTRL, SHIFT and friends are
0125     // pressed.  CTRL is used for copying, instead of moving, the
0126     // selection.  SHIFT is used for sweeping.
0127     bool careAboutModifierState () const override { return true; }
0128 
0129 
0130 //
0131 // Drawing - Subclass Accessors
0132 //
0133 
0134 protected:
0135     friend struct kpAbstractSelectionToolPrivate;
0136     enum DrawType
0137     {
0138         None, Create, Move, SelectText, ResizeScale
0139     };
0140 
0141 
0142     // The return value is not "None" during a drawing operation.
0143     //
0144     // The returned value is set by beginDraw(), after being determined
0145     // by calculateDrawType().  It is cleared in cancelShape() and endDraw().
0146     DrawType drawType () const;
0147 
0148     bool hadSelectionBeforeDraw () const;
0149 
0150 
0151 //
0152 // Drawing
0153 //
0154 
0155 protected:
0156     // (overrides non-virtual method in kpTool)
0157     kpToolSelectionEnvironment *environ () const;
0158 
0159     // Returns whether a CTRL or SHIFT key is currently pressed.
0160     // Convenience method.
0161     bool controlOrShiftPressed () const;
0162 
0163 
0164 protected:
0165     // Deselects the current selection:
0166     //
0167     //   1. If it has no content, it is simply deleted.
0168     //   2. If it has content, it pushes it onto the document, adding the
0169     //      necessary commands to the command history.
0170     //
0171     // ASSUMPTIONS:
0172     //   1. There is a current selection.
0173     //   2. You have not called giveContentIfNeeded() nor
0174     //      addNeedingContentCommand() on the current selection.
0175     void pushOntoDocument ();
0176 
0177 
0178 //
0179 // The command lifecycle is as follows:
0180 //
0181 // 1. Ensure that the document has a selection, with or without content.
0182 //
0183 // 2. Call giveContentIfNeeded().
0184 //
0185 // 3. Create the command.
0186 //
0187 // 4. Process user input, mutate the selection directly and update the
0188 //    command with the user's input.
0189 //
0190 // 5. When the drawing operation is complete, call addNeedingContentCommand()
0191 //    with the command created in Step 3.
0192 //
0193 protected:
0194     // Returns a new instance of the give-the-selection-content command
0195     // that matches the current selection type.  The command should not be
0196     // executed by this method.
0197     virtual kpAbstractSelectionContentCommand *newGiveContentCommand () const = 0;
0198 
0199     // Before changing a selection (e.g. moving or resizing), you must
0200     // ensure that it has content.  Call this method to ensure that.
0201     //
0202     // If the selection has no content, this calls newGiveContentCommand()
0203     // and executes it.  If the selection already has content, this does
0204     // nothing.
0205     //
0206     // ASSUMPTION: There is a selection.
0207     void giveContentIfNeeded ();
0208 
0209     // The name that should be given to command that is constructed in
0210     // response to a drag that creates a new selection.
0211     virtual QString nameOfCreateCommand () const = 0;
0212 
0213     // Add a command to the command history.
0214     // The command is not executed.
0215     //
0216     // If the prior call to giveContentIfNeeded() created content, this
0217     // will, in line with KolourPaint selection convention:
0218     //
0219     //   1. Adds a selection border creation command (this is a bit clever
0220     //      and may overwrite the last "Undo" command instead -- see
0221     //      kpCommandHistory::addCreateSelectionCommand()).
0222     //
0223     //   2. Group the content command created by giveContentIfNeeded()
0224     //      with <cmd>, as a kpMacroCommand also named <cmd>.
0225     //
0226     // ASSUMPTION: giveContentIfNeeded() must have been called before
0227     //             creating <cmd>.
0228     void addNeedingContentCommand (kpCommand *cmd);
0229 
0230 
0231 protected:
0232     // Sets the selection border mode when no drawing operation is active.
0233     //
0234     // Subclasses may wish to reimplement but should still call the base
0235     // implementation.  Reimplementations should wrap the whole
0236     // reimplementation in a kpViewManager::setQueueUpdates() block.
0237     virtual void setSelectionBorderForHaventBegunDraw ();
0238 private:
0239     // Returns the statusbar message from when no draw operation is in
0240     // progress.  Calls operation() with "HaventBegunDrawUserMessage".
0241     //
0242     // (not const due to purely syntactic issue: it calls the non-const
0243     //  operation(); it really acts like a const method though)
0244     QString haventBegunDrawUserMessage ();
0245 
0246 
0247 public:
0248     void begin () override;
0249     void end () override;
0250 
0251 
0252 public:
0253     void reselect () override;
0254 
0255 
0256 //
0257 // Drawing - Beginning a Drag
0258 //
0259 
0260 protected:
0261     // Called by calculateDrawType() to determine what type of draw type
0262     // is being started in response to a drag inside the bounding rectangle of
0263     // a selection.
0264     //
0265     // This implementation returns "Move".
0266     //
0267     // You are free to reimplement this and may choose to call this base
0268     // implementation or not.
0269     virtual DrawType calculateDrawTypeInsideSelection () const;
0270 
0271     // Called by beginDraw() to determine what type of draw type is
0272     // being started.  The returned draw type is passed to operation().
0273     //
0274     // This implementation behaves according to the first rule that matches:
0275     //
0276     // 1. If the cursor is on top of a selection resize handle and no modifiers
0277     //    are held (i.e. not a smearing move draw type), it returns "ResizeScale".
0278     //
0279     // 2. If the cursor is inside the bounding rectangle of a selection, it
0280     //    calls calculateDrawTypeInsideSelection().
0281     //
0282     // 3. Otherwise, it returns "Create".
0283     //
0284     // You are free to reimplement this and may choose to call this base
0285     // implementation or not.  Reimplementing allows you to support new
0286     // draw types for different types of selections (e.g. kpToolText
0287     // supports "SelectText").  It also allows you to make certain
0288     // drags (e.g. dragging in the middle of a selection) do nothing by
0289     // returning "None" instead of calling the base implementation.
0290     virtual DrawType calculateDrawType () const;
0291 public:
0292     void beginDraw () override;
0293 
0294 
0295 //
0296 // Drawing - Mouse Movement
0297 //
0298 
0299 public:
0300     void hover (const QPoint &point) override;
0301     void draw (const QPoint &thisPoint, const QPoint &lastPoint,
0302                        const QRect &normalizedRect) override;
0303 
0304 
0305 //
0306 // Drawing - Ending a Drag
0307 //
0308 
0309 public:
0310     void cancelShape () override;
0311     void releasedAllButtons () override;
0312 
0313 
0314 protected:
0315     // Displays the right-mouse-button-triggered selection menu, re-entering
0316     // the event loop and blocking until the menu closes.
0317     //
0318     // This menu is a subset of the main window's Edit and Selection menus.
0319     //
0320     // WARNING: This may cause a re-entry of view/tool event handlers.
0321     //          If you are calling this from a view/tool event handler,
0322     //          either make all your handlers re-entrant or do not put any
0323     //          code in your handler after the call.
0324     void popupRMBMenu ();
0325 public:
0326     void endDraw (const QPoint &thisPoint, const QRect &normalizedRect) override;
0327 
0328 
0329 //
0330 // Drawing - Operation Dispatching
0331 //
0332 
0333 protected:
0334     enum Operation
0335     {
0336         //
0337         // These may be called outside of a drawing operation where
0338         // drawType() will return None.
0339         //
0340 
0341         // Returns the message for the given draw type and operation.
0342         HaventBegunDrawUserMessage,
0343 
0344         SetCursor,
0345 
0346 
0347         //
0348         // Called to start, to end, or inside, a drawing operation.
0349         //
0350 
0351         BeginDraw, Draw, Cancel, EndDraw
0352     };
0353 
0354     // (See the class API Doc for a description).
0355     virtual QVariant operation (DrawType drawType, Operation op,
0356         const QVariant &data1 = QVariant (), const QVariant &data2 = QVariant ());
0357 
0358 
0359 //
0360 // Create
0361 //
0362 
0363 private:
0364     // Called by constructor to initialize the "Create" draw type.
0365     void initCreate ();
0366     // Called by destructor to uninitialize the "Create" draw type.
0367     void uninitCreate ();
0368 
0369 
0370 private:
0371     void beginCreate ();
0372     void endCreate ();
0373 
0374 
0375 protected:
0376     virtual QString haventBegunDrawUserMessageCreate () const = 0;
0377 private:
0378     void setCursorCreate ();
0379 
0380 
0381 protected:
0382     // Sets the selection border mode when beginning to drag to create a
0383     // selection.
0384     //
0385     // Subclasses may wish to reimplement but should still call the base
0386     // implementation.  Reimplementations should wrap the whole
0387     // reimplementation in a kpViewManager::setQueueUpdates() block.
0388     virtual void setSelectionBorderForBeginDrawCreate ();
0389 private:
0390     void beginDrawCreate ();
0391 
0392 
0393 protected:
0394     //
0395     // If the drag has already been substantial enough to be considered as a
0396     // non-NOP drag (<dragAccepted>), you must return "true".
0397     //
0398     // If it has not, you should return whether you think the drag should
0399     // be started.  This criteria usually includes "if
0400     // <accidentalDragAdjustedPoint> is not equal to startPoint()".
0401     //
0402     // If you are returning true, you must:
0403     //
0404     //   1. Set the document's selection (which may not have previously
0405     //      existed) to the specified size.
0406     //
0407     //   2. Update the status bar by calling kpTool::setUserShapePoints().
0408     //
0409     // If you return false, you are still permitted to do the above,
0410     // although it would be unusual (kpToolText does the above to allow a
0411     // single click -- with no dragging -- to create a new text box).
0412     //
0413     // The return value will be fed into the next call as <dragAccepted>.
0414     //
0415     // Arguments:
0416     //
0417     //   1. <accidentalDragAdjustedPoint>:
0418     //      This is the same as currentPoint() but is set to startPoint()
0419     //      if the mouse has not been moved much (6 manhatten length pixels
0420     //      from startPoint() within a short period of time (200ms)).
0421     //      This provides the accidental drag detection, referred to in the
0422     //      class' API Doc.
0423     //
0424     //   2. <normalizedRect>:
0425     //      This is as passed to kpTool::draw().
0426     //
0427     virtual bool drawCreateMoreSelectionAndUpdateStatusBar (
0428         bool dragAccepted,
0429         const QPoint &accidentalDragAdjustedPoint,
0430         const QRect &normalizedRect) = 0;
0431     void drawCreate (const QPoint &thisPoint, const QRect &normalizedRect);
0432 private Q_SLOTS:
0433     void delayedDrawCreate ();
0434 
0435 
0436 private:
0437     void cancelCreate ();
0438     void endDrawCreate ();
0439 
0440 
0441 private:
0442     QVariant operationCreate (Operation op,
0443         const QVariant &data1, const QVariant &data2);
0444 
0445 
0446 //
0447 // Move
0448 //
0449 
0450 private:
0451     // Called by constructor to initialize the "Move" draw type.
0452     void initMove ();
0453     // Called by destructor to uninitialize the "Move" draw type.
0454     void uninitMove ();
0455 
0456 
0457 private:
0458     void beginMove ();
0459     void endMove ();
0460 
0461 
0462 protected:
0463     virtual QString haventBegunDrawUserMessageMove () const = 0;
0464 private:
0465     void setCursorMove ();
0466 
0467 
0468 protected:
0469     // Sets the selection border mode when beginning to drag to move a
0470     // selection.
0471     //
0472     // Subclasses may wish to reimplement but should still call the base
0473     // implementation.  Reimplementations should wrap the whole
0474     // reimplementation in a kpViewManager::setQueueUpdates() block.
0475     virtual void setSelectionBorderForBeginDrawMove ();
0476 private:
0477     void beginDrawMove ();
0478 private Q_SLOTS:
0479     void slotRMBMoveUpdateGUI ();
0480 
0481 
0482 private:
0483     void drawMove (const QPoint &thisPoint, const QRect &normalizedRect);
0484 
0485 
0486 private:
0487     void cancelMove ();
0488 protected:
0489     // Returns what the name of the command that moves -- but does not smear
0490     // (not holding SHIFT) -- the selection, should be.
0491     virtual QString nonSmearMoveCommandName () const;
0492 private:
0493     void endDrawMove ();
0494 
0495 
0496 private:
0497     QVariant operationMove (Operation op,
0498         const QVariant &data1, const QVariant &data2);
0499 
0500 
0501 //
0502 // Resize/Scale
0503 //
0504 
0505 private:
0506     int onSelectionResizeHandle () const;
0507 
0508 
0509 private:
0510     // Called by constructor to initialize the "Resize/Scale" draw type.
0511     void initResizeScale ();
0512     // Called by destructor to uninitialize the "Resize/Scale" draw type.
0513     void uninitResizeScale ();
0514 
0515 
0516 private:
0517     void beginResizeScale ();
0518     void endResizeScale ();
0519 
0520 
0521 protected:
0522     virtual QString haventBegunDrawUserMessageResizeScale () const = 0;
0523 private:
0524     void setCursorResizeScale ();
0525 
0526 
0527 protected:
0528     // Sets the selection border mode when beginning to drag to resize or
0529     // scale a selection.
0530     //
0531     // Subclasses may wish to reimplement but should still call the base
0532     // implementation.  Reimplementations should wrap the whole
0533     // reimplementation in a kpViewManager::setQueueUpdates() block.
0534     virtual void setSelectionBorderForBeginDrawResizeScale ();
0535 private:
0536     void beginDrawResizeScale ();
0537 
0538 
0539 private:
0540     // drawResizeScaleCalculateNewSelectionPosSize() calls us with what the
0541     // <newWidth>x<newHeight> should be, but before any aspect maintenance
0542     // operations.
0543     //
0544     // <horizontalGripDragged> specifies whether a horizontal grip is being
0545     // dragged.  <verticalGripDragged> specifies whether a vertical grip is
0546     // being dragged.
0547     //
0548     // The selection before any resizing/scaling (before the sequence of
0549     // drags, where the mouse has been held down) is <originalSelection>.
0550     //
0551     // The method should output its attempt at maintaining the aspect ratio.
0552     // We say "attempt" because it is constrained by the minimum allowed
0553     // size of the selection.
0554     void drawResizeScaleTryKeepAspect (int newWidth, int newHeight,
0555         bool horizontalGripDragged, bool verticalGripDragged,
0556         const kpAbstractSelection &originalSelection,
0557         int *newWidthOut, int *newHeightOut);
0558 
0559     void drawResizeScaleCalculateNewSelectionPosSize (
0560         const kpAbstractSelection &originalSelection,
0561         int *newX, int *newY,
0562         int *newWidth, int *newHeight);
0563 
0564     void drawResizeScale (const QPoint &thisPoint, const QRect &normalizedRect);
0565 
0566 
0567 private:
0568     void cancelResizeScale ();
0569     void endDrawResizeScale ();
0570 
0571 
0572 private:
0573     QVariant operationResizeScale (Operation op,
0574         const QVariant &data1, const QVariant &data2);
0575 
0576 
0577 //
0578 // User Setting Selection Options
0579 //
0580 
0581 protected Q_SLOTS:
0582     virtual void slotIsOpaqueChanged (bool isOpaque) = 0;
0583 
0584 
0585 //
0586 // Keyboard Events
0587 //
0588 
0589 protected:
0590     // Reimplemented to trap Esc presses for deselecting the selection.
0591     // All other keypresses are passed to the base implementation.
0592     void keyPressEvent (QKeyEvent *e) override;
0593 
0594 
0595 private:
0596     struct kpAbstractSelectionToolPrivate * const d;
0597 };
0598 
0599 
0600 #endif  // kpAbstractSelectionTool_H