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