1/*
2 SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org>
3
4 Based on code of the SmartCursor/Range by:
5 SPDX-FileCopyrightText: 2003-2005 Hamish Rodda <rodda@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#ifndef KTEXTEDITOR_MOVINGRANGE_H
11#define KTEXTEDITOR_MOVINGRANGE_H
12
13#include <ktexteditor/attribute.h>
14#include <ktexteditor/linerange.h>
15#include <ktexteditor/movingcursor.h>
16#include <ktexteditor/range.h>
17#include <ktexteditor_export.h>
18
19class QDebug;
20
21namespace KTextEditor
22{
23class Document;
24class View;
25class MovingRangeFeedback;
26
27/*!
28 * \class KTextEditor::MovingRange
29 * \inmodule KTextEditor
30 * \inheaderfile KTextEditor/MovingRange
31 *
32 * \brief A range that is bound to a specific Document, and maintains its
33 * position.
34 *
35 * \ingroup kte_group_moving_classes
36 *
37 * \target movingrange_intro
38 * \section1 Introduction
39 *
40 * A MovingRange is an extension of the basic Range class. It maintains its
41 * position in the document. As a result of this, MovingRange%s may not be
42 * copied, as they need to maintain a connection to the associated Document.
43 *
44 * Create a new MovingRange like this:
45 * \code
46 * auto* movingRange = aDocument->newMovingRange(range);
47 * \endcode
48 *
49 * The ownership of the range is passed to the user. When finished with a MovingRange,
50 * simply delete it.
51 *
52 * \target movingrange_behavior
53 * \section1 Editing Behavior
54 *
55 * The insert behavior controls how the range reacts to characters inserted
56 * at the range boundaries, i.e. at the start of the range or the end of the
57 * range. Either the range boundary moves with text insertion, or it stays.
58 * Use setInsertBehaviors() and insertBehaviors() to set and query the current
59 * insert behavior.
60 *
61 * When the start() and end() Cursor of a range equal, isEmpty() returns true.
62 * Further, the empty-behavior can be changed such that the start() and end()
63 * Cursor%s of MovingRange%s that get empty are automatically set to (-1, -1).
64 * Use setEmptyBehavior() and emptyBehavior() to control the empty behavior.
65 *
66 * \warning MovingRanges may be set to (-1, -1, -1, -1) at any time, if the
67 * user reloads a document (F5)! Use a MovingRangeFeedback to get notified
68 * if you need to catch this case, and/or listen to the signal
69 * Document::aboutToInvalidateMovingInterfaceContent().
70 *
71 * \target movingrange_feedback
72 * \section1 MovingRange Feedback
73 *
74 * With setFeedback() a feedback instance can be associated with the moving
75 * range. The MovingRangeFeedback notifies about the following events:
76 * \list
77 * \li the text cursor (caret) entered the range,
78 * \li the text cursor (caret) left the range,
79 * \li the mouse cursor entered the range,
80 * \li the mouse cursor left the range,
81 * \li the range became empty, i.e. start() == end(),
82 * \li the range became invalid, i.e. start() == end() == (-1, -1).
83 * \endlist
84 *
85 * If a feedback is not needed anymore, call setFeedback(0).
86 *
87 * \target movingrange_details
88 * \section1 Working with Ranges
89 *
90 * There are several convenience methods that make working with MovingRanges
91 * very simple. For instance, use isEmpty() to check if the start() Cursor
92 * equals the end() Cursor. Use contains(), containsLine() or containsColumn()
93 * to check whether the MovingRange contains a Range, a Cursor, a line or
94 * column. The same holds for overlaps(), overlapsLine() and overlapsColumn().
95 * Besides onSingleLine() returns whether a MovingRange spans only one line.
96 *
97 * For compatibility, a MovingRange can be explicitly converted to a simple
98 * Range by calling toRange(), or implicitly by the Range operator.
99 *
100 * \target movingrange_highlighting
101 * \section1 Arbitrary Highlighting
102 *
103 * With setAttribute() highlighting Attribute%s can be assigned to a
104 * MovingRange. By default, this highlighting is used in all views of a
105 * document. Use setView(), if the highlighting should only appear in a
106 * specific view. Further, if the additional highlighting should not be
107 * printed call setAttributeOnlyForViews() with the parameter true.
108 *
109 * \target movingrange_example
110 * \section1 MovingRange Example
111 *
112 * In the following example, we assume the KTextEditor::Document has the
113 * contents:
114 * \code
115 * void printText(const std::string & text); // this is line 3
116 * \endcode
117 * In order to highlight the function name \e printText with a yellow background
118 * color, the following code is needed:
119 * \code
120 * KTextEditor::View * view = ...;
121 * KTextEditor::Document * doc = view->document();
122 *
123 * // range is of type KTextEditor::MovingRange*
124 * auto range = doc->newMovingRange(KTextEditor::Range(3, 5, 3, 14));
125 *
126 * KTextEditor::Attribute::Ptr attrib = new KTextEditor::Attribute();
127 * attrib->setBackground(Qt::yellow);
128 *
129 * range->setAttribute(attrib);
130 * \endcode
131 *
132 * MovingRange%s are invalidated automatically when a document is cleared or closed.
133 *
134 * \sa Cursor, MovingCursor, Range, MovingRangeFeedback, Document
135 *
136 * \since 4.5
137 */
138class KTEXTEDITOR_EXPORT MovingRange
139{
140 //
141 // sub types
142 //
143public:
144 /*!
145 \enum KTextEditor::MovingRange::InsertBehavior
146
147 \brief Determines how the range reacts to characters inserted immediately outside the range.
148
149 \value DoNotExpand
150 Don't expand to encapsulate new characters in either direction. This is the default.
151
152 \value ExpandLeft
153 Expand to encapsulate new characters to the left of the range.
154
155 \value ExpandRight
156 Expand to encapsulate new characters to the right of the range.
157
158 \sa InsertBehaviors
159 */
160 enum InsertBehavior {
161 DoNotExpand = 0x0,
162 ExpandLeft = 0x1,
163 ExpandRight = 0x2
164 };
165 Q_DECLARE_FLAGS(InsertBehaviors, InsertBehavior)
166
167 /*!
168 \enum KTextEditor::MovingRange::EmptyBehavior
169
170 Behavior of range if it becomes empty.
171
172 \value AllowEmpty
173 Allow range to be empty
174 \value InvalidateIfEmpty
175 Invalidate range if it becomes empty
176 */
177 enum EmptyBehavior {
178 AllowEmpty = 0x0,
179 InvalidateIfEmpty = 0x1
180 };
181
182 //
183 // stuff that needs to be implemented by editor part cursors
184 //
185public:
186 /*!
187 * Set insert behaviors.
188 *
189 * \a insertBehaviors are the new insert behaviors
190 *
191 */
192 virtual void setInsertBehaviors(InsertBehaviors insertBehaviors) = 0;
193
194 /*!
195 * Get current insert behaviors.
196 * Returns current insert behaviors
197 */
198 virtual InsertBehaviors insertBehaviors() const = 0;
199
200 /*!
201 * Set if this range will invalidate itself if it becomes empty.
202 *
203 * \a emptyBehavior is the behavior on becoming empty
204 *
205 */
206 virtual void setEmptyBehavior(EmptyBehavior emptyBehavior) = 0;
207
208 /*!
209 * Will this range invalidate itself if it becomes empty?
210 * Returns behavior on becoming empty
211 */
212 virtual EmptyBehavior emptyBehavior() const = 0;
213
214 /*!
215 * Gets the document to which this range is bound.
216 *
217 * Returns a pointer to the document
218 */
219 virtual Document *document() const = 0;
220
221 /*!
222 * Set the range of this range.
223 *
224 * A TextRange is not allowed to be empty, as soon as start == end position, it will become
225 * automatically invalid!
226 *
227 * \a range new range for this clever range
228 *
229 */
230 virtual void setRange(KTextEditor::Range range) = 0;
231
232 /*!
233 * Set the range of this range and the connected attribute.
234 * Avoids internal overhead of separate setting that.
235 *
236 * A TextRange is not allowed to be empty, as soon as start == end position, it will become
237 * automatically invalid!
238 *
239 * \a range is the new range for this clever range
240 *
241 * \a attribute is the attribute to assign to this range. If null, simply removes the previous Attribute.
242 *
243 * \since 6.0
244 */
245 virtual void setRange(KTextEditor::Range range, Attribute::Ptr attribute) = 0;
246
247 /*!
248 * Set the range of this range and the connected attribute and Z-depth.
249 * Avoids internal overhead of separate setting that.
250 *
251 * A TextRange is not allowed to be empty, as soon as start == end position, it will become
252 * automatically invalid!
253 *
254 * \a range is the new range for this clever range
255 *
256 * \a attribute is the attribute to assign to this range. If null, simply removes the previous Attribute.
257 *
258 * \a zDepth is the new Z-depth of this range
259 *
260 * \since 6.0
261 */
262 virtual void setRange(KTextEditor::Range range, Attribute::Ptr attribute, qreal zDepth) = 0;
263
264 /*!
265 * Retrieve start cursor of this range, read-only.
266 * Returns start cursor
267 */
268 virtual const MovingCursor &start() const = 0;
269
270 /*!
271 * Retrieve end cursor of this range, read-only.
272 * Returns end cursor
273 */
274 virtual const MovingCursor &end() const = 0;
275
276 /*!
277 * Gets the active view for this range. Might be already invalid, internally only used for pointer comparisons.
278 *
279 * Returns a pointer to the active view
280 */
281 virtual View *view() const = 0;
282
283 /*!
284 * Sets the currently active view for this range.
285 * This will trigger update of the relevant view parts, if the view changed.
286 * Set view before the attribute, that will avoid not needed redraws.
287 *
288 * \a view is the View to assign to this range. If null, simply
289 * removes the previous view.
290 *
291 */
292 virtual void setView(View *view) = 0;
293
294 /*!
295 * Gets the active Attribute for this range.
296 *
297 * Returns a pointer to the active attribute
298 */
299 virtual const Attribute::Ptr &attribute() const = 0;
300
301 /*!
302 * Sets the currently active attribute for this range.
303 * This will trigger update of the relevant view parts, if the attribute changed.
304 *
305 * \a attribute is the attribute to assign to this range. If null, simply
306 * removes the previous Attribute.
307 *
308 */
309 virtual void setAttribute(Attribute::Ptr attribute) = 0;
310
311 /*!
312 * Is this range's attribute only visible in views, not for example prints?
313 * Default is false.
314 * Returns range visible only for views
315 */
316 virtual bool attributeOnlyForViews() const = 0;
317
318 /*!
319 * Set if this range's attribute is only visible in views, not for example prints.
320 *
321 * \a onlyForViews if true, the attribute is only valid for views
322 *
323 */
324 virtual void setAttributeOnlyForViews(bool onlyForViews) = 0;
325
326 /*!
327 * Gets the active MovingRangeFeedback for this range.
328 *
329 * Returns a pointer to the active MovingRangeFeedback
330 */
331 virtual MovingRangeFeedback *feedback() const = 0;
332
333 /*!
334 * Sets the currently active MovingRangeFeedback for this range.
335 * This will trigger evaluation if feedback must be send again (for example if mouse is already inside range).
336 *
337 * \a feedback is the MovingRangeFeedback to assign to this range. If null, simply
338 * removes the previous MovingRangeFeedback.
339 *
340 */
341 virtual void setFeedback(MovingRangeFeedback *feedback) = 0;
342
343 /*!
344 * Gets the current Z-depth of this range.
345 * Ranges with smaller Z-depth than others will win during rendering.
346 * Default is 0.0.
347 *
348 * Defined depths for common kind of ranges use in editor components implementing this interface,
349 * smaller depths are more more in the foreground and will win during rendering:
350 * \list
351 * \li Selection == -100000.0
352 * \li Search == -10000.0
353 * \li Bracket Highlighting == -1000.0
354 * \li Folding Hover == -100.0
355 * \endlist
356 *
357 * Returns current Z-depth of this range
358 */
359 virtual qreal zDepth() const = 0;
360
361 /*!
362 * Set the current Z-depth of this range.
363 * Ranges with smaller Z-depth than others will win during rendering.
364 * This will trigger update of the relevant view parts, if the depth changed.
365 * Set depth before the attribute, that will avoid not needed redraws.
366 * Default is 0.0.
367 *
368 * \a zDepth is new Z-depth of this range
369 *
370 */
371 virtual void setZDepth(qreal zDepth) = 0;
372
373 /*!
374 * Destruct the moving range.
375 */
376 virtual ~MovingRange();
377
378 //
379 // forbidden stuff
380 //
381protected:
382 /*!
383 * For inherited class only.
384 */
385 MovingRange();
386
387public:
388 /*!
389 * no copy constructor, don't allow this to be copied.
390 */
391 MovingRange(const MovingRange &) = delete;
392
393 /*!
394 * no assignment operator, no copying around clever ranges.
395 */
396 MovingRange &operator=(const MovingRange &) = delete;
397
398 //
399 // convenience API
400 //
401public:
402 /*!
403 * \overload
404 * Set the range of this range
405 * A TextRange is not allowed to be empty, as soon as start == end position, it will become
406 * automatically invalid!
407 *
408 * \a start is the new start for this clever range
409 *
410 * \a end is the new end for this clever range
411 *
412 */
413 void setRange(Cursor start, Cursor end);
414
415 /*!
416 * Convert this clever range into a dumb one.
417 * Returns normal range
418 */
419 const Range toRange() const
420 {
421 return Range(start().toCursor(), end().toCursor());
422 }
423
424 /*!
425 * Convert this clever range into a dumb one. Equal to toRange, allowing to use implicit conversion.
426 * Returns normal range
427 */
428 operator Range() const
429 {
430 return Range(start().toCursor(), end().toCursor());
431 }
432
433 /*!
434 * Convert this MovingRange to a simple LineRange.
435 * Returns LineRange from the start line to the end line of this range.
436 */
437 inline LineRange toLineRange() const Q_DECL_NOEXCEPT
438 {
439 return {start().line(), end().line()};
440 }
441
442 /*!
443 * Returns true if this range contains no characters, ie. the start() and
444 * end() positions are the same.
445 *
446 * Returns \e true if the range contains no characters, otherwise \e false
447 */
448 inline bool isEmpty() const
449 {
450 return start() == end();
451 }
452
453 // BEGIN comparison functions
454 /*
455 * \name Comparison
456 *
457 * The following functions perform checks against this range in comparison
458 * to other lines, columns, cursors, and ranges.
459 */
460 /*!
461 * Check whether the this range wholly encompasses \e range.
462 *
463 * \a range is the range to check
464 *
465 * Returns \e true, if this range contains \e range, otherwise \e false
466 */
467 inline bool contains(const Range &range) const
468 {
469 return range.start() >= start() && range.end() <= end();
470 }
471
472 /*!
473 * Check to see if \a cursor is contained within this range, ie >= start() and \< end().
474 *
475 * \a cursor is the position to test for containment
476 *
477 * Returns \e true if the cursor is contained within this range, otherwise \e false.
478 */
479 inline bool contains(Cursor cursor) const
480 {
481 return cursor >= start() && cursor < end();
482 }
483
484 /*!
485 * Returns true if this range wholly encompasses \a line.
486 *
487 * \a line is the line to check
488 *
489 * Returns \e true if the line is wholly encompassed by this range, otherwise \e false.
490 */
491 inline bool containsLine(int line) const
492 {
493 return (line > start().line() || (line == start().line() && !start().column())) && line < end().line();
494 }
495
496 /*!
497 * Check whether the range contains \e column.
498 *
499 * \a column is the column to check
500 *
501 * Returns \e true if the range contains \e column, otherwise \e false
502 */
503 inline bool containsColumn(int column) const
504 {
505 return column >= start().column() && column < end().column();
506 }
507
508 /*!
509 * Check whether the this range overlaps with \e range.
510 *
511 * \a range is the range to check against
512 *
513 * Returns \e true, if this range overlaps with \e range, otherwise \e false
514 */
515 bool overlaps(const Range &range) const;
516
517 /*!
518 * Check whether the range overlaps at least part of \e line.
519 *
520 * \a line is the line to check
521 *
522 * Returns \e true, if the range overlaps at least part of \e line, otherwise \e false
523 */
524 inline bool overlapsLine(int line) const
525 {
526 return line >= start().line() && line <= end().line();
527 }
528
529 /*!
530 * Check to see if this range overlaps \a column; that is, if \a column is
531 * between start().column() and end().column(). This function is most likely
532 * to be useful in relation to block text editing.
533 *
534 * \a column is the column to test
535 *
536 * Returns \e true if the column is between the range's starting and ending
537 * columns, otherwise \e false.
538 */
539 inline bool overlapsColumn(int column) const
540 {
541 return start().column() <= column && end().column() > column;
542 }
543
544 /*!
545 * Check whether the start() and end() cursors of this range
546 * are on the same line.
547 *
548 * Returns \e true if both the start and end positions are on the same
549 * line, otherwise \e false
550 */
551 inline bool onSingleLine() const
552 {
553 return start().line() == end().line();
554 }
555
556 /*!
557 * Returns the number of lines separating the start() and end() positions.
558 *
559 * Returns the number of lines separating the start() and end() positions;
560 * 0 if the start and end lines are the same.
561 */
562 inline int numberOfLines() const Q_DECL_NOEXCEPT
563 {
564 return end().line() - start().line();
565 }
566
567 // END comparison functions
568};
569
570Q_DECLARE_OPERATORS_FOR_FLAGS(MovingRange::InsertBehaviors)
571
572/*!
573 * qDebug() stream operator. Writes this range to the debug output in a nicely formatted way.
574 *
575 * \a s is the debug stream
576 *
577 * \a range is the range to print
578 *
579 * Returns debug stream
580 *
581 * \relates KTextEditor::MovingRange
582 */
583KTEXTEDITOR_EXPORT QDebug operator<<(QDebug s, const MovingRange *range);
584
585/*!
586 * qDebug() stream operator. Writes this range to the debug output in a nicely formatted way.
587 *
588 * \a s is the debug stream
589 *
590 * \a range is the range to print
591 *
592 * Returns debug stream
593 *
594 * \relates KTextEditor::MovingRange
595 */
596KTEXTEDITOR_EXPORT QDebug operator<<(QDebug s, const MovingRange &range);
597}
598
599#endif
600

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