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