1 /* Scintilla source code edit control */
2 /* ScintillaGTKAccessible.c - GTK+ accessibility for ScintillaGTK */
3 /* Copyright 2016 by Colomban Wendling <colomban@geany.org>
4 * The License.txt file describes the conditions under which this software may be distributed. */
6 // REFERENCES BETWEEN THE DIFFERENT OBJECTS
8 // ScintillaGTKAccessible is the actual implementation, as a C++ class.
9 // ScintillaObjectAccessible is the GObject derived from AtkObject that
10 // implements the various ATK interfaces, through ScintillaGTKAccessible.
11 // This follows the same pattern as ScintillaGTK and ScintillaObject.
13 // ScintillaGTK owns a strong reference to the ScintillaObjectAccessible, and
14 // is both responsible for creating and destroying that object.
16 // ScintillaObjectAccessible owns a strong reference to ScintillaGTKAccessible,
17 // and is responsible for creating and destroying that object.
19 // ScintillaGTKAccessible has weak references to both the ScintillaGTK and
20 // the ScintillaObjectAccessible objects associated, but does not own any
21 // strong references to those objects.
23 // The chain of ownership is as follows:
24 // ScintillaGTK -> ScintillaObjectAccessible -> ScintillaGTKAccessible
26 // DETAILS ON THE GOBJECT TYPE IMPLEMENTATION
28 // On GTK < 3.2, we need to use the AtkObjectFactory. We need to query
29 // the factory to see what type we should derive from, thus making use of
30 // dynamic inheritance. It's tricky, but it works so long as it's done
33 // On GTK 3.2 through 3.6, we need to hack around because GTK stopped
34 // registering its accessible types in the factory, so we can't query
35 // them that way. Unfortunately, the accessible types aren't exposed
36 // yet (not until 3.8), so there's no proper way to know which type to
37 // inherit from. To work around this, we instantiate the parent's
38 // AtkObject temporarily, and use it's type. It means creating an extra
39 // throwaway object and being able to pass the type information up to the
40 // type registration code, but it's the only solution I could find.
42 // On GTK 3.8 onward, we use the proper exposed GtkContainerAccessible as
43 // parent, and so a straightforward class.
45 // To hide and contain the complexity in type creation arising from the
46 // hackish support for GTK 3.2 to 3.8, the actual implementation for the
47 // widget's get_accessible() is located in the accessibility layer itself.
49 // Initially based on GtkTextViewAccessible from GTK 3.20
50 // Inspiration for the GTK < 3.2 part comes from Evince 2.24, thanks.
52 // FIXME: optimize character/byte offset conversion (with a cache?)
67 // whether we have widget_set() and widget_unset()
68 #define HAVE_WIDGET_SET_UNSET (GTK_CHECK_VERSION(3, 3, 6))
69 // whether GTK accessibility is available through the ATK factory
70 #define HAVE_GTK_FACTORY (! GTK_CHECK_VERSION(3, 1, 9))
71 // whether we have gtk-a11y.h and the public GTK accessible types
72 #define HAVE_GTK_A11Y_H (GTK_CHECK_VERSION(3, 7, 6))
75 # include <gtk/gtk-a11y.h>
78 #if defined(__WIN32__) || defined(_MSC_VER)
82 // ScintillaGTK.h and stuff it needs
86 #include "Scintilla.h"
87 #include "ScintillaWidget.h"
91 #include "StringCopy.h"
93 #include "LexerModule.h"
96 #include "SplitVector.h"
97 #include "Partitioning.h"
98 #include "RunStyles.h"
99 #include "ContractionState.h"
100 #include "CellBuffer.h"
103 #include "Indicator.h"
105 #include "LineMarker.h"
107 #include "ViewStyle.h"
108 #include "CharClassify.h"
109 #include "Decoration.h"
110 #include "CaseFolder.h"
111 #include "Document.h"
112 #include "CaseConvert.h"
113 #include "UniConversion.h"
114 #include "UnicodeFromUTF8.h"
115 #include "Selection.h"
116 #include "PositionCache.h"
117 #include "EditModel.h"
118 #include "MarginView.h"
119 #include "EditView.h"
121 #include "AutoComplete.h"
122 #include "ScintillaBase.h"
124 #include "ScintillaGTK.h"
125 #include "ScintillaGTKAccessible.h"
128 using namespace Scintilla
;
131 struct ScintillaObjectAccessiblePrivate
{
132 ScintillaGTKAccessible
*pscin
;
135 typedef GtkAccessible ScintillaObjectAccessible
;
136 typedef GtkAccessibleClass ScintillaObjectAccessibleClass
;
138 #define SCINTILLA_OBJECT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SCINTILLA_TYPE_OBJECT_ACCESSIBLE, ScintillaObjectAccessible))
139 #define SCINTILLA_TYPE_OBJECT_ACCESSIBLE (scintilla_object_accessible_get_type(0))
141 // We can't use priv member because of dynamic inheritance, so we don't actually know the offset. Meh.
142 #define SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(inst) (G_TYPE_INSTANCE_GET_PRIVATE((inst), SCINTILLA_TYPE_OBJECT_ACCESSIBLE, ScintillaObjectAccessiblePrivate))
144 static GType
scintilla_object_accessible_get_type(GType parent_type
);
146 ScintillaGTKAccessible
*ScintillaGTKAccessible::FromAccessible(GtkAccessible
*accessible
) {
147 // FIXME: do we need the check below? GTK checks that in all methods, so maybe
148 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
153 return SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible
)->pscin
;
156 ScintillaGTKAccessible::ScintillaGTKAccessible(GtkAccessible
*accessible_
, GtkWidget
*widget_
) :
157 accessible(accessible_
),
158 sci(ScintillaGTK::FromWidget(widget_
)),
159 deletionLengthChar(0),
161 g_signal_connect(widget_
, "sci-notify", G_CALLBACK(SciNotify
), this);
164 ScintillaGTKAccessible::~ScintillaGTKAccessible() {
165 g_signal_handlers_disconnect_by_func (sci
->sci
, reinterpret_cast<gpointer
>(SciNotify
), this);
168 gchar
*ScintillaGTKAccessible::GetTextRangeUTF8(Position startByte
, Position endByte
) {
169 g_return_val_if_fail(startByte
>= 0, NULL
);
170 // FIXME: should we swap start/end if necessary?
171 g_return_val_if_fail(endByte
>= startByte
, NULL
);
173 gchar
*utf8Text
= NULL
;
174 const char *charSetBuffer
;
176 // like TargetAsUTF8, but avoids a double conversion
177 if (sci
->IsUnicodeMode() || ! *(charSetBuffer
= sci
->CharacterSetID())) {
178 int len
= endByte
- startByte
;
179 utf8Text
= (char *) g_malloc(len
+ 1);
180 sci
->pdoc
->GetCharRange(utf8Text
, startByte
, len
);
181 utf8Text
[len
] = '\0';
184 std::string s
= sci
->RangeText(startByte
, endByte
);
185 std::string tmputf
= ConvertText(&s
[0], s
.length(), "UTF-8", charSetBuffer
, false);
186 size_t len
= tmputf
.length();
187 utf8Text
= (char *) g_malloc(len
+ 1);
188 memcpy(utf8Text
, tmputf
.c_str(), len
);
189 utf8Text
[len
] = '\0';
195 gchar
*ScintillaGTKAccessible::GetText(int startChar
, int endChar
) {
196 Position startByte
, endByte
;
198 startByte
= ByteOffsetFromCharacterOffset(startChar
);
199 endByte
= sci
->pdoc
->Length();
201 ByteRangeFromCharacterRange(startChar
, endChar
, startByte
, endByte
);
203 return GetTextRangeUTF8(startByte
, endByte
);
206 gchar
*ScintillaGTKAccessible::GetTextAfterOffset(int charOffset
,
207 AtkTextBoundary boundaryType
, int *startChar
, int *endChar
) {
208 g_return_val_if_fail(charOffset
>= 0, NULL
);
210 Position startByte
, endByte
;
211 Position byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
213 switch (boundaryType
) {
214 case ATK_TEXT_BOUNDARY_CHAR
:
215 startByte
= PositionAfter(byteOffset
);
216 endByte
= PositionAfter(startByte
);
217 // FIXME: optimize conversion back, as we can reasonably assume +1 char?
220 case ATK_TEXT_BOUNDARY_WORD_START
:
221 startByte
= sci
->WndProc(SCI_WORDENDPOSITION
, byteOffset
, 1);
222 startByte
= sci
->WndProc(SCI_WORDENDPOSITION
, startByte
, 0);
223 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, startByte
, 1);
224 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, endByte
, 0);
227 case ATK_TEXT_BOUNDARY_WORD_END
:
228 startByte
= sci
->WndProc(SCI_WORDENDPOSITION
, byteOffset
, 0);
229 startByte
= sci
->WndProc(SCI_WORDENDPOSITION
, startByte
, 1);
230 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, startByte
, 0);
231 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, endByte
, 1);
234 case ATK_TEXT_BOUNDARY_LINE_START
: {
235 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
236 startByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
+ 1, 0);
237 endByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
+ 2, 0);
241 case ATK_TEXT_BOUNDARY_LINE_END
: {
242 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
243 startByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
, 0);
244 endByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
+ 1, 0);
249 *startChar
= *endChar
= -1;
253 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
254 return GetTextRangeUTF8(startByte
, endByte
);
257 gchar
*ScintillaGTKAccessible::GetTextBeforeOffset(int charOffset
,
258 AtkTextBoundary boundaryType
, int *startChar
, int *endChar
) {
259 g_return_val_if_fail(charOffset
>= 0, NULL
);
261 Position startByte
, endByte
;
262 Position byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
264 switch (boundaryType
) {
265 case ATK_TEXT_BOUNDARY_CHAR
:
266 endByte
= PositionBefore(byteOffset
);
267 startByte
= PositionBefore(endByte
);
270 case ATK_TEXT_BOUNDARY_WORD_START
:
271 endByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, byteOffset
, 0);
272 endByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, endByte
, 1);
273 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, endByte
, 0);
274 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, startByte
, 1);
277 case ATK_TEXT_BOUNDARY_WORD_END
:
278 endByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, byteOffset
, 1);
279 endByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, endByte
, 0);
280 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, endByte
, 1);
281 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, startByte
, 0);
284 case ATK_TEXT_BOUNDARY_LINE_START
: {
285 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
286 endByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
, 0);
288 startByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
- 1, 0);
295 case ATK_TEXT_BOUNDARY_LINE_END
: {
296 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
298 endByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
- 1, 0);
303 startByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
- 2, 0);
311 *startChar
= *endChar
= -1;
315 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
316 return GetTextRangeUTF8(startByte
, endByte
);
319 gchar
*ScintillaGTKAccessible::GetTextAtOffset(int charOffset
,
320 AtkTextBoundary boundaryType
, int *startChar
, int *endChar
) {
321 g_return_val_if_fail(charOffset
>= 0, NULL
);
323 Position startByte
, endByte
;
324 Position byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
326 switch (boundaryType
) {
327 case ATK_TEXT_BOUNDARY_CHAR
:
328 startByte
= byteOffset
;
329 endByte
= sci
->WndProc(SCI_POSITIONAFTER
, byteOffset
, 0);
332 case ATK_TEXT_BOUNDARY_WORD_START
:
333 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, byteOffset
, 1);
334 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, byteOffset
, 1);
335 if (! sci
->WndProc(SCI_ISRANGEWORD
, startByte
, endByte
)) {
336 // if the cursor was not on a word, forward back
337 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, startByte
, 0);
338 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, startByte
, 1);
340 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, endByte
, 0);
343 case ATK_TEXT_BOUNDARY_WORD_END
:
344 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, byteOffset
, 1);
345 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, byteOffset
, 1);
346 if (! sci
->WndProc(SCI_ISRANGEWORD
, startByte
, endByte
)) {
347 // if the cursor was not on a word, forward back
348 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, endByte
, 0);
349 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, endByte
, 1);
351 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, startByte
, 0);
354 case ATK_TEXT_BOUNDARY_LINE_START
: {
355 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
356 startByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
, 0);
357 endByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
+ 1, 0);
361 case ATK_TEXT_BOUNDARY_LINE_END
: {
362 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
364 startByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
- 1, 0);
368 endByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
, 0);
373 *startChar
= *endChar
= -1;
377 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
378 return GetTextRangeUTF8(startByte
, endByte
);
381 #if ATK_CHECK_VERSION(2, 10, 0)
382 gchar
*ScintillaGTKAccessible::GetStringAtOffset(int charOffset
,
383 AtkTextGranularity granularity
, int *startChar
, int *endChar
) {
384 g_return_val_if_fail(charOffset
>= 0, NULL
);
386 Position startByte
, endByte
;
387 Position byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
389 switch (granularity
) {
390 case ATK_TEXT_GRANULARITY_CHAR
:
391 startByte
= byteOffset
;
392 endByte
= sci
->WndProc(SCI_POSITIONAFTER
, byteOffset
, 0);
394 case ATK_TEXT_GRANULARITY_WORD
:
395 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, byteOffset
, 1);
396 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, byteOffset
, 1);
398 case ATK_TEXT_GRANULARITY_LINE
: {
399 gint line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
400 startByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
, 0);
401 endByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
, 0);
405 *startChar
= *endChar
= -1;
409 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
410 return GetTextRangeUTF8(startByte
, endByte
);
414 gunichar
ScintillaGTKAccessible::GetCharacterAtOffset(int charOffset
) {
415 g_return_val_if_fail(charOffset
>= 0, 0);
417 Position startByte
= ByteOffsetFromCharacterOffset(charOffset
);
418 Position endByte
= PositionAfter(startByte
);
419 gchar
*ch
= GetTextRangeUTF8(startByte
, endByte
);
420 gunichar unichar
= g_utf8_get_char_validated(ch
, -1);
426 gint
ScintillaGTKAccessible::GetCharacterCount() {
427 return sci
->pdoc
->CountCharacters(0, sci
->pdoc
->Length());
430 gint
ScintillaGTKAccessible::GetCaretOffset() {
431 return CharacterOffsetFromByteOffset(sci
->WndProc(SCI_GETCURRENTPOS
, 0, 0));
434 gboolean
ScintillaGTKAccessible::SetCaretOffset(int charOffset
) {
435 sci
->WndProc(SCI_GOTOPOS
, ByteOffsetFromCharacterOffset(charOffset
), 0);
439 gint
ScintillaGTKAccessible::GetOffsetAtPoint(gint x
, gint y
, AtkCoordType coords
) {
440 gint x_widget
, y_widget
, x_window
, y_window
;
441 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
443 GdkWindow
*window
= gtk_widget_get_window(widget
);
444 gdk_window_get_origin(window
, &x_widget
, &y_widget
);
445 if (coords
== ATK_XY_SCREEN
) {
448 } else if (coords
== ATK_XY_WINDOW
) {
449 window
= gdk_window_get_toplevel(window
);
450 gdk_window_get_origin(window
, &x_window
, &y_window
);
452 x
= x
- x_widget
+ x_window
;
453 y
= y
- y_widget
+ y_window
;
458 // FIXME: should we handle scrolling?
459 return CharacterOffsetFromByteOffset(sci
->WndProc(SCI_CHARPOSITIONFROMPOINTCLOSE
, x
, y
));
462 void ScintillaGTKAccessible::GetCharacterExtents(int charOffset
,
463 gint
*x
, gint
*y
, gint
*width
, gint
*height
, AtkCoordType coords
) {
464 *x
= *y
= *height
= *width
= 0;
466 Position byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
468 // FIXME: should we handle scrolling?
469 *x
= sci
->WndProc(SCI_POINTXFROMPOSITION
, 0, byteOffset
);
470 *y
= sci
->WndProc(SCI_POINTYFROMPOSITION
, 0, byteOffset
);
472 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
473 *height
= sci
->WndProc(SCI_TEXTHEIGHT
, line
, 0);
475 int nextByteOffset
= PositionAfter(byteOffset
);
476 int next_x
= sci
->WndProc(SCI_POINTXFROMPOSITION
, 0, nextByteOffset
);
478 *width
= next_x
- *x
;
479 } else if (nextByteOffset
> byteOffset
) {
480 /* maybe next position was on the next line or something.
481 * just compute the expected character width */
482 int style
= StyleAt(byteOffset
, true);
483 int len
= nextByteOffset
- byteOffset
;
484 char *ch
= new char[len
+ 1];
485 sci
->pdoc
->GetCharRange(ch
, byteOffset
, len
);
487 *width
= sci
->TextWidth(style
, ch
);
491 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
492 GdkWindow
*window
= gtk_widget_get_window(widget
);
493 int x_widget
, y_widget
;
494 gdk_window_get_origin(window
, &x_widget
, &y_widget
);
495 if (coords
== ATK_XY_SCREEN
) {
498 } else if (coords
== ATK_XY_WINDOW
) {
499 window
= gdk_window_get_toplevel(window
);
500 int x_window
, y_window
;
501 gdk_window_get_origin(window
, &x_window
, &y_window
);
503 *x
+= x_widget
- x_window
;
504 *y
+= y_widget
- y_window
;
506 *x
= *y
= *height
= *width
= 0;
510 static AtkAttributeSet
*AddTextAttribute(AtkAttributeSet
*attributes
, AtkTextAttribute attr
, gchar
*value
) {
511 AtkAttribute
*at
= g_new(AtkAttribute
, 1);
512 at
->name
= g_strdup(atk_text_attribute_get_name(attr
));
515 return g_slist_prepend(attributes
, at
);
518 static AtkAttributeSet
*AddTextIntAttribute(AtkAttributeSet
*attributes
, AtkTextAttribute attr
, gint i
) {
519 return AddTextAttribute(attributes
, attr
, g_strdup(atk_text_attribute_get_value(attr
, i
)));
522 static AtkAttributeSet
*AddTextColorAttribute(AtkAttributeSet
*attributes
, AtkTextAttribute attr
, const ColourDesired
&colour
) {
523 return AddTextAttribute(attributes
, attr
,
524 g_strdup_printf("%u,%u,%u", colour
.GetRed() * 257, colour
.GetGreen() * 257, colour
.GetBlue() * 257));
527 AtkAttributeSet
*ScintillaGTKAccessible::GetAttributesForStyle(unsigned int styleNum
) {
528 AtkAttributeSet
*attr_set
= NULL
;
530 if (styleNum
>= sci
->vs
.styles
.size())
532 Style
&style
= sci
->vs
.styles
[styleNum
];
534 attr_set
= AddTextAttribute(attr_set
, ATK_TEXT_ATTR_FAMILY_NAME
, g_strdup(style
.fontName
));
535 attr_set
= AddTextAttribute(attr_set
, ATK_TEXT_ATTR_SIZE
, g_strdup_printf("%d", style
.size
/ SC_FONT_SIZE_MULTIPLIER
));
536 attr_set
= AddTextIntAttribute(attr_set
, ATK_TEXT_ATTR_WEIGHT
, CLAMP(style
.weight
, 100, 1000));
537 attr_set
= AddTextIntAttribute(attr_set
, ATK_TEXT_ATTR_STYLE
, style
.italic
? PANGO_STYLE_ITALIC
: PANGO_STYLE_NORMAL
);
538 attr_set
= AddTextIntAttribute(attr_set
, ATK_TEXT_ATTR_UNDERLINE
, style
.underline
? PANGO_UNDERLINE_SINGLE
: PANGO_UNDERLINE_NONE
);
539 attr_set
= AddTextColorAttribute(attr_set
, ATK_TEXT_ATTR_FG_COLOR
, style
.fore
);
540 attr_set
= AddTextColorAttribute(attr_set
, ATK_TEXT_ATTR_BG_COLOR
, style
.back
);
541 attr_set
= AddTextIntAttribute(attr_set
, ATK_TEXT_ATTR_INVISIBLE
, style
.visible
? 0 : 1);
542 attr_set
= AddTextIntAttribute(attr_set
, ATK_TEXT_ATTR_EDITABLE
, style
.changeable
? 1 : 0);
547 AtkAttributeSet
*ScintillaGTKAccessible::GetRunAttributes(int charOffset
, int *startChar
, int *endChar
) {
548 g_return_val_if_fail(charOffset
>= -1, NULL
);
551 if (charOffset
== -1) {
552 byteOffset
= sci
->WndProc(SCI_GETCURRENTPOS
, 0, 0);
554 byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
556 int length
= sci
->pdoc
->Length();
558 g_return_val_if_fail(byteOffset
<= length
, NULL
);
560 const char style
= StyleAt(byteOffset
, true);
561 // compute the range for this style
562 Position startByte
= byteOffset
;
563 // when going backwards, we know the style is already computed
564 while (startByte
> 0 && sci
->pdoc
->StyleAt((startByte
) - 1) == style
)
566 Position endByte
= byteOffset
+ 1;
567 while (endByte
< length
&& StyleAt(endByte
, true) == style
)
570 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
571 return GetAttributesForStyle((unsigned int) style
);
574 AtkAttributeSet
*ScintillaGTKAccessible::GetDefaultAttributes() {
575 return GetAttributesForStyle(0);
578 gint
ScintillaGTKAccessible::GetNSelections() {
579 return sci
->sel
.Empty() ? 0 : sci
->sel
.Count();
582 gchar
*ScintillaGTKAccessible::GetSelection(gint selection_num
, int *startChar
, int *endChar
) {
583 if (selection_num
< 0 || (unsigned int) selection_num
>= sci
->sel
.Count())
586 Position startByte
= sci
->sel
.Range(selection_num
).Start().Position();
587 Position endByte
= sci
->sel
.Range(selection_num
).End().Position();
589 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
590 return GetTextRangeUTF8(startByte
, endByte
);
593 gboolean
ScintillaGTKAccessible::AddSelection(int startChar
, int endChar
) {
594 size_t n_selections
= sci
->sel
.Count();
595 Position startByte
, endByte
;
596 ByteRangeFromCharacterRange(startChar
, endChar
, startByte
, endByte
);
597 // use WndProc() to set the selections so it notifies as needed
598 if (n_selections
> 1 || ! sci
->sel
.Empty()) {
599 sci
->WndProc(SCI_ADDSELECTION
, startByte
, endByte
);
601 sci
->WndProc(SCI_SETSELECTION
, startByte
, endByte
);
607 gboolean
ScintillaGTKAccessible::RemoveSelection(gint selection_num
) {
608 size_t n_selections
= sci
->sel
.Count();
609 if (selection_num
< 0 || (unsigned int) selection_num
>= n_selections
)
612 if (n_selections
> 1) {
613 sci
->WndProc(SCI_DROPSELECTIONN
, selection_num
, 0);
614 } else if (sci
->sel
.Empty()) {
617 sci
->WndProc(SCI_CLEARSELECTIONS
, 0, 0);
623 gboolean
ScintillaGTKAccessible::SetSelection(gint selection_num
, int startChar
, int endChar
) {
624 if (selection_num
< 0 || (unsigned int) selection_num
>= sci
->sel
.Count())
627 Position startByte
, endByte
;
628 ByteRangeFromCharacterRange(startChar
, endChar
, startByte
, endByte
);
630 sci
->WndProc(SCI_SETSELECTIONNSTART
, selection_num
, startByte
);
631 sci
->WndProc(SCI_SETSELECTIONNEND
, selection_num
, endByte
);
636 void ScintillaGTKAccessible::AtkTextIface::init(::AtkTextIface
*iface
) {
637 iface
->get_text
= GetText
;
638 iface
->get_text_after_offset
= GetTextAfterOffset
;
639 iface
->get_text_at_offset
= GetTextAtOffset
;
640 iface
->get_text_before_offset
= GetTextBeforeOffset
;
641 #if ATK_CHECK_VERSION(2, 10, 0)
642 iface
->get_string_at_offset
= GetStringAtOffset
;
644 iface
->get_character_at_offset
= GetCharacterAtOffset
;
645 iface
->get_character_count
= GetCharacterCount
;
646 iface
->get_caret_offset
= GetCaretOffset
;
647 iface
->set_caret_offset
= SetCaretOffset
;
648 iface
->get_offset_at_point
= GetOffsetAtPoint
;
649 iface
->get_character_extents
= GetCharacterExtents
;
650 iface
->get_n_selections
= GetNSelections
;
651 iface
->get_selection
= GetSelection
;
652 iface
->add_selection
= AddSelection
;
653 iface
->remove_selection
= RemoveSelection
;
654 iface
->set_selection
= SetSelection
;
655 iface
->get_run_attributes
= GetRunAttributes
;
656 iface
->get_default_attributes
= GetDefaultAttributes
;
659 /* atkeditabletext.h */
661 void ScintillaGTKAccessible::SetTextContents(const gchar
*contents
) {
662 // FIXME: it's probably useless to check for READONLY here, SETTEXT probably does it just fine?
663 if (! sci
->pdoc
->IsReadOnly()) {
664 sci
->WndProc(SCI_SETTEXT
, 0, (sptr_t
) contents
);
668 bool ScintillaGTKAccessible::InsertStringUTF8(Position bytePos
, const gchar
*utf8
, int lengthBytes
) {
669 if (sci
->pdoc
->IsReadOnly()) {
673 // like EncodedFromUTF8(), but avoids an extra copy
674 // FIXME: update target?
675 const char *charSetBuffer
;
676 if (sci
->IsUnicodeMode() || ! *(charSetBuffer
= sci
->CharacterSetID())) {
677 sci
->pdoc
->InsertString(bytePos
, utf8
, lengthBytes
);
680 std::string encoded
= ConvertText(utf8
, lengthBytes
, charSetBuffer
, "UTF-8", true);
681 sci
->pdoc
->InsertString(bytePos
, encoded
.c_str(), encoded
.length());
687 void ScintillaGTKAccessible::InsertText(const gchar
*text
, int lengthBytes
, int *charPosition
) {
688 Position bytePosition
= ByteOffsetFromCharacterOffset(*charPosition
);
690 // FIXME: should we update the target?
691 if (InsertStringUTF8(bytePosition
, text
, lengthBytes
)) {
692 (*charPosition
) += sci
->pdoc
->CountCharacters(bytePosition
, lengthBytes
);
696 void ScintillaGTKAccessible::CopyText(int startChar
, int endChar
) {
697 Position startByte
, endByte
;
698 ByteRangeFromCharacterRange(startChar
, endChar
, startByte
, endByte
);
699 sci
->CopyRangeToClipboard(startByte
, endByte
);
702 void ScintillaGTKAccessible::CutText(int startChar
, int endChar
) {
703 g_return_if_fail(endChar
>= startChar
);
705 if (! sci
->pdoc
->IsReadOnly()) {
706 // FIXME: have a byte variant of those and convert only once?
707 CopyText(startChar
, endChar
);
708 DeleteText(startChar
, endChar
);
712 void ScintillaGTKAccessible::DeleteText(int startChar
, int endChar
) {
713 g_return_if_fail(endChar
>= startChar
);
715 if (! sci
->pdoc
->IsReadOnly()) {
716 Position startByte
, endByte
;
717 ByteRangeFromCharacterRange(startChar
, endChar
, startByte
, endByte
);
719 if (! sci
->RangeContainsProtected(startByte
, endByte
)) {
720 // FIXME: restore the target?
721 sci
->pdoc
->DeleteChars(startByte
, endByte
- startByte
);
726 void ScintillaGTKAccessible::PasteText(int charPosition
) {
727 if (sci
->pdoc
->IsReadOnly())
730 // Helper class holding the position for the asynchronous paste operation.
731 // We can only hope that when the callback gets called scia is still valid, but ScintillaGTK
732 // has always done that without problems, so let's guess it's a fairly safe bet.
733 struct Helper
: GObjectWatcher
{
734 ScintillaGTKAccessible
*scia
;
735 Position bytePosition
;
737 virtual void Destroyed() {
741 Helper(ScintillaGTKAccessible
*scia_
, Position bytePos_
) :
742 GObjectWatcher(G_OBJECT(scia_
->sci
->sci
)),
744 bytePosition(bytePos_
) {
747 void TextReceived(GtkClipboard
*, const gchar
*text
) {
749 size_t len
= strlen(text
);
750 std::string convertedText
;
751 if (len
> 0 && scia
->sci
->convertPastes
) {
752 // Convert line endings of the paste into our local line-endings mode
753 convertedText
= Document::TransformLineEnds(text
, len
, scia
->sci
->pdoc
->eolMode
);
754 len
= convertedText
.length();
755 text
= convertedText
.c_str();
757 scia
->InsertStringUTF8(bytePosition
, text
, static_cast<int>(len
));
761 static void TextReceivedCallback(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
) {
762 Helper
*helper
= reinterpret_cast<Helper
*>(data
);
764 if (helper
->scia
!= 0) {
765 helper
->TextReceived(clipboard
, text
);
772 Helper
*helper
= new Helper(this, ByteOffsetFromCharacterOffset(charPosition
));
773 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
774 GtkClipboard
*clipboard
= gtk_widget_get_clipboard(widget
, GDK_SELECTION_CLIPBOARD
);
775 gtk_clipboard_request_text(clipboard
, helper
->TextReceivedCallback
, helper
);
778 void ScintillaGTKAccessible::AtkEditableTextIface::init(::AtkEditableTextIface
*iface
) {
779 iface
->set_text_contents
= SetTextContents
;
780 iface
->insert_text
= InsertText
;
781 iface
->copy_text
= CopyText
;
782 iface
->cut_text
= CutText
;
783 iface
->delete_text
= DeleteText
;
784 iface
->paste_text
= PasteText
;
785 //~ iface->set_run_attributes = SetRunAttributes;
790 void ScintillaGTKAccessible::UpdateCursor() {
791 Position pos
= sci
->WndProc(SCI_GETCURRENTPOS
, 0, 0);
792 if (old_pos
!= pos
) {
793 int charPosition
= CharacterOffsetFromByteOffset(pos
);
794 g_signal_emit_by_name(accessible
, "text-caret-moved", charPosition
);
798 size_t n_selections
= sci
->sel
.Count();
799 size_t prev_n_selections
= old_sels
.size();
800 bool selection_changed
= n_selections
!= prev_n_selections
;
802 old_sels
.resize(n_selections
);
803 for (size_t i
= 0; i
< n_selections
; i
++) {
804 SelectionRange
&sel
= sci
->sel
.Range(i
);
806 if (i
< prev_n_selections
&& ! selection_changed
) {
807 SelectionRange
&old_sel
= old_sels
[i
];
808 // do not consider a caret move to be a selection change
809 selection_changed
= ((! old_sel
.Empty() || ! sel
.Empty()) && ! (old_sel
== sel
));
815 if (selection_changed
)
816 g_signal_emit_by_name(accessible
, "text-selection-changed");
819 void ScintillaGTKAccessible::ChangeDocument(Document
*oldDoc
, Document
*newDoc
) {
820 if (oldDoc
== newDoc
) {
825 int charLength
= oldDoc
->CountCharacters(0, oldDoc
->Length());
826 g_signal_emit_by_name(accessible
, "text-changed::delete", 0, charLength
);
830 PLATFORM_ASSERT(newDoc
== sci
->pdoc
);
832 int charLength
= newDoc
->CountCharacters(0, newDoc
->Length());
833 g_signal_emit_by_name(accessible
, "text-changed::insert", 0, charLength
);
835 if ((oldDoc
? oldDoc
->IsReadOnly() : false) != newDoc
->IsReadOnly()) {
839 // update cursor and selection
846 void ScintillaGTKAccessible::NotifyReadOnly() {
847 bool readonly
= sci
->pdoc
->IsReadOnly();
848 atk_object_notify_state_change(ATK_OBJECT(accessible
), ATK_STATE_EDITABLE
, ! readonly
);
849 #if ATK_CHECK_VERSION(2, 16, 0)
850 atk_object_notify_state_change(ATK_OBJECT(accessible
), ATK_STATE_READ_ONLY
, readonly
);
854 void ScintillaGTKAccessible::Notify(GtkWidget
*, gint
, SCNotification
*nt
) {
855 switch (nt
->nmhdr
.code
) {
857 if (nt
->modificationType
& SC_MOD_INSERTTEXT
) {
858 int startChar
= CharacterOffsetFromByteOffset(nt
->position
);
859 int lengthChar
= sci
->pdoc
->CountCharacters(nt
->position
, nt
->position
+ nt
->length
);
860 g_signal_emit_by_name(accessible
, "text-changed::insert", startChar
, lengthChar
);
863 if (nt
->modificationType
& SC_MOD_BEFOREDELETE
) {
864 // We cannot compute the deletion length in DELETETEXT as it requires accessing the
865 // buffer, so that the character are still present. So, we cache the value here,
866 // and use it in DELETETEXT that fires quickly after.
867 deletionLengthChar
= sci
->pdoc
->CountCharacters(nt
->position
, nt
->position
+ nt
->length
);
869 if (nt
->modificationType
& SC_MOD_DELETETEXT
) {
870 int startChar
= CharacterOffsetFromByteOffset(nt
->position
);
871 g_signal_emit_by_name(accessible
, "text-changed::delete", startChar
, deletionLengthChar
);
874 if (nt
->modificationType
& SC_MOD_CHANGESTYLE
) {
875 g_signal_emit_by_name(accessible
, "text-attributes-changed");
879 if (nt
->updated
& SC_UPDATE_SELECTION
) {
886 // ATK method wrappers
888 // wraps a call from the accessible object to the ScintillaGTKAccessible, and avoid leaking any exception
889 #define WRAPPER_METHOD_BODY(accessible, call, defret) \
891 ScintillaGTKAccessible *thisAccessible = FromAccessible(reinterpret_cast<GtkAccessible*>(accessible)); \
892 if (thisAccessible) { \
893 return thisAccessible->call; \
902 gchar
*ScintillaGTKAccessible::AtkTextIface::GetText(AtkText
*text
, int start_offset
, int end_offset
) {
903 WRAPPER_METHOD_BODY(text
, GetText(start_offset
, end_offset
), NULL
);
905 gchar
*ScintillaGTKAccessible::AtkTextIface::GetTextAfterOffset(AtkText
*text
, int offset
, AtkTextBoundary boundary_type
, int *start_offset
, int *end_offset
) {
906 WRAPPER_METHOD_BODY(text
, GetTextAfterOffset(offset
, boundary_type
, start_offset
, end_offset
), NULL
)
908 gchar
*ScintillaGTKAccessible::AtkTextIface::GetTextBeforeOffset(AtkText
*text
, int offset
, AtkTextBoundary boundary_type
, int *start_offset
, int *end_offset
) {
909 WRAPPER_METHOD_BODY(text
, GetTextBeforeOffset(offset
, boundary_type
, start_offset
, end_offset
), NULL
)
911 gchar
*ScintillaGTKAccessible::AtkTextIface::GetTextAtOffset(AtkText
*text
, gint offset
, AtkTextBoundary boundary_type
, gint
*start_offset
, gint
*end_offset
) {
912 WRAPPER_METHOD_BODY(text
, GetTextAtOffset(offset
, boundary_type
, start_offset
, end_offset
), NULL
)
914 #if ATK_CHECK_VERSION(2, 10, 0)
915 gchar
*ScintillaGTKAccessible::AtkTextIface::GetStringAtOffset(AtkText
*text
, gint offset
, AtkTextGranularity granularity
, gint
*start_offset
, gint
*end_offset
) {
916 WRAPPER_METHOD_BODY(text
, GetStringAtOffset(offset
, granularity
, start_offset
, end_offset
), NULL
)
919 gunichar
ScintillaGTKAccessible::AtkTextIface::GetCharacterAtOffset(AtkText
*text
, gint offset
) {
920 WRAPPER_METHOD_BODY(text
, GetCharacterAtOffset(offset
), 0)
922 gint
ScintillaGTKAccessible::AtkTextIface::GetCharacterCount(AtkText
*text
) {
923 WRAPPER_METHOD_BODY(text
, GetCharacterCount(), 0)
925 gint
ScintillaGTKAccessible::AtkTextIface::GetCaretOffset(AtkText
*text
) {
926 WRAPPER_METHOD_BODY(text
, GetCaretOffset(), 0)
928 gboolean
ScintillaGTKAccessible::AtkTextIface::SetCaretOffset(AtkText
*text
, gint offset
) {
929 WRAPPER_METHOD_BODY(text
, SetCaretOffset(offset
), FALSE
)
931 gint
ScintillaGTKAccessible::AtkTextIface::GetOffsetAtPoint(AtkText
*text
, gint x
, gint y
, AtkCoordType coords
) {
932 WRAPPER_METHOD_BODY(text
, GetOffsetAtPoint(x
, y
, coords
), -1)
934 void ScintillaGTKAccessible::AtkTextIface::GetCharacterExtents(AtkText
*text
, gint offset
, gint
*x
, gint
*y
, gint
*width
, gint
*height
, AtkCoordType coords
) {
935 WRAPPER_METHOD_BODY(text
, GetCharacterExtents(offset
, x
, y
, width
, height
, coords
), )
937 AtkAttributeSet
*ScintillaGTKAccessible::AtkTextIface::GetRunAttributes(AtkText
*text
, gint offset
, gint
*start_offset
, gint
*end_offset
) {
938 WRAPPER_METHOD_BODY(text
, GetRunAttributes(offset
, start_offset
, end_offset
), NULL
)
940 AtkAttributeSet
*ScintillaGTKAccessible::AtkTextIface::GetDefaultAttributes(AtkText
*text
) {
941 WRAPPER_METHOD_BODY(text
, GetDefaultAttributes(), NULL
)
943 gint
ScintillaGTKAccessible::AtkTextIface::GetNSelections(AtkText
*text
) {
944 WRAPPER_METHOD_BODY(text
, GetNSelections(), 0)
946 gchar
*ScintillaGTKAccessible::AtkTextIface::GetSelection(AtkText
*text
, gint selection_num
, gint
*start_pos
, gint
*end_pos
) {
947 WRAPPER_METHOD_BODY(text
, GetSelection(selection_num
, start_pos
, end_pos
), NULL
)
949 gboolean
ScintillaGTKAccessible::AtkTextIface::AddSelection(AtkText
*text
, gint start
, gint end
) {
950 WRAPPER_METHOD_BODY(text
, AddSelection(start
, end
), FALSE
)
952 gboolean
ScintillaGTKAccessible::AtkTextIface::RemoveSelection(AtkText
*text
, gint selection_num
) {
953 WRAPPER_METHOD_BODY(text
, RemoveSelection(selection_num
), FALSE
)
955 gboolean
ScintillaGTKAccessible::AtkTextIface::SetSelection(AtkText
*text
, gint selection_num
, gint start
, gint end
) {
956 WRAPPER_METHOD_BODY(text
, SetSelection(selection_num
, start
, end
), FALSE
)
959 void ScintillaGTKAccessible::AtkEditableTextIface::SetTextContents(AtkEditableText
*text
, const gchar
*contents
) {
960 WRAPPER_METHOD_BODY(text
, SetTextContents(contents
), )
962 void ScintillaGTKAccessible::AtkEditableTextIface::InsertText(AtkEditableText
*text
, const gchar
*contents
, gint length
, gint
*position
) {
963 WRAPPER_METHOD_BODY(text
, InsertText(contents
, length
, position
), )
965 void ScintillaGTKAccessible::AtkEditableTextIface::CopyText(AtkEditableText
*text
, gint start
, gint end
) {
966 WRAPPER_METHOD_BODY(text
, CopyText(start
, end
), )
968 void ScintillaGTKAccessible::AtkEditableTextIface::CutText(AtkEditableText
*text
, gint start
, gint end
) {
969 WRAPPER_METHOD_BODY(text
, CutText(start
, end
), )
971 void ScintillaGTKAccessible::AtkEditableTextIface::DeleteText(AtkEditableText
*text
, gint start
, gint end
) {
972 WRAPPER_METHOD_BODY(text
, DeleteText(start
, end
), )
974 void ScintillaGTKAccessible::AtkEditableTextIface::PasteText(AtkEditableText
*text
, gint position
) {
975 WRAPPER_METHOD_BODY(text
, PasteText(position
), )
981 static GType
scintilla_object_accessible_factory_get_type(void);
984 static void scintilla_object_accessible_init(ScintillaObjectAccessible
*accessible
);
985 static void scintilla_object_accessible_class_init(ScintillaObjectAccessibleClass
*klass
);
986 static gpointer scintilla_object_accessible_parent_class
= NULL
;
989 // @p parent_type is only required on GTK 3.2 to 3.6, and only on the first call
990 static GType
scintilla_object_accessible_get_type(GType parent_type G_GNUC_UNUSED
) {
991 static volatile gsize type_id_result
= 0;
993 if (g_once_init_enter(&type_id_result
)) {
996 (GBaseInitFunc
) NULL
, /* base init */
997 (GBaseFinalizeFunc
) NULL
, /* base finalize */
998 (GClassInitFunc
) scintilla_object_accessible_class_init
, /* class init */
999 (GClassFinalizeFunc
) NULL
, /* class finalize */
1000 NULL
, /* class data */
1001 0, /* instance size */
1002 0, /* nb preallocs */
1003 (GInstanceInitFunc
) scintilla_object_accessible_init
, /* instance init */
1004 NULL
/* value table */
1007 const GInterfaceInfo atk_text_info
= {
1008 (GInterfaceInitFunc
) ScintillaGTKAccessible::AtkTextIface::init
,
1009 (GInterfaceFinalizeFunc
) NULL
,
1013 const GInterfaceInfo atk_editable_text_info
= {
1014 (GInterfaceInitFunc
) ScintillaGTKAccessible::AtkEditableTextIface::init
,
1015 (GInterfaceFinalizeFunc
) NULL
,
1020 // good, we have gtk-a11y.h, we can use that
1021 GType derived_atk_type
= GTK_TYPE_CONTAINER_ACCESSIBLE
;
1022 tinfo
.class_size
= sizeof (GtkContainerAccessibleClass
);
1023 tinfo
.instance_size
= sizeof (GtkContainerAccessible
);
1024 #else // ! HAVE_GTK_A11Y_H
1025 # if HAVE_GTK_FACTORY
1026 // Figure out the size of the class and instance we are deriving from through the registry
1027 GType derived_type
= g_type_parent(SCINTILLA_TYPE_OBJECT
);
1028 AtkObjectFactory
*factory
= atk_registry_get_factory(atk_get_default_registry(), derived_type
);
1029 GType derived_atk_type
= atk_object_factory_get_accessible_type(factory
);
1030 # else // ! HAVE_GTK_FACTORY
1031 // We're kind of screwed and can't determine the parent (no registry, and no public type)
1032 // Hack your way around by requiring the caller to give us our parent type. The caller
1033 // might be able to trick its way into doing that, by e.g. instantiating the parent's
1034 // accessible type and get its GType. It's ugly but we can't do better on GTK 3.2 to 3.6.
1035 g_assert(parent_type
!= 0);
1037 GType derived_atk_type
= parent_type
;
1038 # endif // ! HAVE_GTK_FACTORY
1041 g_type_query(derived_atk_type
, &query
);
1042 tinfo
.class_size
= query
.class_size
;
1043 tinfo
.instance_size
= query
.instance_size
;
1044 #endif // ! HAVE_GTK_A11Y_H
1046 GType type_id
= g_type_register_static(derived_atk_type
, "ScintillaObjectAccessible", &tinfo
, (GTypeFlags
) 0);
1047 g_type_add_interface_static(type_id
, ATK_TYPE_TEXT
, &atk_text_info
);
1048 g_type_add_interface_static(type_id
, ATK_TYPE_EDITABLE_TEXT
, &atk_editable_text_info
);
1050 g_once_init_leave(&type_id_result
, type_id
);
1053 return type_id_result
;
1056 static AtkObject
*scintilla_object_accessible_new(GType parent_type
, GObject
*obj
) {
1057 g_return_val_if_fail(SCINTILLA_IS_OBJECT(obj
), NULL
);
1059 AtkObject
*accessible
= (AtkObject
*) g_object_new(scintilla_object_accessible_get_type(parent_type
),
1060 #if HAVE_WIDGET_SET_UNSET
1064 atk_object_initialize(accessible
, obj
);
1069 // implementation for gtk_widget_get_accessible().
1070 // See the comment at the top of the file for details on the implementation
1071 // @p widget the widget.
1072 // @p cache pointer to store the AtkObject between repeated calls. Might or might not be filled.
1073 // @p widget_parent_class pointer to the widget's parent class (to chain up method calls).
1074 AtkObject
*ScintillaGTKAccessible::WidgetGetAccessibleImpl(GtkWidget
*widget
, AtkObject
**cache
, gpointer widget_parent_class G_GNUC_UNUSED
) {
1075 if (*cache
!= NULL
) {
1079 #if HAVE_GTK_A11Y_H // just instantiate the accessible
1080 *cache
= scintilla_object_accessible_new(0, G_OBJECT(widget
));
1081 #elif HAVE_GTK_FACTORY // register in the factory and let GTK instantiate
1082 static volatile gsize registered
= 0;
1084 if (g_once_init_enter(®istered
)) {
1085 // Figure out whether accessibility is enabled by looking at the type of the accessible
1086 // object which would be created for the parent type of ScintillaObject.
1087 GType derived_type
= g_type_parent(SCINTILLA_TYPE_OBJECT
);
1089 AtkRegistry
*registry
= atk_get_default_registry();
1090 AtkObjectFactory
*factory
= atk_registry_get_factory(registry
, derived_type
);
1091 GType derived_atk_type
= atk_object_factory_get_accessible_type(factory
);
1092 if (g_type_is_a(derived_atk_type
, GTK_TYPE_ACCESSIBLE
)) {
1093 atk_registry_set_factory_type(registry
, SCINTILLA_TYPE_OBJECT
,
1094 scintilla_object_accessible_factory_get_type());
1096 g_once_init_leave(®istered
, 1);
1098 AtkObject
*obj
= GTK_WIDGET_CLASS(widget_parent_class
)->get_accessible(widget
);
1099 *cache
= static_cast<AtkObject
*>(g_object_ref(obj
));
1100 #else // no public API, no factory, so guess from the parent and instantiate
1101 static GType parent_atk_type
= 0;
1103 if (parent_atk_type
== 0) {
1104 AtkObject
*parent_obj
= GTK_WIDGET_CLASS(widget_parent_class
)->get_accessible(widget
);
1106 GType parent_atk_type
= G_OBJECT_TYPE(parent_obj
);
1108 // Figure out whether accessibility is enabled by looking at the type of the accessible
1109 // object which would be created for the parent type of ScintillaObject.
1110 if (g_type_is_a(parent_atk_type
, GTK_TYPE_ACCESSIBLE
)) {
1111 *cache
= scintilla_object_accessible_new(parent_atk_type
, G_OBJECT(widget
));
1113 *cache
= static_cast<AtkObject
*>(g_object_ref(parent_obj
));
1121 static AtkStateSet
*scintilla_object_accessible_ref_state_set(AtkObject
*accessible
) {
1122 AtkStateSet
*state_set
= ATK_OBJECT_CLASS(scintilla_object_accessible_parent_class
)->ref_state_set(accessible
);
1124 GtkWidget
*widget
= gtk_accessible_get_widget(GTK_ACCESSIBLE(accessible
));
1125 if (widget
== NULL
) {
1126 atk_state_set_add_state(state_set
, ATK_STATE_DEFUNCT
);
1128 if (! scintilla_send_message(SCINTILLA_OBJECT(widget
), SCI_GETREADONLY
, 0, 0))
1129 atk_state_set_add_state(state_set
, ATK_STATE_EDITABLE
);
1130 #if ATK_CHECK_VERSION(2, 16, 0)
1132 atk_state_set_add_state(state_set
, ATK_STATE_READ_ONLY
);
1134 atk_state_set_add_state(state_set
, ATK_STATE_MULTI_LINE
);
1135 atk_state_set_add_state(state_set
, ATK_STATE_MULTISELECTABLE
);
1136 atk_state_set_add_state(state_set
, ATK_STATE_SELECTABLE_TEXT
);
1137 /*atk_state_set_add_state(state_set, ATK_STATE_SUPPORTS_AUTOCOMPLETION);*/
1143 static void scintilla_object_accessible_widget_set(GtkAccessible
*accessible
) {
1144 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
1148 ScintillaObjectAccessiblePrivate
*priv
= SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible
);
1149 if (priv
->pscin
!= 0)
1151 priv
->pscin
= new ScintillaGTKAccessible(accessible
, widget
);
1154 #if HAVE_WIDGET_SET_UNSET
1155 static void scintilla_object_accessible_widget_unset(GtkAccessible
*accessible
) {
1156 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
1160 ScintillaObjectAccessiblePrivate
*priv
= SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible
);
1166 static void scintilla_object_accessible_initialize(AtkObject
*obj
, gpointer data
) {
1167 ATK_OBJECT_CLASS(scintilla_object_accessible_parent_class
)->initialize(obj
, data
);
1169 #if ! HAVE_WIDGET_SET_UNSET
1170 scintilla_object_accessible_widget_set(GTK_ACCESSIBLE(obj
));
1173 obj
->role
= ATK_ROLE_TEXT
;
1176 static void scintilla_object_accessible_finalize(GObject
*object
) {
1177 ScintillaObjectAccessiblePrivate
*priv
= SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(object
);
1184 G_OBJECT_CLASS(scintilla_object_accessible_parent_class
)->finalize(object
);
1187 static void scintilla_object_accessible_class_init(ScintillaObjectAccessibleClass
*klass
) {
1188 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
1189 AtkObjectClass
*object_class
= ATK_OBJECT_CLASS(klass
);
1191 #if HAVE_WIDGET_SET_UNSET
1192 GtkAccessibleClass
*accessible_class
= GTK_ACCESSIBLE_CLASS(klass
);
1193 accessible_class
->widget_set
= scintilla_object_accessible_widget_set
;
1194 accessible_class
->widget_unset
= scintilla_object_accessible_widget_unset
;
1197 object_class
->ref_state_set
= scintilla_object_accessible_ref_state_set
;
1198 object_class
->initialize
= scintilla_object_accessible_initialize
;
1200 gobject_class
->finalize
= scintilla_object_accessible_finalize
;
1202 scintilla_object_accessible_parent_class
= g_type_class_peek_parent(klass
);
1204 g_type_class_add_private(klass
, sizeof (ScintillaObjectAccessiblePrivate
));
1207 static void scintilla_object_accessible_init(ScintillaObjectAccessible
*accessible
) {
1208 ScintillaObjectAccessiblePrivate
*priv
= SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible
);
1213 #if HAVE_GTK_FACTORY
1215 typedef AtkObjectFactory ScintillaObjectAccessibleFactory
;
1216 typedef AtkObjectFactoryClass ScintillaObjectAccessibleFactoryClass
;
1218 G_DEFINE_TYPE(ScintillaObjectAccessibleFactory
, scintilla_object_accessible_factory
, ATK_TYPE_OBJECT_FACTORY
)
1220 static void scintilla_object_accessible_factory_init(ScintillaObjectAccessibleFactory
*) {
1223 static GType
scintilla_object_accessible_factory_get_accessible_type(void) {
1224 return SCINTILLA_TYPE_OBJECT_ACCESSIBLE
;
1227 static AtkObject
*scintilla_object_accessible_factory_create_accessible(GObject
*obj
) {
1228 return scintilla_object_accessible_new(0, obj
);
1231 static void scintilla_object_accessible_factory_class_init(AtkObjectFactoryClass
* klass
) {
1232 klass
->create_accessible
= scintilla_object_accessible_factory_create_accessible
;
1233 klass
->get_accessible_type
= scintilla_object_accessible_factory_get_accessible_type
;