KTextEditor

documentcursor.cpp
1 /*
2  SPDX-FileCopyrightText: 2010 Christoph Cullmann <[email protected]>
3  SPDX-FileCopyrightText: 2012 Dominik Haumann <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "documentcursor.h"
9 
10 namespace KTextEditor
11 {
12 DocumentCursor::DocumentCursor(KTextEditor::Document *document)
13  : m_document(document)
14  , m_cursor(KTextEditor::Cursor::invalid())
15 {
16  // we require a valid document
17  Q_ASSERT(m_document);
18 }
19 
20 DocumentCursor::DocumentCursor(KTextEditor::Document *document, const KTextEditor::Cursor &position)
21  : m_document(document)
22  , m_cursor(position)
23 {
24  // we require a valid document
25  Q_ASSERT(m_document);
26 }
27 
28 DocumentCursor::DocumentCursor(KTextEditor::Document *document, int line, int column)
29  : m_document(document)
30  , m_cursor(line, column)
31 {
32  // we require a valid document
33  Q_ASSERT(m_document);
34 }
35 
36 DocumentCursor::DocumentCursor(const DocumentCursor &other)
37  : m_document(other.m_document)
38  , m_cursor(other.m_cursor)
39 {
40 }
41 
43 {
44  const int line = m_cursor.line();
45  const int col = m_cursor.line();
46 
47  if (line < 0) {
48  m_cursor.setPosition(0, 0);
49  } else if (line >= m_document->lines()) {
50  m_cursor = m_document->documentEnd();
51  } else if (col > m_document->lineLength(line)) {
52  m_cursor.setColumn(m_document->lineLength(line));
53  } else if (col < 0) {
54  m_cursor.setColumn(0);
55  } else if (!isValidTextPosition()) {
56  // inside a unicode surrogate (utf-32 character)
57  // -> move half one char left to the start of the utf-32 char
58  m_cursor.setColumn(col - 1);
59  }
60 
61  Q_ASSERT(isValidTextPosition());
62 }
63 
64 void DocumentCursor::setPosition(int line, int column)
65 {
66  m_cursor.setPosition(line, column);
67 }
68 
70 {
72 }
73 
74 void DocumentCursor::setColumn(int column)
75 {
77 }
78 
80 {
81  return isValidTextPosition() && column() == 0;
82 }
83 
85 {
86  return isValidTextPosition() && column() == document()->lineLength(line());
87 }
88 
90 {
91  return line() == 0 && column() == 0;
92 }
93 
95 {
96  // avoid costly lineLength computation if we are not in the last line
97  // this is called often e.g. during search & replace, >> 2% of the total costs
98  const auto lastLine = document()->lines() - 1;
99  return line() == lastLine && column() == document()->lineLength(lastLine);
100 }
101 
103 {
104  // only allow valid cursors
105  const bool ok = isValid() && (line() + 1 < document()->lines());
106 
107  if (ok) {
108  setPosition(Cursor(line() + 1, 0));
109  }
110 
111  return ok;
112 }
113 
115 {
116  // only allow valid cursors
117  bool ok = (line() > 0) && (column() >= 0);
118 
119  if (ok) {
120  setPosition(Cursor(line() - 1, 0));
121  }
122 
123  return ok;
124 }
125 
126 bool DocumentCursor::move(int chars, WrapBehavior wrapBehavior)
127 {
128  if (!isValid()) {
129  return false;
130  }
131 
132  // create temporary cursor to modify
133  Cursor c(m_cursor);
134 
135  // forwards?
136  if (chars > 0) {
137  // cache lineLength to minimize calls of KTextEditor::DocumentPrivate::lineLength(), as
138  // results in locating the correct block in the text buffer every time,
139  // which is relatively slow
140  int lineLength = document()->lineLength(c.line());
141 
142  // special case: cursor position is not in valid text, then the algo does
143  // not work for Wrap mode. Hence, catch this special case by setting
144  // c.column() to the lineLength()
145  if (wrapBehavior == Wrap && c.column() > lineLength) {
146  c.setColumn(lineLength);
147  }
148 
149  while (chars != 0) {
150  if (wrapBehavior == Wrap) {
151  const int advance = qMin(lineLength - c.column(), chars);
152 
153  if (chars > advance) {
154  if (c.line() + 1 >= document()->lines()) {
155  return false;
156  }
157 
158  c.setPosition(c.line() + 1, 0);
159  chars -= advance + 1; // +1 because of end-of-line wrap
160 
161  // advanced one line, so cache correct line length again
162  lineLength = document()->lineLength(c.line());
163  } else {
164  c.setColumn(c.column() + chars);
165  chars = 0;
166  }
167  } else { // NoWrap
168  c.setColumn(c.column() + chars);
169  chars = 0;
170  }
171  }
172  }
173 
174  // backwards?
175  else {
176  while (chars != 0) {
177  const int back = qMin(c.column(), -chars);
178  if (-chars > back) {
179  if (c.line() == 0) {
180  return false;
181  }
182 
183  c.setPosition(c.line() - 1, document()->lineLength(c.line() - 1));
184  chars += back + 1; // +1 because of wrap-around at start-of-line
185  } else {
186  c.setColumn(c.column() + chars);
187  chars = 0;
188  }
189  }
190  }
191 
192  if (c != m_cursor) {
193  setPosition(c);
194  }
195  return true;
196 }
197 
198 }
void setPosition(KTextEditor::Cursor position)
Set the current cursor position to position.
void setColumn(int column)
Set the cursor column to column.
bool gotoNextLine()
Moves the cursor to the next line and sets the column to 0.
constexpr int column() const Q_DECL_NOEXCEPT
Retrieve the column on which this cursor is situated.
Definition: cursor.h:215
bool move(int chars, WrapBehavior wrapBehavior=Wrap)
Moves the cursor chars character forward or backwards.
WrapBehavior
Wrap behavior for end of line treatement used in move().
virtual int lines() const =0
Get the count of lines of the document.
A Cursor which is bound to a specific Document.
bool gotoPreviousLine()
Moves the cursor to the previous line and sets the column to 0.
Document * document() const
Gets the document to which this cursor is bound.
virtual Cursor documentEnd() const =0
End position of the document.
void makeValid()
Make sure the cursor position is at a valid text position according to the following rules.
The Cursor represents a position in a Document.
Definition: cursor.h:71
@ Wrap
wrap at end of line
int lastLine() const
gets the last line number (lines() - 1)
Definition: katedocument.h:822
virtual int lineLength(int line) const =0
Get the length of a given line in characters.
void setColumn(int column) Q_DECL_NOEXCEPT
Set the cursor column to column.
Definition: cursor.h:224
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
Definition: katetextblock.h:22
void setPosition(const Cursor &position) Q_DECL_NOEXCEPT
Set the current cursor position to position.
Definition: cursor.h:173
bool isValid() const
Check if the current position of this cursor is a valid position, i.e.
bool atStartOfLine() const
Determine if this cursor is located at column 0 of a valid text line.
bool atStartOfDocument() const
Determine if this cursor is located at line 0 and column 0.
int column() const
Retrieve the column on which this cursor is situated.
void setLine(int line)
Set the cursor line to line.
int line() const
Retrieve the line on which this cursor is situated.
bool atEndOfLine() const
Determine if this cursor is located at the end of the current line.
constexpr int line() const Q_DECL_NOEXCEPT
Retrieve the line on which this cursor is situated.
Definition: cursor.h:197
bool atEndOfDocument() const
Determine if this cursor is located at the end of the last line in the document.
bool isValidTextPosition() const
Check if this cursor is currently at a valid text position.
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 Mon May 8 2023 03:50:21 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.