File indexing completed on 2024-05-05 16:18:21
0001 /* 0002 SPDX-FileCopyrightText: 2010 Sebastian Sauer <mail@dipe.org> 0003 SPDX-FileCopyrightText: 2012 Frederik Gladhorn <gladhorn@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #ifndef _KATE_VIEW_ACCESSIBLE_ 0009 #define _KATE_VIEW_ACCESSIBLE_ 0010 0011 #ifndef QT_NO_ACCESSIBILITY 0012 0013 #include "katedocument.h" 0014 #include "kateview.h" 0015 #include "kateviewinternal.h" 0016 0017 #include <KLocalizedString> 0018 #include <QAccessible> 0019 #include <QAccessibleWidget> 0020 0021 /** 0022 * This class implements a QAccessible-interface for a KateViewInternal. 0023 * 0024 * This is the root class for the kateview. The \a KateCursorAccessible class 0025 * represents the cursor in the kateview and is a child of this class. 0026 */ 0027 class KateViewAccessible : public QAccessibleWidget, public QAccessibleTextInterface // FIXME maybe:, public QAccessibleEditableTextInterface 0028 { 0029 public: 0030 explicit KateViewAccessible(KateViewInternal *view) 0031 : QAccessibleWidget(view, QAccessible::EditableText) 0032 , m_lastPosition(-1) 0033 { 0034 // to invalidate positionFromCursor cache when the document is changed 0035 m_conn = QObject::connect(view->view()->document(), &KTextEditor::Document::textChanged, [this]() { 0036 m_lastPosition = -1; 0037 }); 0038 } 0039 0040 void *interface_cast(QAccessible::InterfaceType t) override 0041 { 0042 if (t == QAccessible::TextInterface) 0043 return static_cast<QAccessibleTextInterface *>(this); 0044 return nullptr; 0045 } 0046 0047 ~KateViewAccessible() override 0048 { 0049 QObject::disconnect(m_conn); 0050 } 0051 0052 QAccessibleInterface *childAt(int x, int y) const override 0053 { 0054 Q_UNUSED(x); 0055 Q_UNUSED(y); 0056 return nullptr; 0057 } 0058 0059 void setText(QAccessible::Text t, const QString &text) override 0060 { 0061 if (t == QAccessible::Value && view()->view()->document()) { 0062 view()->view()->document()->setText(text); 0063 m_lastPosition = -1; 0064 } 0065 } 0066 0067 QAccessible::State state() const override 0068 { 0069 QAccessible::State s = QAccessibleWidget::state(); 0070 s.focusable = view()->focusPolicy() != Qt::NoFocus; 0071 s.focused = view()->hasFocus(); 0072 s.editable = true; 0073 s.multiLine = true; 0074 s.selectableText = true; 0075 return s; 0076 } 0077 0078 QString text(QAccessible::Text t) const override 0079 { 0080 QString s; 0081 if (view()->view()->document()) { 0082 if (t == QAccessible::Name) { 0083 s = view()->view()->document()->documentName(); 0084 } 0085 if (t == QAccessible::Value) { 0086 s = view()->view()->document()->text(); 0087 } 0088 } 0089 return s; 0090 } 0091 0092 int characterCount() const override 0093 { 0094 return view()->view()->document()->text().size(); 0095 } 0096 0097 void addSelection(int startOffset, int endOffset) override 0098 { 0099 KTextEditor::Range range; 0100 range.setRange(cursorFromInt(startOffset), cursorFromInt(endOffset)); 0101 view()->view()->setSelection(range); 0102 view()->view()->setCursorPosition(cursorFromInt(endOffset)); 0103 } 0104 0105 QString attributes(int offset, int *startOffset, int *endOffset) const override 0106 { 0107 Q_UNUSED(offset); 0108 *startOffset = 0; 0109 *endOffset = characterCount(); 0110 return QString(); 0111 } 0112 QRect characterRect(int offset) const override 0113 { 0114 KTextEditor::Cursor c = cursorFromInt(offset); 0115 if (!c.isValid()) { 0116 return QRect(); 0117 } 0118 QPoint p = view()->cursorToCoordinate(c); 0119 KTextEditor::Cursor endCursor = KTextEditor::Cursor(c.line(), c.column() + 1); 0120 QPoint size = view()->cursorToCoordinate(endCursor) - p; 0121 return QRect(view()->mapToGlobal(p), QSize(size.x(), size.y())); 0122 } 0123 int cursorPosition() const override 0124 { 0125 KTextEditor::Cursor c = view()->cursorPosition(); 0126 return positionFromCursor(view(), c); 0127 } 0128 int offsetAtPoint(const QPoint & /*point*/) const override 0129 { 0130 return 0; 0131 } 0132 void removeSelection(int selectionIndex) override 0133 { 0134 if (selectionIndex != 0) { 0135 return; 0136 } 0137 view()->view()->clearSelection(); 0138 } 0139 void scrollToSubstring(int /*startIndex*/, int /*endIndex*/) override 0140 { 0141 // FIXME 0142 } 0143 void selection(int selectionIndex, int *startOffset, int *endOffset) const override 0144 { 0145 if (selectionIndex != 0 || !view()->view()->selection()) { 0146 *startOffset = 0; 0147 *endOffset = 0; 0148 return; 0149 } 0150 KTextEditor::Range range = view()->view()->selectionRange(); 0151 *startOffset = positionFromCursor(view(), range.start()); 0152 *endOffset = positionFromCursor(view(), range.end()); 0153 } 0154 0155 int selectionCount() const override 0156 { 0157 return view()->view()->selection() ? 1 : 0; 0158 } 0159 void setCursorPosition(int position) override 0160 { 0161 view()->view()->setCursorPosition(cursorFromInt(position)); 0162 } 0163 void setSelection(int selectionIndex, int startOffset, int endOffset) override 0164 { 0165 if (selectionIndex != 0) { 0166 return; 0167 } 0168 KTextEditor::Range range = KTextEditor::Range(cursorFromInt(startOffset), cursorFromInt(endOffset)); 0169 view()->view()->setSelection(range); 0170 } 0171 QString text(int startOffset, int endOffset) const override 0172 { 0173 if (startOffset > endOffset) { 0174 return QString(); 0175 } 0176 return view()->view()->document()->text().mid(startOffset, endOffset - startOffset); 0177 } 0178 0179 /** 0180 * When possible, using the last returned value m_lastPosition do the count 0181 * from the last cursor position m_lastCursor. 0182 * @return the number of chars (including one character for new lines) 0183 * from the beginning of the file. 0184 */ 0185 int positionFromCursor(KateViewInternal *view, const KTextEditor::Cursor &cursor) const 0186 { 0187 int pos = m_lastPosition; 0188 const KTextEditor::DocumentPrivate *doc = view->view()->doc(); 0189 0190 // m_lastPosition < 0 is invalid, calculate from the beginning of the document 0191 if (m_lastPosition < 0 || view != m_lastView) { 0192 pos = 0; 0193 // Default (worst) case 0194 for (int line = 0; line < cursor.line(); ++line) { 0195 pos += doc->lineLength(line); 0196 } 0197 // new line for each line 0198 pos += cursor.line(); 0199 m_lastView = view; 0200 } else { 0201 // if the lines are the same, just add the cursor.column(), otherwise 0202 if (cursor.line() != m_lastCursor.line()) { 0203 // If the cursor is after the previous cursor 0204 if (m_lastCursor.line() < cursor.line()) { 0205 for (int line = m_lastCursor.line(); line < cursor.line(); ++line) { 0206 pos += doc->lineLength(line); 0207 } 0208 // add new line character for each line 0209 pos += cursor.line() - m_lastCursor.line(); 0210 } else { 0211 for (int line = cursor.line(); line < m_lastCursor.line(); ++line) { 0212 pos -= doc->lineLength(line); 0213 } 0214 // remove new line character for each line 0215 pos -= m_lastCursor.line() - cursor.line(); 0216 } 0217 } 0218 } 0219 m_lastCursor = cursor; 0220 m_lastPosition = pos; 0221 0222 return pos + cursor.column(); 0223 } 0224 0225 private: 0226 inline KateViewInternal *view() const 0227 { 0228 return static_cast<KateViewInternal *>(object()); 0229 } 0230 0231 KTextEditor::Cursor cursorFromInt(int position) const 0232 { 0233 int line = 0; 0234 for (;;) { 0235 const QString lineString = view()->view()->document()->line(line); 0236 if (position > lineString.length()) { 0237 // one is the newline 0238 position -= lineString.length() + 1; 0239 ++line; 0240 } else { 0241 break; 0242 } 0243 } 0244 return KTextEditor::Cursor(line, position); 0245 } 0246 0247 QString textLine(int shiftLines, int offset, int *startOffset, int *endOffset) const 0248 { 0249 KTextEditor::Cursor pos = cursorFromInt(offset); 0250 pos.setColumn(0); 0251 if (shiftLines) { 0252 pos.setLine(pos.line() + shiftLines); 0253 } 0254 *startOffset = positionFromCursor(view(), pos); 0255 QString line = view()->view()->document()->line(pos.line()) + QLatin1Char('\n'); 0256 *endOffset = *startOffset + line.length(); 0257 return line; 0258 } 0259 0260 private: 0261 // Cache data for positionFromCursor 0262 mutable KateViewInternal *m_lastView; 0263 mutable KTextEditor::Cursor m_lastCursor; 0264 // m_lastPosition stores the positionFromCursor, with the cursor always in column 0 0265 mutable int m_lastPosition; 0266 // to disconnect the signal 0267 QMetaObject::Connection m_conn; 0268 }; 0269 0270 /** 0271 * Factory-function used to create \a KateViewAccessible instances for KateViewInternal 0272 * to make the KateViewInternal accessible. 0273 */ 0274 QAccessibleInterface *accessibleInterfaceFactory(const QString &key, QObject *object) 0275 { 0276 Q_UNUSED(key) 0277 // if (key == QLatin1String("KateViewInternal")) 0278 if (KateViewInternal *view = qobject_cast<KateViewInternal *>(object)) { 0279 return new KateViewAccessible(view); 0280 } 0281 return nullptr; 0282 } 0283 0284 #endif 0285 #endif