File indexing completed on 2024-12-22 04:16:42

0001 /*
0002  *  SPDX-FileCopyrightText: 2023 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef SVGTEXTCURSOR_H
0008 #define SVGTEXTCURSOR_H
0009 
0010 #include <KoSvgTextShape.h>
0011 #include <KoToolSelection.h>
0012 #include <QPainter>
0013 #include <KoShape.h>
0014 #include "kritatoolsvgtext_export.h"
0015 
0016 class KoCanvasBase;
0017 class SvgTextInsertCommand;
0018 class SvgTextRemoveCommand;
0019 class KUndo2Command;
0020 class QKeyEvent;
0021 class QInputMethodEvent;
0022 
0023 /**
0024  * @brief The SvgTextCursor class
0025  *
0026  * This class handles cursor movement and text editing operations.
0027  *
0028  * It acts as the KoToolSelection for SvgTextTool, allowing it to
0029  * integrate with the basic KoToolBase functionality for copy, cut
0030  * paste and clear.
0031  *
0032  * A selection is defined as the anchor being different from the cursor
0033  * position, with the move operation accepting whether you want to shift
0034  * the cursor position.
0035  *
0036  * It is also a shape listener to allow the textcursor to update itself
0037  * whenever the corresponding text shape changes.
0038  */
0039 
0040 class KRITATOOLSVGTEXT_EXPORT SvgTextCursor : public KoToolSelection, public KoSvgTextShape::TextCursorChangeListener
0041 {
0042     Q_OBJECT
0043 public:
0044     explicit SvgTextCursor(KoCanvasBase *canvas);
0045 
0046     enum MoveMode {
0047         MoveNone,
0048         MoveLeft,
0049         MoveRight,
0050         MoveUp,
0051         MoveDown,
0052         MoveNextChar,
0053         MovePreviousChar,
0054         MoveNextLine,
0055         MovePreviousLine,
0056         MoveWordLeft,
0057         MoveWordRight,
0058         MoveWordEnd,
0059         MoveWordStart,
0060         MoveLineStart,
0061         MoveLineEnd,
0062         ParagraphStart,
0063         ParagraphEnd,
0064     };
0065 
0066     ~SvgTextCursor();
0067 
0068     /**
0069      * @brief Get the current text shape
0070      * @return KoSvgTextShape *
0071      */
0072     KoSvgTextShape *shape() const;
0073 
0074     /**
0075      * @brief setShape
0076      * @param textShape KoSvgTextShape to set, is allowed to be a nullptr, the cursor just won't do anything.
0077      */
0078     void setShape(KoSvgTextShape *textShape);
0079 
0080     /**
0081      * @brief setCaretSetting
0082      * Set the caret settings for the cursor. Qt has some standard functionality associated, which we pass via this.
0083      * @param cursorWidth - Cursor width from the style.
0084      * @param cursorFlash - the total time it takes for a cursor to hide reapear.
0085      * @param cursorFlashLimit - maximum amount of time a cursor is allowed to flash.
0086      */
0087     void setCaretSetting(int cursorWidth = 1, int cursorFlash = 1000, int cursorFlashLimit = 5000);
0088 
0089     /**
0090      * @brief setVisualMode
0091      * set whether the navigation mode is visual or logical.
0092      * This right now primarily affects Bidirectional text.
0093      * @param mode whether to turn off visual mode.
0094      */
0095     void setVisualMode(bool visualMode = true);
0096 
0097     /// Get the current position.
0098     int getPos();
0099 
0100     /// Get the current selection anchor. This is the same as position, unless there's a selection.
0101     int getAnchor();
0102 
0103     /// Set the pos and the anchor.
0104     void setPos(int pos, int anchor);
0105 
0106     /// Set the pos from a point. This currently does a search inside the text shape.
0107     void setPosToPoint(QPointF point, bool moveAnchor = true);
0108 
0109     /// Move the cursor, and, if you don't want a selection, move the anchor.
0110     void moveCursor(MoveMode mode, bool moveAnchor = true);
0111 
0112     /// Insert text at getPos()
0113     void insertText(QString text);
0114 
0115     /**
0116      * @brief removeText
0117      * remove text relative to the current position.
0118      * This will move the cursor according to the move modes and then
0119      * remove the text between the two positions.
0120      * @param first how the cursor should move to get to the start position.
0121      * @param second how the cursor should move to get to the end position.
0122      */
0123     void removeText(MoveMode first, MoveMode second);
0124 
0125     void removeLastCodePoint();
0126 
0127     /**
0128      * @brief removeSelection
0129      * if there's a selection, creates a text-removal command.
0130      * @param parent
0131      * @return the text-removal command, if possible, if there's no selection or shape, it'll return 0;
0132      */
0133     void removeSelection();
0134 
0135     /**
0136      * @brief copy
0137      * copies plain text into the clipboard between anchor and pos.
0138      */
0139     void copy() const;
0140     /**
0141      * @brief paste
0142      * @return pastes plain text in the clipboard at pos.
0143      */
0144     bool paste();
0145 
0146     void deselectText();
0147 
0148     void paintDecorations(QPainter &gc, QColor selectionColor, int decorationThickness = 1);
0149 
0150     QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
0151     void inputMethodEvent(QInputMethodEvent *event);
0152 
0153     // Reimplemented.
0154     bool hasSelection() override;
0155 
0156     /// ShapeChangeListener reimplementation. This will update the cursor position
0157     /// when the shape was updated.
0158     void notifyShapeChanged(KoShape::ChangeType type, KoShape *shape) override;
0159 
0160     /// TextCursorChangeListener reimplementation, this allows undo commands
0161     /// to update the cursor without having the cursor owned by the command.
0162     void notifyCursorPosChanged(int pos, int anchor) override;
0163 
0164     /// Handle the cursor-related key events.
0165     void keyPressEvent(QKeyEvent *event);
0166 
0167     /// the cursor is currently adding a command
0168     bool isAddingCommand() const;
0169 
0170     /// Turns on blinking cursor.
0171     void focusIn();
0172 
0173     /// Stops blinking cursor.
0174     void focusOut();
0175 
0176 Q_SIGNALS:
0177 
0178     void updateCursorDecoration(QRectF updateRect);
0179 private Q_SLOTS:
0180     void blinkCursor();
0181     void stopBlinkCursor();
0182 
0183     void updateInputMethodItemTransform();
0184 
0185 private:
0186 
0187     /**
0188      * @brief removeSelection
0189      * if there's a selection, creates a text-removal command.
0190      * @param parent
0191      * @return the text-removal command, if possible, if there's no selection or shape, it'll return 0;
0192      */
0193     SvgTextRemoveCommand *removeSelectionImpl(KUndo2Command *parent = 0);
0194 
0195 
0196     /// update the cursor shape. First update will block ensuring the canvas is visible so setShape won't cause this.
0197     void updateCursor(bool firstUpdate = false);
0198     void updateSelection();
0199     void updateIMEDecoration();
0200     void addCommandToUndoAdapter(KUndo2Command *cmd);
0201 
0202     int moveModeResult(MoveMode &mode, int &pos, bool visual = false) const;
0203     bool acceptableInput(const QKeyEvent *event) const;
0204 
0205     void commitIMEPreEdit();
0206 
0207     struct Private;
0208     const QScopedPointer<Private> d;
0209 };
0210 
0211 Q_DECLARE_METATYPE(SvgTextCursor::MoveMode)
0212 
0213 
0214 #endif // SVGTEXTCURSOR_H