File indexing completed on 2024-05-12 15:45:40

0001 /*
0002     SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org>
0003     SPDX-FileCopyrightText: 2012-2013 Dominik Haumann <dhaumann@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #ifndef KTEXTEDITOR_DOCUMENT_CURSOR_H
0009 #define KTEXTEDITOR_DOCUMENT_CURSOR_H
0010 
0011 #include <ktexteditor/cursor.h>
0012 #include <ktexteditor/document.h>
0013 #include <ktexteditor_export.h>
0014 
0015 #include <QDebug>
0016 
0017 namespace KTextEditor
0018 {
0019 /**
0020  * \class DocumentCursor documentcursor.h <KTextEditor/DocumentCursor>
0021  *
0022  * \short A Cursor which is bound to a specific Document.
0023  *
0024  * \section documentcursor_intro Introduction
0025  * A DocumentCursor is an extension of the basic Cursor class.
0026  * The DocumentCursor is bound to a specific Document instance.
0027  * This way, the cursor provides additional functions like gotoNextLine(),
0028  * gotoPreviousLine() and move() according to the WrapBehavior.
0029  *
0030  * The only difference to a MovingCursor is that the DocumentCursor's
0031  * position does not automatically move on text manipulation.
0032  *
0033  * \section documentcursor_position Validity
0034  *
0035  * When constructing a DocumentCursor, a valid document pointer is required
0036  * in the constructor. A null pointer will assert in debug builds.
0037  * Further, a DocumentCursor should only be used as long as the Document
0038  * exists, otherwise the DocumentCursor contains a dangling pointer to the
0039  * previously assigned Document.
0040  *
0041  * \section documentcursor_example Example
0042  *
0043  * A DocumentCursor is created and used like this:
0044  * \code
0045  * KTextEditor::DocumentCursor docCursor(document);
0046  * docCursor.setPosition(0, 0);
0047  * docCursor.gotoNextLine();
0048  * docCursor.move(5); // move 5 characters to the right
0049  * \endcode
0050  *
0051  * \see KTextEditor::Cursor, KTextEditor::MovingCursor
0052  *
0053  * \author Dominik Haumann \<dhaumann@kde.org\>
0054  */
0055 class KTEXTEDITOR_EXPORT DocumentCursor
0056 {
0057     //
0058     // sub types
0059     //
0060 public:
0061     /**
0062      * Wrap behavior for end of line treatement used in move().
0063      */
0064     enum WrapBehavior {
0065         Wrap = 0x0, ///< wrap at end of line
0066         NoWrap = 0x1 ///< do not wrap at end of line
0067     };
0068 
0069     //
0070     // Constructor
0071     //
0072 public:
0073     /**
0074      * Constructor that creates a DocumentCursor at the \e invalid position
0075      * (-1, -1).
0076      * \see isValid()
0077      */
0078     DocumentCursor(KTextEditor::Document *document);
0079 
0080     /**
0081      * Constructor that creates a DocumentCursor located at \p position.
0082      */
0083     DocumentCursor(KTextEditor::Document *document, const KTextEditor::Cursor &position);
0084 
0085     /**
0086      * Constructor that creates a DocumentCursor located at \p line and \p column.
0087      */
0088     DocumentCursor(KTextEditor::Document *document, int line, int column);
0089 
0090     /**
0091      * Copy constructor. Make sure the Document of the DocumentCursor is
0092      * valid.
0093      */
0094     DocumentCursor(const DocumentCursor &other);
0095 
0096     //
0097     // stuff that needs to be implemented by editor part cursors
0098     //
0099 public:
0100     /**
0101      * Gets the document to which this cursor is bound.
0102      * \return a pointer to the document
0103      */
0104     inline Document *document() const
0105     {
0106         return m_document;
0107     }
0108 
0109     /**
0110      * Set the current cursor position to \e position.
0111      * If \e position is not valid, meaning that either its line < 0 or its
0112      * column < 0, then the document cursor will also be invalid
0113      *
0114      * \param position new cursor position
0115      */
0116     void setPosition(KTextEditor::Cursor position)
0117     {
0118         m_cursor = position;
0119     }
0120 
0121     /**
0122      * Retrieve the line on which this cursor is situated.
0123      * \return line number, where 0 is the first line.
0124      */
0125     inline int line() const
0126     {
0127         return m_cursor.line();
0128     }
0129 
0130     /**
0131      * Retrieve the column on which this cursor is situated.
0132      * \return column number, where 0 is the first column.
0133      */
0134     inline int column() const
0135     {
0136         return m_cursor.column();
0137     }
0138 
0139     /**
0140      * Destruct the moving cursor.
0141      */
0142     ~DocumentCursor()
0143     {
0144     }
0145 
0146     //
0147     // forbidden stuff
0148     //
0149 private:
0150     /**
0151      * no default constructor, as we need a document.
0152      */
0153     DocumentCursor();
0154 
0155     //
0156     // convenience API
0157     //
0158 public:
0159     /**
0160      * Check if the current position of this cursor is a valid position,
0161      * i.e. whether line() >= 0 and column() >= 0.
0162      * \return \e true, if the cursor position is valid, otherwise \e false
0163      * \see KTextEditor::Cursor::isValid(), isValidTextPosition()
0164      */
0165     inline bool isValid() const
0166     {
0167         return m_cursor.isValid();
0168     }
0169 
0170     /**
0171      * Check if this cursor is currently at a valid text position.
0172      * A cursor position at (line, column) is valid, if
0173      * - line >= 0 and line < lines() holds, and
0174      * - column >= 0 and column <= lineLength(column).
0175      *
0176      * The text position is also invalid if it is inside a Unicode surrogate.
0177      * Therefore, use this function when iterating over the characters of a line.
0178      *
0179      * \see KTextEditor::Document::isValidTextPosition(), isValid()
0180      */
0181     inline bool isValidTextPosition() const
0182     {
0183         return document()->isValidTextPosition(m_cursor);
0184     }
0185 
0186     /**
0187      * Make sure the cursor position is at a valid text position according to
0188      * the following rules.
0189      * - If the cursor is invalid(), i.e. either line < 0 or column < 0, it is
0190      *   set to (0, 0).
0191      * - If the cursor's line is past the number of lines in the document, the
0192      *   cursor is set to Document::documentEnd().
0193      * - If the cursor's column is past the line length, the cursor column is
0194      *   set to the line length.
0195      * - If the cursor is inside a Unicode surrogate, the cursor is moved to the
0196      *   beginning of the Unicode surrogate.
0197      *
0198      * After calling makeValid(), the cursor is guaranteed to be located at
0199      * a valid text position.
0200      * \see isValidTextPosition(), isValid()
0201      */
0202     void makeValid();
0203 
0204     /**
0205      * \overload
0206      *
0207      * Set the cursor position to \e line and \e column.
0208      *
0209      * \param line new cursor line
0210      * \param column new cursor column
0211      */
0212     void setPosition(int line, int column);
0213 
0214     /**
0215      * Set the cursor line to \e line.
0216      * \param line new cursor line
0217      */
0218     void setLine(int line);
0219 
0220     /**
0221      * Set the cursor column to \e column.
0222      * \param column new cursor column
0223      */
0224     void setColumn(int column);
0225 
0226     /**
0227      * Determine if this cursor is located at column 0 of a valid text line.
0228      *
0229      * \return \e true if cursor is a valid text position and column()=0, otherwise \e false.
0230      */
0231     bool atStartOfLine() const;
0232 
0233     /**
0234      * Determine if this cursor is located at the end of the current line.
0235      *
0236      * \return \e true if the cursor is situated at the end of the line, otherwise \e false.
0237      */
0238     bool atEndOfLine() const;
0239 
0240     /**
0241      * Determine if this cursor is located at line 0 and column 0.
0242      *
0243      * \return \e true if the cursor is at start of the document, otherwise \e false.
0244      */
0245     bool atStartOfDocument() const;
0246 
0247     /**
0248      * Determine if this cursor is located at the end of the last line in the
0249      * document.
0250      *
0251      * \return \e true if the cursor is at the end of the document, otherwise \e false.
0252      */
0253     bool atEndOfDocument() const;
0254 
0255     /**
0256      * Moves the cursor to the next line and sets the column to 0. If the cursor
0257      * position is already in the last line of the document, the cursor position
0258      * remains unchanged and the return value is \e false.
0259      *
0260      * \return \e true on success, otherwise \e false
0261      */
0262     bool gotoNextLine();
0263 
0264     /**
0265      * Moves the cursor to the previous line and sets the column to 0. If the
0266      * cursor position is already in line 0, the cursor position remains
0267      * unchanged and the return value is \e false.
0268      *
0269      * \return \e true on success, otherwise \e false
0270      */
0271     bool gotoPreviousLine();
0272 
0273     /**
0274      * Moves the cursor \p chars character forward or backwards. If \e wrapBehavior
0275      * equals WrapBehavior::Wrap, the cursor is automatically wrapped to the
0276      * next line at the end of a line.
0277      *
0278      * When moving backwards, the WrapBehavior does not have any effect.
0279      * \note If the cursor could not be moved the amount of chars requested,
0280      *       the cursor is not moved at all!
0281      *
0282      * \return \e true on success, otherwise \e false
0283      */
0284     bool move(int chars, WrapBehavior wrapBehavior = Wrap);
0285 
0286     /**
0287      * Convert this clever cursor into a dumb one.
0288      * @return normal cursor
0289      */
0290     inline Cursor toCursor() const
0291     {
0292         return m_cursor;
0293     }
0294 
0295     /**
0296      * Convert this clever cursor into a dumb one. Equal to toCursor, allowing to use implicit conversion.
0297      * @return normal cursor
0298      */
0299     inline operator Cursor() const
0300     {
0301         return m_cursor;
0302     }
0303 
0304     //
0305     // operators for: DocumentCursor <-> DocumentCursor
0306     //
0307     /**
0308      * Assignment operator. Same as the copy constructor. Make sure that
0309      * the assigned Document is a valid document pointer.
0310      */
0311     DocumentCursor &operator=(const DocumentCursor &other)
0312     {
0313         m_document = other.m_document;
0314         m_cursor = other.m_cursor;
0315         return *this;
0316     }
0317 
0318     /**
0319      * Equality operator.
0320      *
0321      * \note comparison between two invalid cursors is undefined.
0322      *       comparison between an invalid and a valid cursor will always be \e false.
0323      *
0324      * \param c1 first cursor to compare
0325      * \param c2 second cursor to compare
0326      * \return \e true, if c1's and c2's assigned document, line and column are \e equal.
0327      */
0328     inline friend bool operator==(const DocumentCursor &c1, const DocumentCursor &c2)
0329     {
0330         return c1.document() == c2.document() && c1.line() == c2.line() && c1.column() == c2.column();
0331     }
0332 
0333     /**
0334      * Inequality operator.
0335      * \param c1 first cursor to compare
0336      * \param c2 second cursor to compare
0337      * \return \e true, if c1's and c2's assigned document, line and column are \e not equal.
0338      */
0339     inline friend bool operator!=(const DocumentCursor &c1, const DocumentCursor &c2)
0340     {
0341         return !(c1 == c2);
0342     }
0343 
0344     /**
0345      * Greater than operator.
0346      * \param c1 first cursor to compare
0347      * \param c2 second cursor to compare
0348      * \return \e true, if c1's position is greater than c2's position,
0349      *         otherwise \e false.
0350      */
0351     inline friend bool operator>(const DocumentCursor &c1, const DocumentCursor &c2)
0352     {
0353         return c1.line() > c2.line() || (c1.line() == c2.line() && c1.column() > c2.column());
0354     }
0355 
0356     /**
0357      * Greater than or equal to operator.
0358      * \param c1 first cursor to compare
0359      * \param c2 second cursor to compare
0360      * \return \e true, if c1's position is greater than or equal to c2's
0361      *         position, otherwise \e false.
0362      */
0363     inline friend bool operator>=(const DocumentCursor &c1, const DocumentCursor &c2)
0364     {
0365         return c1.line() > c2.line() || (c1.line() == c2.line() && c1.column() >= c2.column());
0366     }
0367 
0368     /**
0369      * Less than operator.
0370      * \param c1 first cursor to compare
0371      * \param c2 second cursor to compare
0372      * \return \e true, if c1's position is greater than or equal to c2's
0373      *         position, otherwise \e false.
0374      */
0375     inline friend bool operator<(const DocumentCursor &c1, const DocumentCursor &c2)
0376     {
0377         return !(c1 >= c2);
0378     }
0379 
0380     /**
0381      * Less than or equal to operator.
0382      * \param c1 first cursor to compare
0383      * \param c2 second cursor to compare
0384      * \return \e true, if c1's position is lesser than or equal to c2's
0385      *         position, otherwise \e false.
0386      */
0387     inline friend bool operator<=(const DocumentCursor &c1, const DocumentCursor &c2)
0388     {
0389         return !(c1 > c2);
0390     }
0391 
0392     /**
0393      * qDebug() stream operator. Writes this cursor to the debug output in a nicely formatted way.
0394      * @param s debug stream
0395      * @param cursor cursor to print
0396      * @return debug stream
0397      */
0398     inline friend QDebug operator<<(QDebug s, const DocumentCursor *cursor)
0399     {
0400         if (cursor) {
0401             s.nospace() << "(" << cursor->document() << ": " << cursor->line() << ", " << cursor->column() << ")";
0402         } else {
0403             s.nospace() << "(null document cursor)";
0404         }
0405         return s.space();
0406     }
0407 
0408     /**
0409      * qDebug() stream operator. Writes this cursor to the debug output in a nicely formatted way.
0410      * @param s debug stream
0411      * @param cursor cursor to print
0412      * @return debug stream
0413      */
0414     inline friend QDebug operator<<(QDebug s, const DocumentCursor &cursor)
0415     {
0416         return s << &cursor;
0417     }
0418 
0419 private:
0420     KTextEditor::Document *m_document;
0421     KTextEditor::Cursor m_cursor;
0422 };
0423 
0424 }
0425 
0426 Q_DECLARE_TYPEINFO(KTextEditor::DocumentCursor, Q_MOVABLE_TYPE);
0427 
0428 #endif