KoToolBase.h 17.9 KB
Newer Older
Thomas Zander's avatar
Thomas Zander committed
1
/* This file is part of the KDE project
2 3
 * SPDX-FileCopyrightText: 2006 Thomas Zander <zander@kde.org>
 * SPDX-FileCopyrightText: 2011 Jan Hambrecht <jaham@gmx.net>
Thomas Zander's avatar
Thomas Zander committed
4
 *
Samuel Gaist's avatar
Samuel Gaist committed
5
 * SPDX-License-Identifier: LGPL-2.0-or-later
Thomas Zander's avatar
Thomas Zander committed
6
 */
7 8
#ifndef KOTOOLBASE_H
#define KOTOOLBASE_H
Thomas Zander's avatar
Thomas Zander committed
9 10

#include <QObject>
11
#include <QPointer>
12 13 14
#include <QSet>
#include <QList>
#include <QHash>
Thomas Zander's avatar
Thomas Zander committed
15

16
#include "kritaflake_export.h"
Thomas Zander's avatar
Thomas Zander committed
17

18
class KoShape;
Thomas Zander's avatar
Thomas Zander committed
19
class KoCanvasBase;
20
class KoPointerEvent;
Thomas Zander's avatar
Thomas Zander committed
21
class KoViewConverter;
22
class KoToolSelection;
Thomas Zander's avatar
Thomas Zander committed
23
class KoToolBasePrivate;
24
class KoShapeControllerBase;
Thomas Zander's avatar
Thomas Zander committed
25

26
class QAction;
Thomas Zander's avatar
Thomas Zander committed
27 28
class QKeyEvent;
class QWidget;
29
class QCursor;
Thomas Zander's avatar
Thomas Zander committed
30
class QPainter;
31 32 33 34
class QString;
class QStringList;
class QRectF;
class QPointF;
35
class QInputMethodEvent;
36 37 38
class QDragMoveEvent;
class QDragLeaveEvent;
class QDropEvent;
39
class QTouchEvent;
40
class QMenu;
Thomas Zander's avatar
Thomas Zander committed
41 42

/**
43 44
 * Abstract base class for all tools. Tools can create or manipulate
 * flake shapes, canvas state or any other thing that a user may wish
45
 * to do to his document or his view on a document with a pointing
46 47
 * device.
 *
Thomas Zander's avatar
Thomas Zander committed
48
 * There exists an instance of every tool for every pointer device.
49
 * These instances are managed by the toolmanager..
Thomas Zander's avatar
Thomas Zander committed
50
 */
51
class KRITAFLAKE_EXPORT KoToolBase : public QObject
Thomas Zander's avatar
Thomas Zander committed
52 53 54
{
    Q_OBJECT
public:
55 56 57 58 59 60
    /// Option for activate()
    enum ToolActivation {
        TemporaryActivation, ///< The tool is activated temporarily and works 'in-place' of another one.
        DefaultActivation   ///< The tool is activated normally and emitting 'done' goes to the defaultTool
    };

Thomas Zander's avatar
Thomas Zander committed
61
    /**
62
     * Constructor, normally only called by the factory (see KoToolFactoryBase)
Thomas Zander's avatar
Thomas Zander committed
63 64
     * @param canvas the canvas interface this tool will work for.
     */
Thomas Zander's avatar
Thomas Zander committed
65
    explicit KoToolBase(KoCanvasBase *canvas);
66
    ~KoToolBase() override;
Thomas Zander's avatar
Thomas Zander committed
67

68 69
    virtual QRectF decorationsRect() const;

Thomas Zander's avatar
Thomas Zander committed
70
    /**
71
     * Return if dragging (moving with the mouse down) to the edge of a canvas should scroll the
72
     * canvas (default is true).
73
     * @return if this tool wants mouse events to cause scrolling of canvas.
Thomas Zander's avatar
Thomas Zander committed
74
     */
75
    virtual bool wantsAutoScroll() const;
Thomas Zander's avatar
Thomas Zander committed
76 77

    /**
78 79 80 81
     * Called by the canvas to paint any decorations that the tool deems needed.
     * The painter has the top left of the canvas as its origin.
     * @param painter used for painting the shape
     * @param converter to convert between internal and view coordinates.
Thomas Zander's avatar
Thomas Zander committed
82
     */
83
    virtual void paint(QPainter &painter, const KoViewConverter &converter) = 0;
Thomas Zander's avatar
Thomas Zander committed
84

85
    /**
86 87 88
     * Return the option widgets for this tool. Create them if they
     * do not exist yet. If the tool does not have an option widget,
     * this method return an empty list. (After discussion with Thomas, who prefers
89
     * the toolmanager to handle that case.)
90
     *
91
     * @see m_optionWidgets
92
     */
93
    QList<QPointer<QWidget> > optionWidgets();
Halla Rempt's avatar
Halla Rempt committed
94

95 96 97
    /**
     * Retrieve an action by name.
     */
Sven Langkamp's avatar
Sven Langkamp committed
98
    QAction *action(const QString &name) const;
99

Thomas Zander's avatar
Thomas Zander committed
100
    /**
Halla Rempt's avatar
Halla Rempt committed
101
     * Called when (one of) the mouse or stylus buttons is pressed.
Thomas Zander's avatar
Thomas Zander committed
102
     * Implementors should call event->ignore() if they do not actually use the event.
Halla Rempt's avatar
Halla Rempt committed
103
     * @param event state and reason of this mouse or stylus press
Thomas Zander's avatar
Thomas Zander committed
104
     */
105
    virtual void mousePressEvent(KoPointerEvent *event) = 0;
Thomas Zander's avatar
Thomas Zander committed
106 107

    /**
108
     * Called when (one of) the mouse or stylus buttons is double clicked.
Thomas Zander's avatar
Thomas Zander committed
109 110
     * Implementors should call event->ignore() if they do not actually use the event.
     * Default implementation ignores this event.
Halla Rempt's avatar
Halla Rempt committed
111
     * @param event state and reason of this mouse or stylus press
Thomas Zander's avatar
Thomas Zander committed
112
     */
113
    virtual void mouseDoubleClickEvent(KoPointerEvent *event);
Thomas Zander's avatar
Thomas Zander committed
114

115 116 117 118 119 120 121 122
    /**
     * Called when (one of) the mouse or stylus buttons is triple clicked.
     * Implementors should call event->ignore() if they do not actually use the event.
     * Default implementation ignores this event.
     * @param event state and reason of this mouse or stylus press
     */
    virtual void mouseTripleClickEvent(KoPointerEvent *event);

Thomas Zander's avatar
Thomas Zander committed
123
    /**
Halla Rempt's avatar
Halla Rempt committed
124
     * Called when the mouse or stylus moved over the canvas.
Thomas Zander's avatar
Thomas Zander committed
125
     * Implementors should call event->ignore() if they do not actually use the event.
Halla Rempt's avatar
Halla Rempt committed
126
     * @param event state and reason of this mouse or stylus move
Thomas Zander's avatar
Thomas Zander committed
127
     */
128
    virtual void mouseMoveEvent(KoPointerEvent *event) = 0;
Thomas Zander's avatar
Thomas Zander committed
129 130

    /**
Halla Rempt's avatar
Halla Rempt committed
131
     * Called when (one of) the mouse or stylus buttons is released.
Thomas Zander's avatar
Thomas Zander committed
132
     * Implementors should call event->ignore() if they do not actually use the event.
Halla Rempt's avatar
Halla Rempt committed
133
     * @param event state and reason of this mouse or stylus release
Thomas Zander's avatar
Thomas Zander committed
134
     */
135
    virtual void mouseReleaseEvent(KoPointerEvent *event) = 0;
Thomas Zander's avatar
Thomas Zander committed
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

    /**
     * Called when a key is pressed.
     * Implementors should call event->ignore() if they do not actually use the event.
     * Default implementation ignores this event.
     * @param event state and reason of this key press
     */
    virtual void keyPressEvent(QKeyEvent *event);

    /**
     * Called when a key is released
     * Implementors should call event->ignore() if they do not actually use the event.
     * Default implementation ignores this event.
     * @param event state and reason of this key release
     */
    virtual void keyReleaseEvent(QKeyEvent *event);

153 154 155 156 157 158 159
    /**
     * @brief explicitUserStrokeEndRequest is called by the input manager
     *        when the user presses Enter key or any equivalent. This callback
     *        comes before requestStrokeEnd(), which comes from a different source.
     */
    virtual void explicitUserStrokeEndRequest();

160 161 162 163
    /**
     * This method is used to query a set of properties of the tool to be
     * able to support complex input method operations as support for surrounding
     * text and reconversions.
164
     * Default implementation returns simple defaults, for tools that want to provide
Yuri Chornoivan's avatar
Yuri Chornoivan committed
165
     * a more responsive text entry experience for CJK languages it would be good to reimplement.
166
     * @param query specifies which property is queried.
Thomas Zander's avatar
Thomas Zander committed
167
     * @param converter the view converter for the current canvas.
168
     */
169
    virtual QVariant inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const;
170 171

    /**
172 173 174 175 176
     * Text entry of complex text, like CJK, can be made more interactive if a tool
     * implements this and the InputMethodQuery() methods.
     * Reimplementing this only provides the user with a more responsive text experience, since the
     * default implementation forwards the typed text as key pressed events.
     * @param event the input method event.
177
     */
178
    virtual void inputMethodEvent(QInputMethodEvent *event);
179

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
    /**
     * Called when (one of) a custom device buttons is pressed.
     * Implementors should call event->ignore() if they do not actually use the event.
     * @param event state and reason of this custom device press
     */
    virtual void customPressEvent(KoPointerEvent *event);

    /**
     * Called when (one of) a custom device buttons is released.
     * Implementors should call event->ignore() if they do not actually use the event.
     * @param event state and reason of this custom device release
     */
    virtual void customReleaseEvent(KoPointerEvent *event);

    /**
     * Called when a custom device moved over the canvas.
     * Implementors should call event->ignore() if they do not actually use the event.
     * @param event state and reason of this custom device move
     */
    virtual void customMoveEvent(KoPointerEvent *event);

201 202 203 204 205 206 207 208 209 210
    /**
     * @return true if synthetic mouse events on the canvas should be eaten.
     *
     * For example, the guides tool should allow click and drag with touch,
     * while the same touch events should be rejected by the freehand tool.
     *
     * These events are sent by the OS in Windows
     */
    bool maskSyntheticEvents() const;

Thomas Zander's avatar
Thomas Zander committed
211
    /**
212
     * get the identifier code from the KoToolFactoryBase that created this tool.
Thomas Zander's avatar
Thomas Zander committed
213
     * @return the toolId.
214
     * @see KoToolFactoryBase::id()
Thomas Zander's avatar
Thomas Zander committed
215
     */
216
    Q_INVOKABLE QString toolId() const;
217

218
    /// return the last emitted cursor
Thomas Zander's avatar
Thomas Zander committed
219 220
    QCursor cursor() const;

221 222 223 224 225 226 227 228 229 230 231 232
    /**
     * Returns the internal selection object of this tool.
     * Each tool can have a selection which is private to that tool and the specified shape that it comes with.
     * The default returns 0.
     */
    virtual KoToolSelection *selection();

    /**
     * @returns true if the tool has selected data.
     */
    virtual bool hasSelection();

233 234 235 236 237
    /**
     * copies the tools selection to the clipboard.
     * The default implementation is empty to aid tools that don't have any selection.
     * @see selection()
     */
238
    virtual void copy() const;
Thomas Zander's avatar
Flake:  
Thomas Zander committed
239

240 241
    /**
     * Delete the tools selection.
Thomas Zander's avatar
Thomas Zander committed
242
     * The default implementation is empty to aid tools that don't have any selection.
243 244 245 246 247 248 249 250 251 252 253 254
     * @see selection()
     */
    virtual void deleteSelection();

    /**
     * Cut the tools selection and copy it to the clipboard.
     * The default implementation calls copy() and then deleteSelection()
     * @see copy()
     * @see deleteSelection()
     */
    virtual void cut();

Thomas Zander's avatar
Thomas Zander committed
255 256 257 258 259 260
    /**
     * Paste the clipboard selection.
     * A tool typically has one or more shapes selected and pasting should do something meaningful
     * for this specific shape and tool combination.  Inserting text in a text tool, for example.
     * @return will return true if pasting succeeded. False if nothing happened.
     */
261
    virtual bool paste();
262

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
    /**
     * Handle the dragMoveEvent
     * A tool typically has one or more shapes selected and dropping into should do
     * something meaningful for this specific shape and tool combination. For example
     * dropping text in a text tool.
     * The tool should Accept the event if it is meaningful; Default implementation does not.
     */
    virtual void dragMoveEvent(QDragMoveEvent *event, const QPointF &point);

    /**
     * Handle the dragLeaveEvent
     * Basically just a noticification that the drag is no long relevant
     * The tool should Accept the event if it is meaningful; Default implementation does not.
     */
    virtual void dragLeaveEvent(QDragLeaveEvent *event);

    /**
     * Handle the dropEvent
     * A tool typically has one or more shapes selected and dropping into should do
     * something meaningful for this specific shape and tool combination. For example
     * dropping text in a text tool.
     * The tool should Accept the event if it is meaningful; Default implementation does not.
     */
    virtual void dropEvent(QDropEvent *event, const QPointF &point);

288
    /**
Halla Rempt's avatar
Halla Rempt committed
289
     * @return a menu with context-aware actions for the current selection. If
290
     *         the returned value is null, no context menu is shown.
291
     */
292
    virtual QMenu* popupActionsMenu();
293

294
    /// Returns the canvas the tool is working on
295
    KoCanvasBase *canvas() const;
296

297 298 299 300 301
    /**
      * This method can be reimplemented in a subclass.
      * @return returns true, if the tool is in text mode. that means, that there is
      *   any kind of textual input and all single key shortcuts should be eaten.
      */
302
    bool isInTextMode() const;
303

Halla Rempt's avatar
Halla Rempt committed
304 305
public Q_SLOTS:

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
    /**
     * Called when the user requested undo while the stroke is
     * active. If you tool supports undo of the part of its actions,
     * override this method and do the needed work there.
     *
     * NOTE: Default implementation forwards this request to
     *       requestStrokeCancellation() method, so that the stroke
     *       would be cancelled.
     */
    virtual void requestUndoDuringStroke();

    /**
     * Called when the user requested the cancellation of the current
     * stroke. If you tool supports cancelling, override this method
     * and do the needed work there
     */
    virtual void requestStrokeCancellation();

    /**
     * Called when the image decided that the stroke should better be
     * ended. If you tool supports long strokes, override this method
     * and do the needed work there
     */
    virtual void requestStrokeEnd();

331

332 333 334 335
    /**
     * This method is called when this tool instance is activated.
     * For any main window there is only one tool active at a time, which then gets all
     * user input.  Switching between tools will call deactivate on one and activate on the
336 337 338
     * new tool allowing the tool to flush items (like a selection)
     * when it is not in use.
     *
339 340 341
     * <p>There is one case where two tools are activated at the same.  This is the case
     * where one tool delegates work to another temporarily.  For example, while shift is
     * being held down.  The second tool will get activated with temporary=true and
342
     * it should emit done() when the state that activated it is ended.
343 344
     * <p>One of the important tasks of activate is to call useCursor()
     *
345 346 347
     * @param shapes the set of shapes that are selected or suggested for editing by a
     *      selected shape for the tool to work on.  Not all shapes will be meant for this
     *      tool.
luz paz's avatar
luz paz committed
348
     * @param toolActivation if TemporaryActivation, this tool is only temporarily activated
349
     *                  and should emit done when it is done.
350 351
     * @see deactivate()
     */
352
    virtual void activate(ToolActivation toolActivation, const QSet<KoShape*> &shapes);
353 354 355 356 357 358 359 360 361 362 363 364 365

    /**
     * This method is called whenever this tool is no longer the
     * active tool
     * @see activate()
     */
    virtual void deactivate();

    /**
     * This method is called whenever a property in the resource
     * provider associated with the canvas this tool belongs to
     * changes. An example is currently selected foreground color.
     */
366
    virtual void canvasResourceChanged(int key, const QVariant &res);
367

368 369 370 371 372 373 374
    /**
     * This method is called whenever a property in the resource
     * provider associated with the document this tool belongs to
     * changes. An example is the handle radius
     */
    virtual void documentResourceChanged(int key, const QVariant &res);

375 376 377 378
    /**
     * This method just relays the given text via the tools statusTextChanged signal.
     * @param statusText the new status text
     */
379
    void setStatusText(const QString &statusText);
380

381 382 383 384 385 386
    /**
     * request a repaint of the decorations to be made. This triggers
     * an update call on the canvas, but does not paint directly.
     */
    virtual void repaintDecorations();

387
Q_SIGNALS:
Thomas Zander's avatar
Thomas Zander committed
388 389

    /**
390
     * Emitted when this tool wants itself to be replaced by another tool.
Thomas Zander's avatar
Thomas Zander committed
391
     *
392
     * @param id the identification of the desired tool
393
     * @see toolId(), KoToolFactoryBase::id()
Thomas Zander's avatar
Thomas Zander committed
394
     */
395
    void activateTool(const QString &id);
Thomas Zander's avatar
Thomas Zander committed
396 397

    /**
398 399
     * Emitted when this tool wants itself to temporarily be replaced by another tool.
     * For instance, a paint tool could desire to be
Thomas Zander's avatar
Thomas Zander committed
400
     * temporarily replaced by a pan tool which could be temporarily
401
     * replaced by a color sampler.
402
     * @param id the identification of the desired tool
Thomas Zander's avatar
Thomas Zander committed
403
     */
Thomas Zander's avatar
Thomas Zander committed
404
    void activateTemporary(const QString &id);
Thomas Zander's avatar
Thomas Zander committed
405 406 407 408 409

    /**
     * Emitted when the tool has been temporarily activated and wants
     * to notify the world that it's done.
     */
410
    void done();
Thomas Zander's avatar
Thomas Zander committed
411

412 413 414 415
    /**
     * Emitted by useCursor() when the cursor to display on the canvas is changed.
     * The KoToolManager should connect to this signal to handle cursors further.
     */
416
    void cursorChanged(const QCursor &cursor);
417

418 419 420 421
    /**
     * A tool can have a selection that is copy-able, this signal is emitted when that status changes.
     * @param hasSelection is true when the tool holds selected data.
     */
422
    void selectionChanged(bool hasSelection);
423

424 425 426 427
    /**
     * Emitted when the tool wants to display a different status text
     * @param statusText the new status text
     */
428
    void statusTextChanged(const QString &statusText);
429

Thomas Zander's avatar
Thomas Zander committed
430
protected:
431
    /**
Thomas Zander's avatar
Thomas Zander committed
432
     * Classes inheriting from this one can call this method to signify which cursor
433 434
     * the tool wants to display at this time.  Logical place to call it is after an
     * incoming event has been handled.
435
     * @param cursor the new cursor.
436
     */
437
    void useCursor(const QCursor &cursor);
438 439 440 441 442

    /**
     * Reimplement this if your tool actually has an option widget.
     * Sets the option widget to 0 by default.
     */
443
    virtual QWidget *createOptionWidget();
444
    virtual QList<QPointer<QWidget> > createOptionWidgets();
445

446
    /// Convenience function to get the current handle radius
447 448 449 450 451
    int handleRadius() const;

    /// Convenience function to get the current handle radius measured in document
    /// coordinates (points)
    qreal handleDocRadius() const;
452 453

    /// Convencience function to get the current grab sensitivity
454
    int grabSensitivity() const;
455

456 457 458 459 460 461 462 463
    /**
    * Returns a handle grab rect at the given position.
    *
    * The position is expected to be in document coordinates. The grab sensitivity
    * canvas resource is used for the dimension of the rectangle.
    *
    * @return the handle rectangle in document coordinates
    */
464
    QRectF handleGrabRect(const QPointF &position) const;
465

466 467 468 469 470 471 472 473
    /**
    * Returns a handle paint rect at the given position.
    *
    * The position is expected to be in document coordinates. The handle radius
    * canvas resource is used for the dimension of the rectangle.
    *
    * @return the handle rectangle in document coordinates
    */
474
    QRectF handlePaintRect(const QPointF &position) const;
475

476 477 478 479 480 481
    /**
      * You should set the text mode to true in subclasses, if this tool is in text input mode, eg if the users
      * are able to type. If you don't set it, then single key shortcuts will get the key event and not this tool.
      */
    void setTextMode(bool value);

482 483 484 485 486
    /**
     * Allows subclasses to specify whether synthetic mouse events should be accepted.
     */
    void setMaskSyntheticEvents(bool value);

487 488 489 490 491
    /**
     * Returns true if activate() has been called (more times than deactivate :) )
     */
    bool isActivated() const;

492
protected:
Thomas Zander's avatar
Thomas Zander committed
493
    KoToolBase(KoToolBasePrivate &dd);
Thomas Zander's avatar
Thomas Zander committed
494

Thomas Zander's avatar
Thomas Zander committed
495
    KoToolBasePrivate *d_ptr;
Thomas Zander's avatar
Thomas Zander committed
496

497

Thomas Zander's avatar
Thomas Zander committed
498
private:
499 500 501 502 503 504 505 506 507 508

    friend class ToolHelper;

    /**
     * Set the identifier code from the KoToolFactoryBase that created this tool.
     * @param id the identifier code
     * @see KoToolFactoryBase::id()
     */
    void setToolId(const QString &id);

Thomas Zander's avatar
Thomas Zander committed
509 510 511
    KoToolBase();
    KoToolBase(const KoToolBase&);
    KoToolBase& operator=(const KoToolBase&);
512

Thomas Zander's avatar
Thomas Zander committed
513
    Q_DECLARE_PRIVATE(KoToolBase)
Thomas Zander's avatar
Thomas Zander committed
514 515 516
};

#endif /* KOTOOL_H */