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