KTextEditor

katetextrange.cpp
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#include "katetextrange.h"
11#include "katedocument.h"
12#include "katetextbuffer.h"
13
14namespace Kate
15{
17 : m_buffer(buffer)
18 , m_start(buffer, this, range.start(), (insertBehavior & ExpandLeft) ? Kate::TextCursor::StayOnInsert : Kate::TextCursor::MoveOnInsert)
19 , m_end(buffer, this, range.end(), (insertBehavior & ExpandRight) ? Kate::TextCursor::MoveOnInsert : Kate::TextCursor::StayOnInsert)
20 , m_view(nullptr)
21 , m_feedback(nullptr)
22 , m_zDepth(0.0)
23 , m_attributeOnlyForViews(false)
24 , m_invalidateIfEmpty(emptyBehavior == InvalidateIfEmpty)
25{
26 // check if range now invalid, there can happen no feedback, as m_feedback == 0
27 // only place where KTextEditor::LineRange::invalid() for old range makes sense, as we were yet not registered!
28 checkValidity();
29
30 // Add the range if it is multiline
31 if (spansMultipleBlocks()) {
32 m_buffer->addMultilineRange(this);
33 }
34}
35
37{
38 if (!m_buffer) {
39 return;
40 }
41 // reset feedback, don't want feedback during destruction
42 const bool hadFeedBack = m_feedback != nullptr;
43 const bool hadDynamicAttr = m_attribute
44 && (m_attribute->dynamicAttribute(KTextEditor::Attribute::ActivateCaretIn) || m_attribute->dynamicAttribute(KTextEditor::Attribute::ActivateMouseIn));
45 const bool notifyDeletion = hadFeedBack || hadDynamicAttr;
46 m_feedback = nullptr;
47
48 // remove range from cached multiline ranges
49 const auto lineRange = toLineRange();
50 if (lineRange.isValid() && spansMultipleBlocks()) {
51 m_buffer->removeMultilineRange(this);
52 }
53
54 // trigger update, if we have attribute
55 // notify right view
56 // here we can ignore feedback, even with feedback, we want none if the range is deleted!
57 if (m_attribute || notifyDeletion) {
58 m_buffer->notifyAboutRangeChange(m_view, lineRange, /*needsRepaint=*/m_attribute != nullptr, /*deletedRange=*/notifyDeletion ? this : nullptr);
59 }
60}
61
63{
64 // nothing to do?
65 if (_insertBehaviors == insertBehaviors() || !m_buffer) {
66 return;
67 }
68
69 // modify cursors
72
73 // notify world
74 if (m_attribute || m_feedback) {
75 m_buffer->notifyAboutRangeChange(m_view, toLineRange(), true /* we have a attribute */);
76 }
77}
78
80{
81 InsertBehaviors behaviors = DoNotExpand;
82
84 behaviors |= ExpandLeft;
85 }
86
88 behaviors |= ExpandRight;
89 }
90
91 return behaviors;
92}
93
95{
96 // nothing to do?
97 if (m_invalidateIfEmpty == (emptyBehavior == InvalidateIfEmpty)) {
98 return;
99 }
100
101 // remember value
102 m_invalidateIfEmpty = (emptyBehavior == InvalidateIfEmpty);
103
104 // invalidate range?
105 if (endInternal() <= startInternal()) {
107 }
108}
109
111{
112 // avoid work if nothing changed!
113 if (!m_buffer || range == toRange()) {
114 return;
115 }
116
117 // remember old line range
118 const auto oldLineRange = toLineRange();
119 const bool oldSpannedMultipleBlocks = spansMultipleBlocks();
120
121 // change start and end cursor
122 m_start.setPosition(range.start());
123 m_end.setPosition(range.end());
124
125 const bool newSpansMultipleBlocks = spansMultipleBlocks();
126 if (oldSpannedMultipleBlocks != newSpansMultipleBlocks) {
127 if (oldSpannedMultipleBlocks) {
128 m_buffer->removeMultilineRange(this);
129 } else {
130 m_buffer->addMultilineRange(this);
131 }
132 }
133
134 // check if range now invalid, don't emit feedback here, will be handled below
135 // otherwise you can't delete ranges in feedback!
136 checkValidity(false);
137
138 // no attribute or feedback set, be done
139 if (!m_attribute && !m_feedback) {
140 return;
141 }
142
143 // get full range
144 int startLineMin = oldLineRange.start();
145 if (oldLineRange.start() == -1 || (m_start.lineInternal() != -1 && m_start.lineInternal() < oldLineRange.start())) {
146 startLineMin = m_start.line();
147 }
148
149 int endLineMax = oldLineRange.end();
150 if (oldLineRange.end() == -1 || m_end.lineInternal() > oldLineRange.end()) {
151 endLineMax = m_end.lineInternal();
152 }
153
154 // notify buffer about attribute change, it will propagate the changes
155 // notify right view
156 m_buffer->notifyAboutRangeChange(m_view, {startLineMin, endLineMax}, m_attribute);
157
158 // perhaps need to notify stuff!
159 if (m_feedback) {
160 // do this last: may delete this range
161 if (!toRange().isValid()) {
162 m_feedback->rangeInvalid(this);
163 } else if (toRange().isEmpty()) {
164 m_feedback->rangeEmpty(this);
165 }
166 }
167}
168
170{
171 // FIXME: optimize
172 setRange(range);
174}
175
177{
178 // FIXME: optimize
179 setRange(range);
182}
183
184void TextRange::checkValidity(bool notifyAboutChange)
185{
186 // in any case: reset the flag, to avoid multiple runs
187 m_isCheckValidityRequired = false;
188
191 // check if any cursor is invalid or the range is zero size and it should be invalidated then
192 if (!start.isValid() || !end.isValid() || (m_invalidateIfEmpty && end <= start)) {
193 m_start.setPosition(-1, -1);
194 m_end.setPosition(-1, -1);
196 }
197
198 // for ranges which are allowed to become empty, normalize them, if the end has moved to the front of the start
199 if (!m_invalidateIfEmpty && end < start) {
200 m_end.setPosition(m_start);
201 }
202
203 // perhaps need to notify stuff!
204 if (notifyAboutChange && m_feedback && m_buffer) {
205 m_buffer->notifyAboutRangeChange(m_view, toLineRange(), false /* attribute not interesting here */);
206
207 // do this last: may delete this range
208 if (!toRange().isValid()) {
209 m_feedback->rangeInvalid(this);
210 } else if (toRange().isEmpty()) {
211 m_feedback->rangeEmpty(this);
212 }
213 }
214}
215
217{
218 // nothing changes, nop
219 if (view == m_view || !m_buffer) {
220 return;
221 }
222
223 // remember the new attribute
224 m_view = view;
225
226 // notify buffer about attribute change, it will propagate the changes
227 // notify all views (can be optimized later)
228 if (m_attribute || m_feedback) {
229 m_buffer->notifyAboutRangeChange(nullptr, toLineRange(), m_attribute);
230 }
231}
232
234{
235 // nothing changes, nop, only pointer compare
236 if (attribute == m_attribute || !m_buffer) {
237 return;
238 }
239
240 // remember the new attribute
241 const bool hadDynamicAttr = m_attribute
242 && (m_attribute->dynamicAttribute(KTextEditor::Attribute::ActivateCaretIn) || m_attribute->dynamicAttribute(KTextEditor::Attribute::ActivateMouseIn));
243
244 m_attribute = attribute;
245
246 const bool stillHasDynAttr = m_attribute
247 && (m_attribute->dynamicAttribute(KTextEditor::Attribute::ActivateCaretIn) || m_attribute->dynamicAttribute(KTextEditor::Attribute::ActivateMouseIn));
248 const bool notifyDeletion = hadDynamicAttr && !stillHasDynAttr;
249
250 // notify buffer about attribute change, it will propagate the changes
251 // notify right view
252 // if dyn attr was cleared, then notify about deleted range now because if this range is deleted
253 // its deletion will not be notified because it will have no dyn attr.
254 m_buffer->notifyAboutRangeChange(m_view,
255 toLineRange(),
256 true /* even for nullptr attribute, we had before one => repaint */,
257 notifyDeletion ? this : nullptr);
258}
259
261{
262 // nothing changes, nop
263 if (feedback == m_feedback || !m_buffer) {
264 return;
265 }
266
267 // remember the new feedback object
268 const bool feedbackCleared = m_feedback && !feedback;
269 m_feedback = feedback;
270
271 // notify buffer about feedback change, it will propagate the changes
272 // notify right view
273 // if feedback was set to null, then notify about deleted range now because if this range is deleted
274 // its deletion will not be notified because it has no feedback.
275 m_buffer->notifyAboutRangeChange(m_view, toLineRange(), m_attribute, feedbackCleared ? this : nullptr);
276}
277
279{
280 // just set the value, no need to trigger updates, printing is not interruptable
281 m_attributeOnlyForViews = onlyForViews;
282}
283
284void TextRange::setZDepth(qreal zDepth)
285{
286 // nothing changes, nop
287 if (zDepth == m_zDepth || !m_buffer) {
288 return;
289 }
290
291 // remember the new attribute
292 m_zDepth = zDepth;
293
294 // notify buffer about attribute change, it will propagate the changes
295 if (m_attribute) {
296 m_buffer->notifyAboutRangeChange(m_view, toLineRange(), m_attribute);
297 }
298}
299
301{
302 return m_buffer ? m_buffer->document() : nullptr;
303}
304
305}
@ ActivateMouseIn
Activate attribute on mouse in.
Definition attribute.h:246
@ ActivateCaretIn
Activate attribute on caret in.
Definition attribute.h:248
The Cursor represents a position in a Document.
Definition cursor.h:75
static constexpr Cursor invalid() noexcept
Returns an invalid cursor.
Definition cursor.h:112
A KParts derived class representing a text document.
Definition document.h:284
bool isValid() const
Returns whether the current position of this cursor is a valid position, i.e.
@ StayOnInsert
stay on insert
@ MoveOnInsert
move on insert
A class which provides notifications of state changes to a MovingRange.
virtual void rangeInvalid(MovingRange *range)
The range is now invalid (ie.
virtual void rangeEmpty(MovingRange *range)
The range is now empty (ie.
bool isEmpty() const
Returns true if this range contains no characters, ie.
EmptyBehavior
Behavior of range if it becomes empty.
@ InvalidateIfEmpty
invalidate range, if it becomes empty
@ DoNotExpand
Don't expand to encapsulate new characters in either direction. This is the default.
@ ExpandRight
Expand to encapsulate new characters to the right of the range.
@ ExpandLeft
Expand to encapsulate new characters to the left of the range.
An object representing a section of text, from one Cursor to another.
constexpr Cursor end() const noexcept
Get the end position of this range.
constexpr Cursor start() const noexcept
Get the start position of this range.
static constexpr Range invalid() noexcept
Returns an invalid range.
A text widget with KXMLGUIClient that represents a Document.
Definition view.h:244
Class representing a text buffer.
KTEXTEDITOR_NO_EXPORT void addMultilineRange(TextRange *range)
Add/Remove a multiline range that spans multiple blocks.
Class representing a 'clever' text cursor.
int lineInternal() const
Non-virtual version of line(), which is faster.
InsertBehavior insertBehavior() const override
Get current insert behavior.
void setInsertBehavior(InsertBehavior insertBehavior) override
Set insert behavior.
void setPosition(const TextCursor &position)
Fast way to set the current cursor position to position.
int columnInternal() const
Non-virtual version of column(), which is faster.
int line() const override
Retrieve the line on which this cursor is situated.
qreal zDepth() const override
Gets the current Z-depth of this range.
void setAttribute(KTextEditor::Attribute::Ptr attribute) override
Sets the currently active attribute for this range.
~TextRange() override
Destruct the text block.
KTextEditor::MovingRangeFeedback * feedback() const override
Gets the active MovingRangeFeedback for this range.
const TextCursor & startInternal() const
Non-virtual version of start(), which is faster.
KTextEditor::Document * document() const override
Gets the document to which this range is bound.
const KTextEditor::MovingCursor & end() const override
Retrieve end cursor of this range, read-only.
const KTextEditor::Attribute::Ptr & attribute() const override
Gets the active Attribute for this range.
void setZDepth(qreal zDepth) override
Set the current Z-depth of this range.
InsertBehaviors insertBehaviors() const override
Get current insert behaviors.
void setAttributeOnlyForViews(bool onlyForViews) override
Set if this range's attribute is only visible in views, not for example prints.
KTextEditor::View * view() const override
Gets the active view for this range.
void setFeedback(KTextEditor::MovingRangeFeedback *feedback) override
Sets the currently active MovingRangeFeedback for this range.
const KTextEditor::Range toRange() const
Convert this clever range into a dumb one.
void setView(KTextEditor::View *view) override
Sets the currently active view for this range.
void setInsertBehaviors(InsertBehaviors insertBehaviors) override
Set insert behaviors.
const TextCursor & endInternal() const
Nonvirtual version of end(), which is faster.
EmptyBehavior emptyBehavior() const override
Will this range invalidate itself if it becomes empty?
KTextEditor::LineRange toLineRange() const
Hides parent's impl of toLineRange() and uses non-virtual functions internally.
void setEmptyBehavior(EmptyBehavior emptyBehavior) override
Set if this range will invalidate itself if it becomes empty.
void setRange(KTextEditor::Range range) override
Set the range of this range.
TextRange(TextBuffer *buffer, KTextEditor::Range range, InsertBehaviors insertBehavior, EmptyBehavior emptyBehavior=AllowEmpty)
Construct a text range.
Q_SCRIPTABLE Q_NOREPLY void start()
bool isValid(QStringView ifopt)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:11:26 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.