/* Scintilla source code edit control */ /* ScintillaGTKAccessible.h - GTK+ accessibility for ScintillaGTK */ /* Copyright 2016 by Colomban Wendling * The License.txt file describes the conditions under which this software may be distributed. */ #ifndef SCINTILLAGTKACCESSIBLE_H #define SCINTILLAGTKACCESSIBLE_H #ifdef SCI_NAMESPACE namespace Scintilla { #endif #ifndef ATK_CHECK_VERSION # define ATK_CHECK_VERSION(x, y, z) 0 #endif class ScintillaGTKAccessible { private: // weak references to related objects GtkAccessible *accessible; ScintillaGTK *sci; // cache holding character offset for each line start, see CharacterOffsetFromByteOffset() std::vector character_offsets; // cached length of the deletion, in characters (see Notify()) int deletionLengthChar; // local state for comparing Sci::Position old_pos; std::vector old_sels; bool Enabled() const; void UpdateCursor(); void Notify(GtkWidget *widget, gint code, SCNotification *nt); static void SciNotify(GtkWidget *widget, gint code, SCNotification *nt, gpointer data) { try { reinterpret_cast(data)->Notify(widget, code, nt); } catch (...) {} } Sci::Position ByteOffsetFromCharacterOffset(Sci::Position startByte, int characterOffset) { Sci::Position pos = sci->pdoc->GetRelativePosition(startByte, characterOffset); if (pos == INVALID_POSITION) { // clamp invalid positions inside the document if (characterOffset > 0) { return sci->pdoc->Length(); } else { return 0; } } return pos; } Sci::Position ByteOffsetFromCharacterOffset(Sci::Position characterOffset) { return ByteOffsetFromCharacterOffset(0, characterOffset); } Sci::Position CharacterOffsetFromByteOffset(Sci::Position byteOffset) { const Sci::Line line = sci->pdoc->LineFromPosition(byteOffset); if (character_offsets.size() <= static_cast(line)) { if (character_offsets.empty()) character_offsets.push_back(0); for (Sci::Position i = character_offsets.size(); i <= line; i++) { const Sci::Position start = sci->pdoc->LineStart(i - 1); const Sci::Position end = sci->pdoc->LineStart(i); character_offsets.push_back(character_offsets[i - 1] + sci->pdoc->CountCharacters(start, end)); } } const Sci::Position lineStart = sci->pdoc->LineStart(line); return character_offsets[line] + sci->pdoc->CountCharacters(lineStart, byteOffset); } void CharacterRangeFromByteRange(Sci::Position startByte, Sci::Position endByte, int *startChar, int *endChar) { *startChar = CharacterOffsetFromByteOffset(startByte); *endChar = *startChar + sci->pdoc->CountCharacters(startByte, endByte); } void ByteRangeFromCharacterRange(int startChar, int endChar, Sci::Position& startByte, Sci::Position& endByte) { startByte = ByteOffsetFromCharacterOffset(startChar); endByte = ByteOffsetFromCharacterOffset(startByte, endChar - startChar); } Sci::Position PositionBefore(Sci::Position pos) { return sci->pdoc->MovePositionOutsideChar(pos - 1, -1, true); } Sci::Position PositionAfter(Sci::Position pos) { return sci->pdoc->MovePositionOutsideChar(pos + 1, 1, true); } int StyleAt(Sci::Position position, bool ensureStyle = false) { if (ensureStyle) sci->pdoc->EnsureStyledTo(position); return sci->pdoc->StyleAt(position); } // For AtkText gchar *GetTextRangeUTF8(Sci::Position startByte, Sci::Position endByte); gchar *GetText(int startChar, int endChar); gchar *GetTextAfterOffset(int charOffset, AtkTextBoundary boundaryType, int *startChar, int *endChar); gchar *GetTextBeforeOffset(int charOffset, AtkTextBoundary boundaryType, int *startChar, int *endChar); gchar *GetTextAtOffset(int charOffset, AtkTextBoundary boundaryType, int *startChar, int *endChar); #if ATK_CHECK_VERSION(2, 10, 0) gchar *GetStringAtOffset(int charOffset, AtkTextGranularity granularity, int *startChar, int *endChar); #endif gunichar GetCharacterAtOffset(int charOffset); gint GetCharacterCount(); gint GetCaretOffset(); gboolean SetCaretOffset(int charOffset); gint GetOffsetAtPoint(gint x, gint y, AtkCoordType coords); void GetCharacterExtents(int charOffset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords); AtkAttributeSet *GetAttributesForStyle(unsigned int styleNum); AtkAttributeSet *GetRunAttributes(int charOffset, int *startChar, int *endChar); AtkAttributeSet *GetDefaultAttributes(); gint GetNSelections(); gchar *GetSelection(gint selection_num, int *startChar, int *endChar); gboolean AddSelection(int startChar, int endChar); gboolean RemoveSelection(int selection_num); gboolean SetSelection(gint selection_num, int startChar, int endChar); // for AtkEditableText bool InsertStringUTF8(Sci::Position bytePos, const gchar *utf8, Sci::Position lengthBytes); void SetTextContents(const gchar *contents); void InsertText(const gchar *text, int lengthBytes, int *charPosition); void CopyText(int startChar, int endChar); void CutText(int startChar, int endChar); void DeleteText(int startChar, int endChar); void PasteText(int charPosition); public: ScintillaGTKAccessible(GtkAccessible *accessible_, GtkWidget *widget_); ~ScintillaGTKAccessible(); static ScintillaGTKAccessible *FromAccessible(GtkAccessible *accessible); static ScintillaGTKAccessible *FromAccessible(AtkObject *accessible) { return FromAccessible(GTK_ACCESSIBLE(accessible)); } // So ScintillaGTK can notify us void ChangeDocument(Document *oldDoc, Document *newDoc); void NotifyReadOnly(); void SetAccessibility(); // Helper GtkWidget methods static AtkObject *WidgetGetAccessibleImpl(GtkWidget *widget, AtkObject **cache, gpointer widget_parent_class); // ATK methods class AtkTextIface { public: static void init(::AtkTextIface *iface); private: AtkTextIface(); static gchar *GetText(AtkText *text, int start_offset, int end_offset); static gchar *GetTextAfterOffset(AtkText *text, int offset, AtkTextBoundary boundary_type, int *start_offset, int *end_offset); static gchar *GetTextBeforeOffset(AtkText *text, int offset, AtkTextBoundary boundary_type, int *start_offset, int *end_offset); static gchar *GetTextAtOffset(AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset); #if ATK_CHECK_VERSION(2, 10, 0) static gchar *GetStringAtOffset(AtkText *text, gint offset, AtkTextGranularity granularity, gint *start_offset, gint *end_offset); #endif static gunichar GetCharacterAtOffset(AtkText *text, gint offset); static gint GetCharacterCount(AtkText *text); static gint GetCaretOffset(AtkText *text); static gboolean SetCaretOffset(AtkText *text, gint offset); static gint GetOffsetAtPoint(AtkText *text, gint x, gint y, AtkCoordType coords); static void GetCharacterExtents(AtkText *text, gint offset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords); static AtkAttributeSet *GetRunAttributes(AtkText *text, gint offset, gint *start_offset, gint *end_offset); static AtkAttributeSet *GetDefaultAttributes(AtkText *text); static gint GetNSelections(AtkText *text); static gchar *GetSelection(AtkText *text, gint selection_num, gint *start_pos, gint *end_pos); static gboolean AddSelection(AtkText *text, gint start, gint end); static gboolean RemoveSelection(AtkText *text, gint selection_num); static gboolean SetSelection(AtkText *text, gint selection_num, gint start, gint end); }; class AtkEditableTextIface { public: static void init(::AtkEditableTextIface *iface); private: AtkEditableTextIface(); static void SetTextContents(AtkEditableText *text, const gchar *contents); static void InsertText(AtkEditableText *text, const gchar *contents, gint length, gint *position); static void CopyText(AtkEditableText *text, gint start, gint end); static void CutText(AtkEditableText *text, gint start, gint end); static void DeleteText(AtkEditableText *text, gint start, gint end); static void PasteText(AtkEditableText *text, gint position); }; }; #ifdef SCI_NAMESPACE } #endif #endif /* SCINTILLAGTKACCESSIBLE_H */