KTextEditor

documentcursor.cpp
1 /*
2  SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org>
3  SPDX-FileCopyrightText: 2012 Dominik Haumann <dhaumann@kde.org>
4 
5  SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "documentcursor.h"
9 
10 namespace KTextEditor
11 {
13  : m_document(document)
14  , m_cursor(KTextEditor::Cursor::invalid())
15 {
16  // we require a valid document
17  Q_ASSERT(m_document);
18 }
19 
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 
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.
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.
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
Definition: cursor.h:174
void setPosition(Cursor position) noexcept
Set the current cursor position to position.
Definition: cursor.h:150
A Cursor which is bound to a specific Document.
bool gotoPreviousLine()
Moves the cursor to the previous line and sets the column to 0.
void setColumn(int column) noexcept
Set the cursor column to column.
Definition: cursor.h:201
DocumentCursor()=delete
no default constructor, as we need a document.
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:74
@ Wrap
wrap at end of line
int lastLine() const
gets the last line number (lines() - 1)
Definition: katedocument.h:758
virtual int lineLength(int line) const =0
Get the length of a given line in characters.
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
Definition: katetextblock.h:19
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.
bool atEndOfDocument() const
Determine if this cursor is located at the end of the last line in the document.
constexpr int column() const noexcept
Retrieve the column on which this cursor is situated.
Definition: cursor.h:192
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:283
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 03:50:42 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.