1/*
2 SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org>
3 SPDX-FileCopyrightText: 2012-2013 Dominik Haumann <dhaumann@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#ifndef KTEXTEDITOR_DOCUMENT_CURSOR_H
9#define KTEXTEDITOR_DOCUMENT_CURSOR_H
10
11#include <ktexteditor/cursor.h>
12#include <ktexteditor/document.h>
13#include <ktexteditor_export.h>
14
15#include <QDebug>
16
17namespace KTextEditor
18{
19/*!
20 * \class KTextEditor::DocumentCursor
21 * \inmodule KTextEditor
22 * \inheaderfile KTextEditor/DocumentCursor
23 *
24 * \brief A Cursor which is bound to a specific Document.
25 *
26 * \target documentcursor_intro
27 * \section1 Introduction
28 *
29 * A DocumentCursor is an extension of the basic Cursor class.
30 * The DocumentCursor is bound to a specific Document instance.
31 * This way, the cursor provides additional functions like gotoNextLine(),
32 * gotoPreviousLine() and move() according to the WrapBehavior.
33 *
34 * The only difference to a MovingCursor is that the DocumentCursor's
35 * position does not automatically move on text manipulation.
36 *
37 * \target documentcursor_position
38 * \section1 Validity
39 *
40 * When constructing a DocumentCursor, a valid document pointer is required
41 * in the constructor. A null pointer will assert in debug builds.
42 * Further, a DocumentCursor should only be used as long as the Document
43 * exists, otherwise the DocumentCursor contains a dangling pointer to the
44 * previously assigned Document.
45 *
46 * \target documentcursor_example
47 * \section1 Example
48 *
49 * A DocumentCursor is created and used like this:
50 * \code
51 * KTextEditor::DocumentCursor docCursor(document);
52 * docCursor.setPosition(0, 0);
53 * docCursor.gotoNextLine();
54 * docCursor.move(5); // move 5 characters to the right
55 * \endcode
56 *
57 * \sa KTextEditor::Cursor, KTextEditor::MovingCursor
58 */
59class KTEXTEDITOR_EXPORT DocumentCursor
60{
61 //
62 // sub types
63 //
64public:
65 /*!
66 \enum KTextEditor::DocumentCursor::WrapBehavior
67
68 Wrap behavior for end of line treatement used in move().
69
70 \value Wrap
71 Wrap at end of line.
72 \value NoWrap
73 Do not wrap at end of line.
74 */
75 enum WrapBehavior {
76 Wrap = 0x0,
77 NoWrap = 0x1
78 };
79
80 //
81 // Constructor
82 //
83public:
84 /*!
85 * Constructor that creates a DocumentCursor at the \c invalid position
86 * (-1, -1).
87 * \sa isValid()
88 */
89 DocumentCursor(KTextEditor::Document *document);
90
91 /*!
92 * Constructor that creates a DocumentCursor located at \a position.
93 */
94 DocumentCursor(KTextEditor::Document *document, KTextEditor::Cursor position);
95
96 /*!
97 * Constructor that creates a DocumentCursor located at \a line and \a column.
98 */
99 DocumentCursor(KTextEditor::Document *document, int line, int column);
100
101 /*!
102 * Copy constructor. Make sure the Document of the DocumentCursor is
103 * valid.
104 */
105 DocumentCursor(const DocumentCursor &other);
106
107 //
108 // stuff that needs to be implemented by editor part cursors
109 //
110public:
111 /*!
112 * Gets the document to which this cursor is bound.
113 * Returns a pointer to the document
114 */
115 inline Document *document() const
116 {
117 return m_document;
118 }
119
120 /*!
121 * Set the current cursor position to \c position.
122 * If \c position is not valid, meaning that either its line < 0 or its
123 * column < 0, then the document cursor will also be invalid
124 *
125 * \a position is new cursor position
126 *
127 */
128 void setPosition(KTextEditor::Cursor position)
129 {
130 m_cursor = position;
131 }
132
133 /*!
134 * Retrieve the line on which this cursor is situated.
135 * Returns line number, where 0 is the first line.
136 */
137 inline int line() const
138 {
139 return m_cursor.line();
140 }
141
142 /*!
143 * Retrieve the column on which this cursor is situated.
144 * Returns column number, where 0 is the first column.
145 */
146 inline int column() const
147 {
148 return m_cursor.column();
149 }
150
151 /*!
152 * Destruct the moving cursor.
153 */
154 ~DocumentCursor()
155 {
156 }
157
158 //
159 // forbidden stuff
160 //
161public:
162 /*!
163 * no default constructor, as we need a document.
164 */
165 DocumentCursor() = delete;
166
167 //
168 // convenience API
169 //
170public:
171 /*!
172 * Check if the current position of this cursor is a valid position,
173 * i.e. whether line() >= 0 and column() >= 0.
174 * Returns \c true, if the cursor position is valid, otherwise \c false
175 * \sa KTextEditor::Cursor::isValid(), isValidTextPosition()
176 */
177 inline bool isValid() const
178 {
179 return m_cursor.isValid();
180 }
181
182 /*!
183 * Returns true if this cursor is currently at a valid text position.
184 * A cursor position at (line, column) is valid, if
185 * \list
186 * \li line >= 0 and line < lines() holds, and
187 * \li column >= 0 and column <= lineLength(column).
188 * \endlist
189 *
190 * The text position is also invalid if it is inside a Unicode surrogate.
191 * Therefore, use this function when iterating over the characters of a line.
192 *
193 * \sa KTextEditor::Document::isValidTextPosition(), isValid()
194 */
195 inline bool isValidTextPosition() const
196 {
197 return document()->isValidTextPosition(cursor: m_cursor);
198 }
199
200 /*!
201 * Make sure the cursor position is at a valid text position according to
202 * the following rules.
203 * \list
204 * \li If the cursor is invalid(), i.e. either line < 0 or column < 0, it is
205 * set to (0, 0).
206 * \li If the cursor's line is past the number of lines in the document, the
207 * cursor is set to Document::documentEnd().
208 * \li If the cursor's column is past the line length, the cursor column is
209 * set to the line length.
210 * \li If the cursor is inside a Unicode surrogate, the cursor is moved to the
211 * beginning of the Unicode surrogate.
212 * \endlist
213 *
214 * After calling makeValid(), the cursor is guaranteed to be located at
215 * a valid text position.
216 * \sa isValidTextPosition(), isValid()
217 */
218 void makeValid();
219
220 /*!
221 * \overload
222 *
223 * Set the cursor position to \c line and \c column.
224 *
225 * \a line is the new cursor line
226 *
227 * \a column is the new cursor column
228 *
229 */
230 void setPosition(int line, int column);
231
232 /*!
233 * Set the cursor line to \c line. The cursor's column is not changed.
234 *
235 * \a line is the new cursor line
236 *
237 */
238 void setLine(int line);
239
240 /*!
241 * Set the cursor column to \c column. The cursor's line number is not changed.
242 *
243 * \a column is the new cursor column
244 *
245 */
246 void setColumn(int column);
247
248 /*!
249 * Determine if this cursor is located at column 0 of a valid text line.
250 *
251 * Returns \c true if cursor is a valid text position and column()=0, otherwise \c false.
252 */
253 bool atStartOfLine() const;
254
255 /*!
256 * Determine if this cursor is located at the end of the current line.
257 *
258 * Returns \c true if the cursor is situated at the end of the line, otherwise \c false.
259 */
260 bool atEndOfLine() const;
261
262 /*!
263 * Determine if this cursor is located at line 0 and column 0.
264 *
265 * Returns \c true if the cursor is at start of the document, otherwise \c false.
266 */
267 bool atStartOfDocument() const;
268
269 /*!
270 * Determine if this cursor is located at the end of the last line in the
271 * document.
272 *
273 * Returns \c true if the cursor is at the end of the document, otherwise \c false.
274 */
275 bool atEndOfDocument() const;
276
277 /*!
278 * Moves the cursor to the next line and sets the column to 0. If the cursor
279 * position is already in the last line of the document, the cursor position
280 * remains unchanged and the return value is \c false.
281 *
282 * Returns \c true on success, otherwise \c false
283 */
284 bool gotoNextLine();
285
286 /*!
287 * Moves the cursor to the previous line and sets the column to 0. If the
288 * cursor position is already in line 0, the cursor position remains
289 * unchanged and the return value is \c false.
290 *
291 * Returns \c true on success, otherwise \c false
292 */
293 bool gotoPreviousLine();
294
295 /*!
296 * Moves the cursor \a chars character forward or backwards. If \a wrapBehavior
297 * equals WrapBehavior::Wrap, the cursor is automatically wrapped to the
298 * next line at the end of a line.
299 *
300 * When moving backwards, the WrapBehavior does not have any effect.
301 * \note If the cursor could not be moved the amount of chars requested,
302 * the cursor is not moved at all!
303 *
304 * Returns \c true on success, otherwise \c false
305 */
306 bool move(int chars, WrapBehavior wrapBehavior = Wrap);
307
308 /*!
309 * Convert this clever cursor into a dumb one.
310 * Returns normal cursor
311 */
312 inline Cursor toCursor() const
313 {
314 return m_cursor;
315 }
316
317 /*!
318 * Convert this clever cursor into a dumb one. Equal to toCursor, allowing to use implicit conversion.
319 * Returns normal cursor
320 */
321 inline operator Cursor() const
322 {
323 return m_cursor;
324 }
325
326 //
327 // operators for: DocumentCursor <-> DocumentCursor
328 //
329 /*!
330 * Assignment operator. Same as the copy constructor. Make sure that
331 * the assigned Document is a valid document pointer.
332 *
333 * \a other is the DocumentCursor to assign to this
334 *
335 */
336 DocumentCursor &operator=(const DocumentCursor &other)
337 {
338 m_document = other.m_document;
339 m_cursor = other.m_cursor;
340 return *this;
341 }
342
343 /*!
344 * Equality operator.
345 *
346 * \note comparison between two invalid cursors is undefined.
347 * comparison between an invalid and a valid cursor will always be \c false.
348 *
349 * \a c1 is the first cursor to compare
350 *
351 * \a c2 is the second cursor to compare
352 *
353 * Returns \c true, if c1's and c2's assigned document, line and column are \c equal.
354 */
355 inline friend bool operator==(DocumentCursor c1, DocumentCursor c2)
356 {
357 return c1.document() == c2.document() && c1.line() == c2.line() && c1.column() == c2.column();
358 }
359
360 /*!
361 * Inequality operator.
362 *
363 * \a c1 is the first cursor to compare
364 *
365 * \a c2 is the second cursor to compare
366 *
367 * Returns \c true, if c1's and c2's assigned document, line and column are \c not equal.
368 */
369 inline friend bool operator!=(DocumentCursor c1, DocumentCursor c2)
370 {
371 return !(c1 == c2);
372 }
373
374 /*!
375 * Greater than operator.
376 *
377 * \a c1 is the first cursor to compare
378 *
379 * \a c2 is the second cursor to compare
380 *
381 * Returns \c true, if c1's position is greater than c2's position,
382 * otherwise \c false.
383 */
384 inline friend bool operator>(DocumentCursor c1, DocumentCursor c2)
385 {
386 return c1.line() > c2.line() || (c1.line() == c2.line() && c1.column() > c2.column());
387 }
388
389 /*!
390 * Greater than or equal to operator.
391 *
392 * \a c1 is the first cursor to compare
393 *
394 * \a c2 is the second cursor to compare
395 *
396 * Returns \c true, if c1's position is greater than or equal to c2's
397 * position, otherwise \c false.
398 */
399 inline friend bool operator>=(DocumentCursor c1, DocumentCursor c2)
400 {
401 return c1.line() > c2.line() || (c1.line() == c2.line() && c1.column() >= c2.column());
402 }
403
404 /*!
405 * Less than operator.
406 *
407 * \a c1 is the first cursor to compare
408 *
409 * \a c2 is the second cursor to compare
410 *
411 * Returns \c true, if c1's position is greater than or equal to c2's
412 * position, otherwise \c false.
413 */
414 inline friend bool operator<(DocumentCursor c1, DocumentCursor c2)
415 {
416 return !(c1 >= c2);
417 }
418
419 /*!
420 * Less than or equal to operator.
421 *
422 * \a c1 is the first cursor to compare
423 *
424 * \a c2 is the second cursor to compare
425 *
426 * Returns \c true, if c1's position is lesser than or equal to c2's
427 * position, otherwise \c false.
428 */
429 inline friend bool operator<=(DocumentCursor c1, DocumentCursor c2)
430 {
431 return !(c1 > c2);
432 }
433
434 /*!
435 * qDebug() stream operator. Writes this cursor to the debug output in a nicely formatted way.
436 *
437 * \a s is the debug stream
438 *
439 * \a cursor is the cursor to print
440 *
441 * Returns debug stream
442 */
443 inline friend QDebug operator<<(QDebug s, const DocumentCursor *cursor)
444 {
445 if (cursor) {
446 s.nospace() << "(" << cursor->document() << ": " << cursor->line() << ", " << cursor->column() << ")";
447 } else {
448 s.nospace() << "(null document cursor)";
449 }
450 return s.space();
451 }
452
453 /*!
454 * qDebug() stream operator. Writes this cursor to the debug output in a nicely formatted way.
455 *
456 * \a s is the debug stream
457 *
458 * \a cursor is the cursor to print
459 *
460 * Returns debug stream
461 */
462 inline friend QDebug operator<<(QDebug s, const DocumentCursor &cursor)
463 {
464 return s << &cursor;
465 }
466
467private:
468 KTextEditor::Document *m_document;
469 KTextEditor::Cursor m_cursor;
470};
471
472}
473
474Q_DECLARE_TYPEINFO(KTextEditor::DocumentCursor, Q_RELOCATABLE_TYPE);
475
476#endif
477

source code of ktexteditor/src/include/ktexteditor/documentcursor.h