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