KTextEditor

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

KDE's Doxygen guidelines are available online.