KTextEditor

kateviewaccessible.h
1/*
2 SPDX-FileCopyrightText: 2010 Sebastian Sauer <mail@dipe.org>
3 SPDX-FileCopyrightText: 2012 Frederik Gladhorn <gladhorn@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#ifndef _KATE_VIEW_ACCESSIBLE_
9#define _KATE_VIEW_ACCESSIBLE_
10
11#ifndef QT_NO_ACCESSIBILITY
12
13#include "katedocument.h"
14#include "kateview.h"
15#include "kateviewinternal.h"
16
17#include <KLocalizedString>
18#include <QAccessible>
19#include <QAccessibleWidget>
20#include <QTextBoundaryFinder>
21
22/**
23 * This class implements a QAccessible-interface for a KateViewInternal.
24 *
25 * This is the root class for the kateview. The \a KateCursorAccessible class
26 * represents the cursor in the kateview and is a child of this class.
27 */
29{
30public:
31 explicit KateViewAccessible(KateViewInternal *view)
33 , m_lastPosition(-1)
34 {
35 // to invalidate positionFromCursor cache when the document is changed
36 m_conn = QObject::connect(view->view()->document(), &KTextEditor::Document::textChanged, [this]() {
37 m_lastPosition = -1;
38 });
39 }
40
41 void *interface_cast(QAccessible::InterfaceType t) override
42 {
44 return static_cast<QAccessibleEditableTextInterface *>(this);
46 return static_cast<QAccessibleTextInterface *>(this);
47 return nullptr;
48 }
49
50 ~KateViewAccessible() override
51 {
52 QObject::disconnect(m_conn);
53 }
54
55 QAccessibleInterface *childAt(int x, int y) const override
56 {
57 Q_UNUSED(x);
58 Q_UNUSED(y);
59 return nullptr;
60 }
61
62 void setText(QAccessible::Text t, const QString &text) override
63 {
64 if (t == QAccessible::Value && view()->view()->document()) {
65 view()->view()->document()->setText(text);
66 m_lastPosition = -1;
67 }
68 }
69
70 QAccessible::State state() const override
71 {
73 s.focusable = view()->focusPolicy() != Qt::NoFocus;
74 s.focused = view()->hasFocus();
75 s.editable = true;
76 s.multiLine = true;
77 s.selectableText = true;
78 return s;
79 }
80
81 QString text(QAccessible::Text t) const override
82 {
83 QString s;
84 if (view()->view()->document()) {
85 if (t == QAccessible::Name) {
86 s = view()->view()->document()->documentName();
87 }
88 if (t == QAccessible::Value) {
89 s = view()->view()->document()->text();
90 }
91 }
92 return s;
93 }
94
95 int characterCount() const override
96 {
97 return view()->view()->document()->text().size();
98 }
99
100 void addSelection(int startOffset, int endOffset) override
101 {
102 KTextEditor::Range range;
103 range.setRange(cursorFromInt(startOffset), cursorFromInt(endOffset));
104 view()->view()->setSelection(range);
105 view()->view()->setCursorPosition(cursorFromInt(endOffset));
106 }
107
108 QString attributes(int offset, int *startOffset, int *endOffset) const override
109 {
110 Q_UNUSED(offset);
111 *startOffset = 0;
112 *endOffset = characterCount();
113 return QString();
114 }
115
116 QRect characterRect(int offset) const override
117 {
118 KTextEditor::Cursor c = cursorFromInt(offset);
119 if (!c.isValid()) {
120 return QRect();
121 }
122 QPoint p = view()->cursorToCoordinate(c);
123 KTextEditor::Cursor endCursor = KTextEditor::Cursor(c.line(), c.column() + 1);
124 QPoint size = view()->cursorToCoordinate(endCursor) - p;
125 return QRect(view()->mapToGlobal(p), QSize(size.x(), size.y()));
126 }
127
128 int cursorPosition() const override
129 {
130 KTextEditor::Cursor c = view()->cursorPosition();
131 return positionFromCursor(view(), c);
132 }
133
134 int offsetAtPoint(const QPoint &point) const override
135 {
136 if (view()) {
137 KTextEditor::Cursor c = view()->coordinatesToCursor(point);
138 return positionFromCursor(view(), c);
139 }
140 return 0;
141 }
142
143 void removeSelection(int selectionIndex) override
144 {
145 if (selectionIndex != 0) {
146 return;
147 }
148 view()->view()->clearSelection();
149 }
150
151 void scrollToSubstring(int startIndex, int /*endIndex*/) override
152 {
153 auto c = cursorFromInt(startIndex);
154 if (!c.isValid()) {
155 return;
156 }
157 view()->view()->setScrollPosition(c);
158 }
159
160 void selection(int selectionIndex, int *startOffset, int *endOffset) const override
161 {
162 if (selectionIndex != 0 || !view()->view()->selection()) {
163 *startOffset = 0;
164 *endOffset = 0;
165 return;
166 }
167 KTextEditor::Range range = view()->view()->selectionRange();
168 *startOffset = positionFromCursor(view(), range.start());
169 *endOffset = positionFromCursor(view(), range.end());
170 }
171
172 int selectionCount() const override
173 {
174 return view()->view()->selection() ? 1 : 0;
175 }
176
177 void setCursorPosition(int position) override
178 {
179 view()->view()->setCursorPosition(cursorFromInt(position));
180 }
181
182 void setSelection(int selectionIndex, int startOffset, int endOffset) override
183 {
184 if (selectionIndex != 0) {
185 return;
186 }
187 KTextEditor::Range range = KTextEditor::Range(cursorFromInt(startOffset), cursorFromInt(endOffset));
188 view()->view()->setSelection(range);
189 }
190
191 QString text(int startOffset, int endOffset) const override
192 {
193 if (startOffset > endOffset) {
194 return QString();
195 }
196 return view()->view()->document()->text().mid(startOffset, endOffset - startOffset);
197 }
198
199 QString textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, int *startOffset, int *endOffset) const override
200 {
201 *startOffset = -1;
202 *endOffset = -1;
203 if (!view() || !startOffset || !endOffset) {
204 return {};
205 }
206 if (offset == -1) {
207 offset = positionFromCursor(view(), view()->view()->doc()->documentEnd());
208 }
209 KTextEditor::Cursor c = cursorFromInt(offset);
210 if (!c.isValid()) {
211 return {};
212 }
213 auto doc = view()->view()->doc();
214
215 switch (boundaryType) {
216 case QAccessible::TextBoundaryType::CharBoundary: {
217 QString t = doc->characterAt(c);
218 *startOffset = offset;
219 *endOffset = *startOffset + 1;
220 return t;
221 } break;
222 case QAccessible::TextBoundaryType::WordBoundary: {
223 QString t = doc->wordAt(c);
224 *startOffset = offset;
225 *endOffset = offset + t.size();
226 return t;
227 } break;
228 case QAccessible::TextBoundaryType::LineBoundary:
229 case QAccessible::TextBoundaryType::ParagraphBoundary: {
230 const QString line = doc->line(c.line());
231 if (line.isEmpty()) {
232 *startOffset = offset;
233 *endOffset = offset;
234 return {};
235 }
236 const int start = positionFromCursor(view(), KTextEditor::Cursor(c.line(), 0));
237 *startOffset = start;
238 *endOffset = offset + line.size();
239 return line;
240 } break;
241 case QAccessible::TextBoundaryType::SentenceBoundary: {
242 const QString line = doc->line(c.line());
243 if (line.isEmpty()) {
244 *startOffset = offset;
245 *endOffset = offset;
246 return {};
247 }
248 QTextBoundaryFinder bf(QTextBoundaryFinder::BoundaryType::Sentence, line);
249 int e = bf.toNextBoundary();
250 if (e != -1) {
251 int start = positionFromCursor(view(), KTextEditor::Cursor(c.line(), 0));
252 *startOffset = start;
253 *endOffset = start + bf.position();
254 return line.mid(0, e);
255 }
256 } break;
257 case QAccessible::TextBoundaryType::NoBoundary: {
258 const QString text = doc->text();
259 *startOffset = 0;
260 *endOffset = text.size();
261 return text;
262 } break;
263 }
264
265 return {};
266 }
267
268 QString textBeforeOffset(int /*offset*/, QAccessible::TextBoundaryType /*boundaryType*/, int *startOffset, int *endOffset) const override
269 {
270 // FIXME
271 *startOffset = -1;
272 *endOffset = -1;
273 return {};
274 }
275 QString textAfterOffset(int /*offset*/, QAccessible::TextBoundaryType /*boundaryType*/, int *startOffset, int *endOffset) const override
276 {
277 // FIXME
278 *startOffset = -1;
279 *endOffset = -1;
280 return {};
281 }
282
283 void deleteText(int startOffset, int endOffset) override
284 {
285 KTextEditor::Document *document = view()->view()->document();
286 KTextEditor::Range range(document->offsetToCursor(startOffset), document->offsetToCursor(endOffset));
287 document->removeText(range);
288 }
289
290 void insertText(int offset, const QString &text) override
291 {
292 KTextEditor::Document *document = view()->view()->document();
293 KTextEditor::Cursor cursor = document->offsetToCursor(offset);
294 document->insertText(cursor, text);
295 }
296
297 void replaceText(int startOffset, int endOffset, const QString &text) override
298 {
299 KTextEditor::Document *document = view()->view()->document();
300 KTextEditor::Range range(document->offsetToCursor(startOffset), document->offsetToCursor(endOffset));
301 document->replaceText(range, text);
302 }
303
304 /**
305 * When possible, using the last returned value m_lastPosition do the count
306 * from the last cursor position m_lastCursor.
307 * @return the number of chars (including one character for new lines)
308 * from the beginning of the file.
309 */
310 int positionFromCursor(KateViewInternal *view, KTextEditor::Cursor cursor) const
311 {
312 int pos = m_lastPosition;
313 const KTextEditor::DocumentPrivate *doc = view->view()->doc();
314
315 // m_lastPosition < 0 is invalid, calculate from the beginning of the document
316 if (m_lastPosition < 0 || view != m_lastView) {
317 pos = doc->cursorToOffset(cursor) - cursor.column();
318 } else {
319 // if the lines are the same, just add the cursor.column(), otherwise
320 if (cursor.line() != m_lastCursor.line()) {
321 // If the cursor is after the previous cursor
322 if (m_lastCursor.line() < cursor.line()) {
323 for (int line = m_lastCursor.line(); line < cursor.line(); ++line) {
324 pos += doc->lineLength(line);
325 }
326 // add new line character for each line
327 pos += cursor.line() - m_lastCursor.line();
328 } else {
329 for (int line = cursor.line(); line < m_lastCursor.line(); ++line) {
330 pos -= doc->lineLength(line);
331 }
332 // remove new line character for each line
333 pos -= m_lastCursor.line() - cursor.line();
334 }
335 }
336 }
337 m_lastCursor = cursor;
338 m_lastPosition = pos;
339
340 return pos + cursor.column();
341 }
342
343private:
344 inline KateViewInternal *view() const
345 {
346 return static_cast<KateViewInternal *>(object());
347 }
348
349 KTextEditor::Cursor cursorFromInt(int position) const
350 {
351 return view()->view()->doc()->offsetToCursor(position);
352 }
353
354 QString textLine(int shiftLines, int offset, int *startOffset, int *endOffset) const
355 {
356 KTextEditor::Cursor pos = cursorFromInt(offset);
357 pos.setColumn(0);
358 if (shiftLines) {
359 pos.setLine(pos.line() + shiftLines);
360 }
361 *startOffset = positionFromCursor(view(), pos);
362 QString line = view()->view()->document()->line(pos.line()) + QLatin1Char('\n');
363 *endOffset = *startOffset + line.length();
364 return line;
365 }
366
367private:
368 // Cache data for positionFromCursor
369 mutable KateViewInternal *m_lastView;
370 mutable KTextEditor::Cursor m_lastCursor;
371 // m_lastPosition stores the positionFromCursor, with the cursor always in column 0
372 mutable int m_lastPosition;
373 // to disconnect the signal
375};
376
377/**
378 * Factory-function used to create \a KateViewAccessible instances for KateViewInternal
379 * to make the KateViewInternal accessible.
380 */
381QAccessibleInterface *accessibleInterfaceFactory(const QString &key, QObject *object)
382{
383 Q_UNUSED(key)
384 // if (key == QLatin1String("KateViewInternal"))
385 if (KateViewInternal *view = qobject_cast<KateViewInternal *>(object)) {
386 return new KateViewAccessible(view);
387 }
388 return nullptr;
389}
390
391#endif
392#endif
The Cursor represents a position in a Document.
Definition cursor.h:75
constexpr int column() const noexcept
Retrieve the column on which this cursor is situated.
Definition cursor.h:192
void setColumn(int column) noexcept
Set the cursor column to column.
Definition cursor.h:201
constexpr bool isValid() const noexcept
Returns whether the current position of this cursor is a valid position (line + column must both be >...
Definition cursor.h:102
void setLine(int line) noexcept
Set the cursor line to line.
Definition cursor.h:183
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
Definition cursor.h:174
Backend of KTextEditor::Document related public KTextEditor interfaces.
qsizetype cursorToOffset(KTextEditor::Cursor c) const override
Retrives the offset for the given cursor position NOTE: It will return -1 if the cursor was invalid o...
KTextEditor::Cursor offsetToCursor(qsizetype offset) const override
Retrives the cursor position for given offset NOTE: It will return an invalid cursor(-1,...
int lineLength(int line) const override
Get the length of a given line in characters.
A KParts derived class representing a text document.
Definition document.h:284
virtual KTextEditor::Cursor offsetToCursor(qsizetype offset) const =0
Retrives the cursor position for given offset NOTE: It will return an invalid cursor(-1,...
void textChanged(KTextEditor::Document *document)
The document emits this signal whenever its text changes.
virtual QString line(int line) const =0
Get a single text line.
virtual bool setText(const QString &text)=0
Set the given text as new document content.
virtual QString documentName() const =0
Get this document's name.
virtual QString text() const =0
Get the document content.
virtual bool removeText(Range range, bool block=false)=0
Remove the text specified in range.
virtual bool insertText(KTextEditor::Cursor position, const QString &text, bool block=false)=0
Insert text at position.
virtual bool replaceText(Range range, const QString &text, bool block=false)
Replace text from range with specified text.
Definition document.cpp:85
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.
void setRange(Range range) noexcept
Set the start and end cursors to range.start() and range.end() respectively.
void setScrollPosition(KTextEditor::Cursor cursor)
Scroll view to cursor.
This class implements a QAccessible-interface for a KateViewInternal.
int positionFromCursor(KateViewInternal *view, KTextEditor::Cursor cursor) const
When possible, using the last returned value m_lastPosition do the count from the last cursor positio...
Q_SCRIPTABLE Q_NOREPLY void start()
virtual QObject * object() const const override
QAccessibleWidget(QWidget *w, QAccessible::Role role, const QString &name)
virtual QAccessible::State state() const const override
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
int x() const const
int y() const const
bool isEmpty() const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
qsizetype size() const const
qsizetype position() const const
qsizetype toNextBoundary()
bool hasFocus() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:11:27 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.