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?)
69 // whether we have widget_set() and widget_unset()
70 #define HAVE_WIDGET_SET_UNSET (GTK_CHECK_VERSION(3, 3, 6))
71 // whether GTK accessibility is available through the ATK factory
72 #define HAVE_GTK_FACTORY (! GTK_CHECK_VERSION(3, 1, 9))
73 // whether we have gtk-a11y.h and the public GTK accessible types
74 #define HAVE_GTK_A11Y_H (GTK_CHECK_VERSION(3, 7, 6))
77 # include <gtk/gtk-a11y.h>
80 #if defined(__WIN32__) || defined(_MSC_VER)
84 // ScintillaGTK.h and stuff it needs
88 #include "Scintilla.h"
89 #include "ScintillaWidget.h"
93 #include "StringCopy.h"
95 #include "LexerModule.h"
98 #include "UniqueString.h"
99 #include "SplitVector.h"
100 #include "Partitioning.h"
101 #include "RunStyles.h"
102 #include "ContractionState.h"
103 #include "CellBuffer.h"
106 #include "Indicator.h"
108 #include "LineMarker.h"
110 #include "ViewStyle.h"
111 #include "CharClassify.h"
112 #include "Decoration.h"
113 #include "CaseFolder.h"
114 #include "Document.h"
115 #include "CaseConvert.h"
116 #include "UniConversion.h"
117 #include "UnicodeFromUTF8.h"
118 #include "Selection.h"
119 #include "PositionCache.h"
120 #include "EditModel.h"
121 #include "MarginView.h"
122 #include "EditView.h"
124 #include "AutoComplete.h"
125 #include "ScintillaBase.h"
127 #include "ScintillaGTK.h"
128 #include "ScintillaGTKAccessible.h"
131 using namespace Scintilla
;
134 struct ScintillaObjectAccessiblePrivate
{
135 ScintillaGTKAccessible
*pscin
;
138 typedef GtkAccessible ScintillaObjectAccessible
;
139 typedef GtkAccessibleClass ScintillaObjectAccessibleClass
;
141 #define SCINTILLA_OBJECT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SCINTILLA_TYPE_OBJECT_ACCESSIBLE, ScintillaObjectAccessible))
142 #define SCINTILLA_TYPE_OBJECT_ACCESSIBLE (scintilla_object_accessible_get_type(0))
144 // We can't use priv member because of dynamic inheritance, so we don't actually know the offset. Meh.
145 #define SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(inst) (G_TYPE_INSTANCE_GET_PRIVATE((inst), SCINTILLA_TYPE_OBJECT_ACCESSIBLE, ScintillaObjectAccessiblePrivate))
147 static GType
scintilla_object_accessible_get_type(GType parent_type
);
149 ScintillaGTKAccessible
*ScintillaGTKAccessible::FromAccessible(GtkAccessible
*accessible
) {
150 // FIXME: do we need the check below? GTK checks that in all methods, so maybe
151 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
156 return SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible
)->pscin
;
159 ScintillaGTKAccessible::ScintillaGTKAccessible(GtkAccessible
*accessible_
, GtkWidget
*widget_
) :
160 accessible(accessible_
),
161 sci(ScintillaGTK::FromWidget(widget_
)),
162 deletionLengthChar(0),
164 g_signal_connect(widget_
, "sci-notify", G_CALLBACK(SciNotify
), this);
167 ScintillaGTKAccessible::~ScintillaGTKAccessible() {
168 if (gtk_accessible_get_widget(accessible
)) {
169 g_signal_handlers_disconnect_matched(sci
->sci
, G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, this);
173 gchar
*ScintillaGTKAccessible::GetTextRangeUTF8(Sci::Position startByte
, Sci::Position endByte
) {
174 g_return_val_if_fail(startByte
>= 0, NULL
);
175 // FIXME: should we swap start/end if necessary?
176 g_return_val_if_fail(endByte
>= startByte
, NULL
);
178 gchar
*utf8Text
= NULL
;
179 const char *charSetBuffer
;
181 // like TargetAsUTF8, but avoids a double conversion
182 if (sci
->IsUnicodeMode() || ! *(charSetBuffer
= sci
->CharacterSetID())) {
183 int len
= endByte
- startByte
;
184 utf8Text
= (char *) g_malloc(len
+ 1);
185 sci
->pdoc
->GetCharRange(utf8Text
, startByte
, len
);
186 utf8Text
[len
] = '\0';
189 std::string s
= sci
->RangeText(startByte
, endByte
);
190 std::string tmputf
= ConvertText(&s
[0], s
.length(), "UTF-8", charSetBuffer
, false);
191 size_t len
= tmputf
.length();
192 utf8Text
= (char *) g_malloc(len
+ 1);
193 memcpy(utf8Text
, tmputf
.c_str(), len
);
194 utf8Text
[len
] = '\0';
200 gchar
*ScintillaGTKAccessible::GetText(int startChar
, int endChar
) {
201 Sci::Position startByte
, endByte
;
203 startByte
= ByteOffsetFromCharacterOffset(startChar
);
204 endByte
= sci
->pdoc
->Length();
206 ByteRangeFromCharacterRange(startChar
, endChar
, startByte
, endByte
);
208 return GetTextRangeUTF8(startByte
, endByte
);
211 gchar
*ScintillaGTKAccessible::GetTextAfterOffset(int charOffset
,
212 AtkTextBoundary boundaryType
, int *startChar
, int *endChar
) {
213 g_return_val_if_fail(charOffset
>= 0, NULL
);
215 Sci::Position startByte
, endByte
;
216 Sci::Position byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
218 switch (boundaryType
) {
219 case ATK_TEXT_BOUNDARY_CHAR
:
220 startByte
= PositionAfter(byteOffset
);
221 endByte
= PositionAfter(startByte
);
222 // FIXME: optimize conversion back, as we can reasonably assume +1 char?
225 case ATK_TEXT_BOUNDARY_WORD_START
:
226 startByte
= sci
->WndProc(SCI_WORDENDPOSITION
, byteOffset
, 1);
227 startByte
= sci
->WndProc(SCI_WORDENDPOSITION
, startByte
, 0);
228 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, startByte
, 1);
229 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, endByte
, 0);
232 case ATK_TEXT_BOUNDARY_WORD_END
:
233 startByte
= sci
->WndProc(SCI_WORDENDPOSITION
, byteOffset
, 0);
234 startByte
= sci
->WndProc(SCI_WORDENDPOSITION
, startByte
, 1);
235 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, startByte
, 0);
236 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, endByte
, 1);
239 case ATK_TEXT_BOUNDARY_LINE_START
: {
240 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
241 startByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
+ 1, 0);
242 endByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
+ 2, 0);
246 case ATK_TEXT_BOUNDARY_LINE_END
: {
247 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
248 startByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
, 0);
249 endByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
+ 1, 0);
254 *startChar
= *endChar
= -1;
258 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
259 return GetTextRangeUTF8(startByte
, endByte
);
262 gchar
*ScintillaGTKAccessible::GetTextBeforeOffset(int charOffset
,
263 AtkTextBoundary boundaryType
, int *startChar
, int *endChar
) {
264 g_return_val_if_fail(charOffset
>= 0, NULL
);
266 Sci::Position startByte
, endByte
;
267 Sci::Position byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
269 switch (boundaryType
) {
270 case ATK_TEXT_BOUNDARY_CHAR
:
271 endByte
= PositionBefore(byteOffset
);
272 startByte
= PositionBefore(endByte
);
275 case ATK_TEXT_BOUNDARY_WORD_START
:
276 endByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, byteOffset
, 0);
277 endByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, endByte
, 1);
278 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, endByte
, 0);
279 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, startByte
, 1);
282 case ATK_TEXT_BOUNDARY_WORD_END
:
283 endByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, byteOffset
, 1);
284 endByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, endByte
, 0);
285 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, endByte
, 1);
286 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, startByte
, 0);
289 case ATK_TEXT_BOUNDARY_LINE_START
: {
290 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
291 endByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
, 0);
293 startByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
- 1, 0);
300 case ATK_TEXT_BOUNDARY_LINE_END
: {
301 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
303 endByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
- 1, 0);
308 startByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
- 2, 0);
316 *startChar
= *endChar
= -1;
320 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
321 return GetTextRangeUTF8(startByte
, endByte
);
324 gchar
*ScintillaGTKAccessible::GetTextAtOffset(int charOffset
,
325 AtkTextBoundary boundaryType
, int *startChar
, int *endChar
) {
326 g_return_val_if_fail(charOffset
>= 0, NULL
);
328 Sci::Position startByte
, endByte
;
329 Sci::Position byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
331 switch (boundaryType
) {
332 case ATK_TEXT_BOUNDARY_CHAR
:
333 startByte
= byteOffset
;
334 endByte
= sci
->WndProc(SCI_POSITIONAFTER
, byteOffset
, 0);
337 case ATK_TEXT_BOUNDARY_WORD_START
:
338 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, byteOffset
, 1);
339 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, byteOffset
, 1);
340 if (! sci
->WndProc(SCI_ISRANGEWORD
, startByte
, endByte
)) {
341 // if the cursor was not on a word, forward back
342 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, startByte
, 0);
343 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, startByte
, 1);
345 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, endByte
, 0);
348 case ATK_TEXT_BOUNDARY_WORD_END
:
349 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, byteOffset
, 1);
350 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, byteOffset
, 1);
351 if (! sci
->WndProc(SCI_ISRANGEWORD
, startByte
, endByte
)) {
352 // if the cursor was not on a word, forward back
353 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, endByte
, 0);
354 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, endByte
, 1);
356 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, startByte
, 0);
359 case ATK_TEXT_BOUNDARY_LINE_START
: {
360 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
361 startByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
, 0);
362 endByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
+ 1, 0);
366 case ATK_TEXT_BOUNDARY_LINE_END
: {
367 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
369 startByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
- 1, 0);
373 endByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
, 0);
378 *startChar
= *endChar
= -1;
382 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
383 return GetTextRangeUTF8(startByte
, endByte
);
386 #if ATK_CHECK_VERSION(2, 10, 0)
387 gchar
*ScintillaGTKAccessible::GetStringAtOffset(int charOffset
,
388 AtkTextGranularity granularity
, int *startChar
, int *endChar
) {
389 g_return_val_if_fail(charOffset
>= 0, NULL
);
391 Sci::Position startByte
, endByte
;
392 Sci::Position byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
394 switch (granularity
) {
395 case ATK_TEXT_GRANULARITY_CHAR
:
396 startByte
= byteOffset
;
397 endByte
= sci
->WndProc(SCI_POSITIONAFTER
, byteOffset
, 0);
399 case ATK_TEXT_GRANULARITY_WORD
:
400 startByte
= sci
->WndProc(SCI_WORDSTARTPOSITION
, byteOffset
, 1);
401 endByte
= sci
->WndProc(SCI_WORDENDPOSITION
, byteOffset
, 1);
403 case ATK_TEXT_GRANULARITY_LINE
: {
404 gint line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
405 startByte
= sci
->WndProc(SCI_POSITIONFROMLINE
, line
, 0);
406 endByte
= sci
->WndProc(SCI_GETLINEENDPOSITION
, line
, 0);
410 *startChar
= *endChar
= -1;
414 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
415 return GetTextRangeUTF8(startByte
, endByte
);
419 gunichar
ScintillaGTKAccessible::GetCharacterAtOffset(int charOffset
) {
420 g_return_val_if_fail(charOffset
>= 0, 0);
422 Sci::Position startByte
= ByteOffsetFromCharacterOffset(charOffset
);
423 Sci::Position endByte
= PositionAfter(startByte
);
424 gchar
*ch
= GetTextRangeUTF8(startByte
, endByte
);
425 gunichar unichar
= g_utf8_get_char_validated(ch
, -1);
431 gint
ScintillaGTKAccessible::GetCharacterCount() {
432 return sci
->pdoc
->CountCharacters(0, sci
->pdoc
->Length());
435 gint
ScintillaGTKAccessible::GetCaretOffset() {
436 return CharacterOffsetFromByteOffset(sci
->WndProc(SCI_GETCURRENTPOS
, 0, 0));
439 gboolean
ScintillaGTKAccessible::SetCaretOffset(int charOffset
) {
440 sci
->WndProc(SCI_GOTOPOS
, ByteOffsetFromCharacterOffset(charOffset
), 0);
444 gint
ScintillaGTKAccessible::GetOffsetAtPoint(gint x
, gint y
, AtkCoordType coords
) {
445 gint x_widget
, y_widget
, x_window
, y_window
;
446 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
448 GdkWindow
*window
= gtk_widget_get_window(widget
);
449 gdk_window_get_origin(window
, &x_widget
, &y_widget
);
450 if (coords
== ATK_XY_SCREEN
) {
453 } else if (coords
== ATK_XY_WINDOW
) {
454 window
= gdk_window_get_toplevel(window
);
455 gdk_window_get_origin(window
, &x_window
, &y_window
);
457 x
= x
- x_widget
+ x_window
;
458 y
= y
- y_widget
+ y_window
;
463 // FIXME: should we handle scrolling?
464 return CharacterOffsetFromByteOffset(sci
->WndProc(SCI_CHARPOSITIONFROMPOINTCLOSE
, x
, y
));
467 void ScintillaGTKAccessible::GetCharacterExtents(int charOffset
,
468 gint
*x
, gint
*y
, gint
*width
, gint
*height
, AtkCoordType coords
) {
469 *x
= *y
= *height
= *width
= 0;
471 Sci::Position byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
473 // FIXME: should we handle scrolling?
474 *x
= sci
->WndProc(SCI_POINTXFROMPOSITION
, 0, byteOffset
);
475 *y
= sci
->WndProc(SCI_POINTYFROMPOSITION
, 0, byteOffset
);
477 int line
= sci
->WndProc(SCI_LINEFROMPOSITION
, byteOffset
, 0);
478 *height
= sci
->WndProc(SCI_TEXTHEIGHT
, line
, 0);
480 int nextByteOffset
= PositionAfter(byteOffset
);
481 int next_x
= sci
->WndProc(SCI_POINTXFROMPOSITION
, 0, nextByteOffset
);
483 *width
= next_x
- *x
;
484 } else if (nextByteOffset
> byteOffset
) {
485 /* maybe next position was on the next line or something.
486 * just compute the expected character width */
487 int style
= StyleAt(byteOffset
, true);
488 int len
= nextByteOffset
- byteOffset
;
489 char *ch
= new char[len
+ 1];
490 sci
->pdoc
->GetCharRange(ch
, byteOffset
, len
);
492 *width
= sci
->TextWidth(style
, ch
);
496 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
497 GdkWindow
*window
= gtk_widget_get_window(widget
);
498 int x_widget
, y_widget
;
499 gdk_window_get_origin(window
, &x_widget
, &y_widget
);
500 if (coords
== ATK_XY_SCREEN
) {
503 } else if (coords
== ATK_XY_WINDOW
) {
504 window
= gdk_window_get_toplevel(window
);
505 int x_window
, y_window
;
506 gdk_window_get_origin(window
, &x_window
, &y_window
);
508 *x
+= x_widget
- x_window
;
509 *y
+= y_widget
- y_window
;
511 *x
= *y
= *height
= *width
= 0;
515 static AtkAttributeSet
*AddTextAttribute(AtkAttributeSet
*attributes
, AtkTextAttribute attr
, gchar
*value
) {
516 AtkAttribute
*at
= g_new(AtkAttribute
, 1);
517 at
->name
= g_strdup(atk_text_attribute_get_name(attr
));
520 return g_slist_prepend(attributes
, at
);
523 static AtkAttributeSet
*AddTextIntAttribute(AtkAttributeSet
*attributes
, AtkTextAttribute attr
, gint i
) {
524 return AddTextAttribute(attributes
, attr
, g_strdup(atk_text_attribute_get_value(attr
, i
)));
527 static AtkAttributeSet
*AddTextColorAttribute(AtkAttributeSet
*attributes
, AtkTextAttribute attr
, const ColourDesired
&colour
) {
528 return AddTextAttribute(attributes
, attr
,
529 g_strdup_printf("%u,%u,%u", colour
.GetRed() * 257, colour
.GetGreen() * 257, colour
.GetBlue() * 257));
532 AtkAttributeSet
*ScintillaGTKAccessible::GetAttributesForStyle(unsigned int styleNum
) {
533 AtkAttributeSet
*attr_set
= NULL
;
535 if (styleNum
>= sci
->vs
.styles
.size())
537 Style
&style
= sci
->vs
.styles
[styleNum
];
539 attr_set
= AddTextAttribute(attr_set
, ATK_TEXT_ATTR_FAMILY_NAME
, g_strdup(style
.fontName
));
540 attr_set
= AddTextAttribute(attr_set
, ATK_TEXT_ATTR_SIZE
, g_strdup_printf("%d", style
.size
/ SC_FONT_SIZE_MULTIPLIER
));
541 attr_set
= AddTextIntAttribute(attr_set
, ATK_TEXT_ATTR_WEIGHT
, CLAMP(style
.weight
, 100, 1000));
542 attr_set
= AddTextIntAttribute(attr_set
, ATK_TEXT_ATTR_STYLE
, style
.italic
? PANGO_STYLE_ITALIC
: PANGO_STYLE_NORMAL
);
543 attr_set
= AddTextIntAttribute(attr_set
, ATK_TEXT_ATTR_UNDERLINE
, style
.underline
? PANGO_UNDERLINE_SINGLE
: PANGO_UNDERLINE_NONE
);
544 attr_set
= AddTextColorAttribute(attr_set
, ATK_TEXT_ATTR_FG_COLOR
, style
.fore
);
545 attr_set
= AddTextColorAttribute(attr_set
, ATK_TEXT_ATTR_BG_COLOR
, style
.back
);
546 attr_set
= AddTextIntAttribute(attr_set
, ATK_TEXT_ATTR_INVISIBLE
, style
.visible
? 0 : 1);
547 attr_set
= AddTextIntAttribute(attr_set
, ATK_TEXT_ATTR_EDITABLE
, style
.changeable
? 1 : 0);
552 AtkAttributeSet
*ScintillaGTKAccessible::GetRunAttributes(int charOffset
, int *startChar
, int *endChar
) {
553 g_return_val_if_fail(charOffset
>= -1, NULL
);
555 Sci::Position byteOffset
;
556 if (charOffset
== -1) {
557 byteOffset
= sci
->WndProc(SCI_GETCURRENTPOS
, 0, 0);
559 byteOffset
= ByteOffsetFromCharacterOffset(charOffset
);
561 int length
= sci
->pdoc
->Length();
563 g_return_val_if_fail(byteOffset
<= length
, NULL
);
565 const char style
= StyleAt(byteOffset
, true);
566 // compute the range for this style
567 Sci::Position startByte
= byteOffset
;
568 // when going backwards, we know the style is already computed
569 while (startByte
> 0 && sci
->pdoc
->StyleAt((startByte
) - 1) == style
)
571 Sci::Position endByte
= byteOffset
+ 1;
572 while (endByte
< length
&& StyleAt(endByte
, true) == style
)
575 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
576 return GetAttributesForStyle((unsigned int) style
);
579 AtkAttributeSet
*ScintillaGTKAccessible::GetDefaultAttributes() {
580 return GetAttributesForStyle(0);
583 gint
ScintillaGTKAccessible::GetNSelections() {
584 return sci
->sel
.Empty() ? 0 : sci
->sel
.Count();
587 gchar
*ScintillaGTKAccessible::GetSelection(gint selection_num
, int *startChar
, int *endChar
) {
588 if (selection_num
< 0 || (unsigned int) selection_num
>= sci
->sel
.Count())
591 Sci::Position startByte
= sci
->sel
.Range(selection_num
).Start().Position();
592 Sci::Position endByte
= sci
->sel
.Range(selection_num
).End().Position();
594 CharacterRangeFromByteRange(startByte
, endByte
, startChar
, endChar
);
595 return GetTextRangeUTF8(startByte
, endByte
);
598 gboolean
ScintillaGTKAccessible::AddSelection(int startChar
, int endChar
) {
599 size_t n_selections
= sci
->sel
.Count();
600 Sci::Position startByte
, endByte
;
601 ByteRangeFromCharacterRange(startChar
, endChar
, startByte
, endByte
);
602 // use WndProc() to set the selections so it notifies as needed
603 if (n_selections
> 1 || ! sci
->sel
.Empty()) {
604 sci
->WndProc(SCI_ADDSELECTION
, startByte
, endByte
);
606 sci
->WndProc(SCI_SETSELECTION
, startByte
, endByte
);
612 gboolean
ScintillaGTKAccessible::RemoveSelection(gint selection_num
) {
613 size_t n_selections
= sci
->sel
.Count();
614 if (selection_num
< 0 || (unsigned int) selection_num
>= n_selections
)
617 if (n_selections
> 1) {
618 sci
->WndProc(SCI_DROPSELECTIONN
, selection_num
, 0);
619 } else if (sci
->sel
.Empty()) {
622 sci
->WndProc(SCI_CLEARSELECTIONS
, 0, 0);
628 gboolean
ScintillaGTKAccessible::SetSelection(gint selection_num
, int startChar
, int endChar
) {
629 if (selection_num
< 0 || (unsigned int) selection_num
>= sci
->sel
.Count())
632 Sci::Position startByte
, endByte
;
633 ByteRangeFromCharacterRange(startChar
, endChar
, startByte
, endByte
);
635 sci
->WndProc(SCI_SETSELECTIONNSTART
, selection_num
, startByte
);
636 sci
->WndProc(SCI_SETSELECTIONNEND
, selection_num
, endByte
);
641 void ScintillaGTKAccessible::AtkTextIface::init(::AtkTextIface
*iface
) {
642 iface
->get_text
= GetText
;
643 iface
->get_text_after_offset
= GetTextAfterOffset
;
644 iface
->get_text_at_offset
= GetTextAtOffset
;
645 iface
->get_text_before_offset
= GetTextBeforeOffset
;
646 #if ATK_CHECK_VERSION(2, 10, 0)
647 iface
->get_string_at_offset
= GetStringAtOffset
;
649 iface
->get_character_at_offset
= GetCharacterAtOffset
;
650 iface
->get_character_count
= GetCharacterCount
;
651 iface
->get_caret_offset
= GetCaretOffset
;
652 iface
->set_caret_offset
= SetCaretOffset
;
653 iface
->get_offset_at_point
= GetOffsetAtPoint
;
654 iface
->get_character_extents
= GetCharacterExtents
;
655 iface
->get_n_selections
= GetNSelections
;
656 iface
->get_selection
= GetSelection
;
657 iface
->add_selection
= AddSelection
;
658 iface
->remove_selection
= RemoveSelection
;
659 iface
->set_selection
= SetSelection
;
660 iface
->get_run_attributes
= GetRunAttributes
;
661 iface
->get_default_attributes
= GetDefaultAttributes
;
664 /* atkeditabletext.h */
666 void ScintillaGTKAccessible::SetTextContents(const gchar
*contents
) {
667 // FIXME: it's probably useless to check for READONLY here, SETTEXT probably does it just fine?
668 if (! sci
->pdoc
->IsReadOnly()) {
669 sci
->WndProc(SCI_SETTEXT
, 0, (sptr_t
) contents
);
673 bool ScintillaGTKAccessible::InsertStringUTF8(Sci::Position bytePos
, const gchar
*utf8
, Sci::Position lengthBytes
) {
674 if (sci
->pdoc
->IsReadOnly()) {
678 // like EncodedFromUTF8(), but avoids an extra copy
679 // FIXME: update target?
680 const char *charSetBuffer
;
681 if (sci
->IsUnicodeMode() || ! *(charSetBuffer
= sci
->CharacterSetID())) {
682 sci
->pdoc
->InsertString(bytePos
, utf8
, lengthBytes
);
685 std::string encoded
= ConvertText(utf8
, lengthBytes
, charSetBuffer
, "UTF-8", true);
686 sci
->pdoc
->InsertString(bytePos
, encoded
.c_str(), encoded
.length());
692 void ScintillaGTKAccessible::InsertText(const gchar
*text
, int lengthBytes
, int *charPosition
) {
693 Sci::Position bytePosition
= ByteOffsetFromCharacterOffset(*charPosition
);
695 // FIXME: should we update the target?
696 if (InsertStringUTF8(bytePosition
, text
, lengthBytes
)) {
697 (*charPosition
) += sci
->pdoc
->CountCharacters(bytePosition
, lengthBytes
);
701 void ScintillaGTKAccessible::CopyText(int startChar
, int endChar
) {
702 Sci::Position startByte
, endByte
;
703 ByteRangeFromCharacterRange(startChar
, endChar
, startByte
, endByte
);
704 sci
->CopyRangeToClipboard(startByte
, endByte
);
707 void ScintillaGTKAccessible::CutText(int startChar
, int endChar
) {
708 g_return_if_fail(endChar
>= startChar
);
710 if (! sci
->pdoc
->IsReadOnly()) {
711 // FIXME: have a byte variant of those and convert only once?
712 CopyText(startChar
, endChar
);
713 DeleteText(startChar
, endChar
);
717 void ScintillaGTKAccessible::DeleteText(int startChar
, int endChar
) {
718 g_return_if_fail(endChar
>= startChar
);
720 if (! sci
->pdoc
->IsReadOnly()) {
721 Sci::Position startByte
, endByte
;
722 ByteRangeFromCharacterRange(startChar
, endChar
, startByte
, endByte
);
724 if (! sci
->RangeContainsProtected(startByte
, endByte
)) {
725 // FIXME: restore the target?
726 sci
->pdoc
->DeleteChars(startByte
, endByte
- startByte
);
731 void ScintillaGTKAccessible::PasteText(int charPosition
) {
732 if (sci
->pdoc
->IsReadOnly())
735 // Helper class holding the position for the asynchronous paste operation.
736 // We can only hope that when the callback gets called scia is still valid, but ScintillaGTK
737 // has always done that without problems, so let's guess it's a fairly safe bet.
738 struct Helper
: GObjectWatcher
{
739 ScintillaGTKAccessible
*scia
;
740 Sci::Position bytePosition
;
742 void Destroyed() override
{
746 Helper(ScintillaGTKAccessible
*scia_
, Sci::Position bytePos_
) :
747 GObjectWatcher(G_OBJECT(scia_
->sci
->sci
)),
749 bytePosition(bytePos_
) {
752 void TextReceived(GtkClipboard
*, const gchar
*text
) {
754 size_t len
= strlen(text
);
755 std::string convertedText
;
756 if (len
> 0 && scia
->sci
->convertPastes
) {
757 // Convert line endings of the paste into our local line-endings mode
758 convertedText
= Document::TransformLineEnds(text
, len
, scia
->sci
->pdoc
->eolMode
);
759 len
= convertedText
.length();
760 text
= convertedText
.c_str();
762 scia
->InsertStringUTF8(bytePosition
, text
, static_cast<Sci::Position
>(len
));
766 static void TextReceivedCallback(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
) {
767 Helper
*helper
= reinterpret_cast<Helper
*>(data
);
769 if (helper
->scia
!= 0) {
770 helper
->TextReceived(clipboard
, text
);
777 Helper
*helper
= new Helper(this, ByteOffsetFromCharacterOffset(charPosition
));
778 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
779 GtkClipboard
*clipboard
= gtk_widget_get_clipboard(widget
, GDK_SELECTION_CLIPBOARD
);
780 gtk_clipboard_request_text(clipboard
, helper
->TextReceivedCallback
, helper
);
783 void ScintillaGTKAccessible::AtkEditableTextIface::init(::AtkEditableTextIface
*iface
) {
784 iface
->set_text_contents
= SetTextContents
;
785 iface
->insert_text
= InsertText
;
786 iface
->copy_text
= CopyText
;
787 iface
->cut_text
= CutText
;
788 iface
->delete_text
= DeleteText
;
789 iface
->paste_text
= PasteText
;
790 //~ iface->set_run_attributes = SetRunAttributes;
793 bool ScintillaGTKAccessible::Enabled() const {
794 return sci
->accessibilityEnabled
== SC_ACCESSIBILITY_ENABLED
;
799 void ScintillaGTKAccessible::UpdateCursor() {
800 Sci::Position pos
= sci
->WndProc(SCI_GETCURRENTPOS
, 0, 0);
801 if (old_pos
!= pos
) {
802 int charPosition
= CharacterOffsetFromByteOffset(pos
);
803 g_signal_emit_by_name(accessible
, "text-caret-moved", charPosition
);
807 size_t n_selections
= sci
->sel
.Count();
808 size_t prev_n_selections
= old_sels
.size();
809 bool selection_changed
= n_selections
!= prev_n_selections
;
811 old_sels
.resize(n_selections
);
812 for (size_t i
= 0; i
< n_selections
; i
++) {
813 SelectionRange
&sel
= sci
->sel
.Range(i
);
815 if (i
< prev_n_selections
&& ! selection_changed
) {
816 SelectionRange
&old_sel
= old_sels
[i
];
817 // do not consider a caret move to be a selection change
818 selection_changed
= ((! old_sel
.Empty() || ! sel
.Empty()) && ! (old_sel
== sel
));
824 if (selection_changed
)
825 g_signal_emit_by_name(accessible
, "text-selection-changed");
828 void ScintillaGTKAccessible::ChangeDocument(Document
*oldDoc
, Document
*newDoc
) {
833 if (oldDoc
== newDoc
) {
838 int charLength
= oldDoc
->CountCharacters(0, oldDoc
->Length());
839 g_signal_emit_by_name(accessible
, "text-changed::delete", 0, charLength
);
843 PLATFORM_ASSERT(newDoc
== sci
->pdoc
);
845 int charLength
= newDoc
->CountCharacters(0, newDoc
->Length());
846 g_signal_emit_by_name(accessible
, "text-changed::insert", 0, charLength
);
848 if ((oldDoc
? oldDoc
->IsReadOnly() : false) != newDoc
->IsReadOnly()) {
852 // update cursor and selection
859 void ScintillaGTKAccessible::NotifyReadOnly() {
860 bool readonly
= sci
->pdoc
->IsReadOnly();
861 atk_object_notify_state_change(ATK_OBJECT(accessible
), ATK_STATE_EDITABLE
, ! readonly
);
862 #if ATK_CHECK_VERSION(2, 16, 0)
863 atk_object_notify_state_change(ATK_OBJECT(accessible
), ATK_STATE_READ_ONLY
, readonly
);
867 void ScintillaGTKAccessible::SetAccessibility() {
868 // Called by ScintillaGTK when application has enabled or disabled accessibility
869 character_offsets
.resize(0);
870 character_offsets
.push_back(0);
873 void ScintillaGTKAccessible::Notify(GtkWidget
*, gint
, SCNotification
*nt
) {
876 switch (nt
->nmhdr
.code
) {
878 if (nt
->modificationType
& (SC_MOD_INSERTTEXT
| SC_MOD_DELETETEXT
)) {
879 // invalidate character offset cache if applicable
880 const Sci::Line line
= sci
->pdoc
->LineFromPosition(nt
->position
);
881 if (character_offsets
.size() > static_cast<size_t>(line
+ 1)) {
882 character_offsets
.resize(line
+ 1);
885 if (nt
->modificationType
& SC_MOD_INSERTTEXT
) {
886 int startChar
= CharacterOffsetFromByteOffset(nt
->position
);
887 int lengthChar
= sci
->pdoc
->CountCharacters(nt
->position
, nt
->position
+ nt
->length
);
888 g_signal_emit_by_name(accessible
, "text-changed::insert", startChar
, lengthChar
);
891 if (nt
->modificationType
& SC_MOD_BEFOREDELETE
) {
892 // We cannot compute the deletion length in DELETETEXT as it requires accessing the
893 // buffer, so that the character are still present. So, we cache the value here,
894 // and use it in DELETETEXT that fires quickly after.
895 deletionLengthChar
= sci
->pdoc
->CountCharacters(nt
->position
, nt
->position
+ nt
->length
);
897 if (nt
->modificationType
& SC_MOD_DELETETEXT
) {
898 int startChar
= CharacterOffsetFromByteOffset(nt
->position
);
899 g_signal_emit_by_name(accessible
, "text-changed::delete", startChar
, deletionLengthChar
);
902 if (nt
->modificationType
& SC_MOD_CHANGESTYLE
) {
903 g_signal_emit_by_name(accessible
, "text-attributes-changed");
907 if (nt
->updated
& SC_UPDATE_SELECTION
) {
914 // ATK method wrappers
916 // wraps a call from the accessible object to the ScintillaGTKAccessible, and avoid leaking any exception
917 #define WRAPPER_METHOD_BODY(accessible, call, defret) \
919 ScintillaGTKAccessible *thisAccessible = FromAccessible(reinterpret_cast<GtkAccessible*>(accessible)); \
920 if (thisAccessible) { \
921 return thisAccessible->call; \
930 gchar
*ScintillaGTKAccessible::AtkTextIface::GetText(AtkText
*text
, int start_offset
, int end_offset
) {
931 WRAPPER_METHOD_BODY(text
, GetText(start_offset
, end_offset
), NULL
);
933 gchar
*ScintillaGTKAccessible::AtkTextIface::GetTextAfterOffset(AtkText
*text
, int offset
, AtkTextBoundary boundary_type
, int *start_offset
, int *end_offset
) {
934 WRAPPER_METHOD_BODY(text
, GetTextAfterOffset(offset
, boundary_type
, start_offset
, end_offset
), NULL
)
936 gchar
*ScintillaGTKAccessible::AtkTextIface::GetTextBeforeOffset(AtkText
*text
, int offset
, AtkTextBoundary boundary_type
, int *start_offset
, int *end_offset
) {
937 WRAPPER_METHOD_BODY(text
, GetTextBeforeOffset(offset
, boundary_type
, start_offset
, end_offset
), NULL
)
939 gchar
*ScintillaGTKAccessible::AtkTextIface::GetTextAtOffset(AtkText
*text
, gint offset
, AtkTextBoundary boundary_type
, gint
*start_offset
, gint
*end_offset
) {
940 WRAPPER_METHOD_BODY(text
, GetTextAtOffset(offset
, boundary_type
, start_offset
, end_offset
), NULL
)
942 #if ATK_CHECK_VERSION(2, 10, 0)
943 gchar
*ScintillaGTKAccessible::AtkTextIface::GetStringAtOffset(AtkText
*text
, gint offset
, AtkTextGranularity granularity
, gint
*start_offset
, gint
*end_offset
) {
944 WRAPPER_METHOD_BODY(text
, GetStringAtOffset(offset
, granularity
, start_offset
, end_offset
), NULL
)
947 gunichar
ScintillaGTKAccessible::AtkTextIface::GetCharacterAtOffset(AtkText
*text
, gint offset
) {
948 WRAPPER_METHOD_BODY(text
, GetCharacterAtOffset(offset
), 0)
950 gint
ScintillaGTKAccessible::AtkTextIface::GetCharacterCount(AtkText
*text
) {
951 WRAPPER_METHOD_BODY(text
, GetCharacterCount(), 0)
953 gint
ScintillaGTKAccessible::AtkTextIface::GetCaretOffset(AtkText
*text
) {
954 WRAPPER_METHOD_BODY(text
, GetCaretOffset(), 0)
956 gboolean
ScintillaGTKAccessible::AtkTextIface::SetCaretOffset(AtkText
*text
, gint offset
) {
957 WRAPPER_METHOD_BODY(text
, SetCaretOffset(offset
), FALSE
)
959 gint
ScintillaGTKAccessible::AtkTextIface::GetOffsetAtPoint(AtkText
*text
, gint x
, gint y
, AtkCoordType coords
) {
960 WRAPPER_METHOD_BODY(text
, GetOffsetAtPoint(x
, y
, coords
), -1)
962 void ScintillaGTKAccessible::AtkTextIface::GetCharacterExtents(AtkText
*text
, gint offset
, gint
*x
, gint
*y
, gint
*width
, gint
*height
, AtkCoordType coords
) {
963 WRAPPER_METHOD_BODY(text
, GetCharacterExtents(offset
, x
, y
, width
, height
, coords
), )
965 AtkAttributeSet
*ScintillaGTKAccessible::AtkTextIface::GetRunAttributes(AtkText
*text
, gint offset
, gint
*start_offset
, gint
*end_offset
) {
966 WRAPPER_METHOD_BODY(text
, GetRunAttributes(offset
, start_offset
, end_offset
), NULL
)
968 AtkAttributeSet
*ScintillaGTKAccessible::AtkTextIface::GetDefaultAttributes(AtkText
*text
) {
969 WRAPPER_METHOD_BODY(text
, GetDefaultAttributes(), NULL
)
971 gint
ScintillaGTKAccessible::AtkTextIface::GetNSelections(AtkText
*text
) {
972 WRAPPER_METHOD_BODY(text
, GetNSelections(), 0)
974 gchar
*ScintillaGTKAccessible::AtkTextIface::GetSelection(AtkText
*text
, gint selection_num
, gint
*start_pos
, gint
*end_pos
) {
975 WRAPPER_METHOD_BODY(text
, GetSelection(selection_num
, start_pos
, end_pos
), NULL
)
977 gboolean
ScintillaGTKAccessible::AtkTextIface::AddSelection(AtkText
*text
, gint start
, gint end
) {
978 WRAPPER_METHOD_BODY(text
, AddSelection(start
, end
), FALSE
)
980 gboolean
ScintillaGTKAccessible::AtkTextIface::RemoveSelection(AtkText
*text
, gint selection_num
) {
981 WRAPPER_METHOD_BODY(text
, RemoveSelection(selection_num
), FALSE
)
983 gboolean
ScintillaGTKAccessible::AtkTextIface::SetSelection(AtkText
*text
, gint selection_num
, gint start
, gint end
) {
984 WRAPPER_METHOD_BODY(text
, SetSelection(selection_num
, start
, end
), FALSE
)
987 void ScintillaGTKAccessible::AtkEditableTextIface::SetTextContents(AtkEditableText
*text
, const gchar
*contents
) {
988 WRAPPER_METHOD_BODY(text
, SetTextContents(contents
), )
990 void ScintillaGTKAccessible::AtkEditableTextIface::InsertText(AtkEditableText
*text
, const gchar
*contents
, gint length
, gint
*position
) {
991 WRAPPER_METHOD_BODY(text
, InsertText(contents
, length
, position
), )
993 void ScintillaGTKAccessible::AtkEditableTextIface::CopyText(AtkEditableText
*text
, gint start
, gint end
) {
994 WRAPPER_METHOD_BODY(text
, CopyText(start
, end
), )
996 void ScintillaGTKAccessible::AtkEditableTextIface::CutText(AtkEditableText
*text
, gint start
, gint end
) {
997 WRAPPER_METHOD_BODY(text
, CutText(start
, end
), )
999 void ScintillaGTKAccessible::AtkEditableTextIface::DeleteText(AtkEditableText
*text
, gint start
, gint end
) {
1000 WRAPPER_METHOD_BODY(text
, DeleteText(start
, end
), )
1002 void ScintillaGTKAccessible::AtkEditableTextIface::PasteText(AtkEditableText
*text
, gint position
) {
1003 WRAPPER_METHOD_BODY(text
, PasteText(position
), )
1008 #if HAVE_GTK_FACTORY
1009 static GType
scintilla_object_accessible_factory_get_type(void);
1012 static void scintilla_object_accessible_init(ScintillaObjectAccessible
*accessible
);
1013 static void scintilla_object_accessible_class_init(ScintillaObjectAccessibleClass
*klass
);
1014 static gpointer scintilla_object_accessible_parent_class
= NULL
;
1017 // @p parent_type is only required on GTK 3.2 to 3.6, and only on the first call
1018 static GType
scintilla_object_accessible_get_type(GType parent_type G_GNUC_UNUSED
) {
1019 static volatile gsize type_id_result
= 0;
1021 if (g_once_init_enter(&type_id_result
)) {
1024 (GBaseInitFunc
) NULL
, /* base init */
1025 (GBaseFinalizeFunc
) NULL
, /* base finalize */
1026 (GClassInitFunc
) scintilla_object_accessible_class_init
, /* class init */
1027 (GClassFinalizeFunc
) NULL
, /* class finalize */
1028 NULL
, /* class data */
1029 0, /* instance size */
1030 0, /* nb preallocs */
1031 (GInstanceInitFunc
) scintilla_object_accessible_init
, /* instance init */
1032 NULL
/* value table */
1035 const GInterfaceInfo atk_text_info
= {
1036 (GInterfaceInitFunc
) ScintillaGTKAccessible::AtkTextIface::init
,
1037 (GInterfaceFinalizeFunc
) NULL
,
1041 const GInterfaceInfo atk_editable_text_info
= {
1042 (GInterfaceInitFunc
) ScintillaGTKAccessible::AtkEditableTextIface::init
,
1043 (GInterfaceFinalizeFunc
) NULL
,
1048 // good, we have gtk-a11y.h, we can use that
1049 GType derived_atk_type
= GTK_TYPE_CONTAINER_ACCESSIBLE
;
1050 tinfo
.class_size
= sizeof (GtkContainerAccessibleClass
);
1051 tinfo
.instance_size
= sizeof (GtkContainerAccessible
);
1052 #else // ! HAVE_GTK_A11Y_H
1053 # if HAVE_GTK_FACTORY
1054 // Figure out the size of the class and instance we are deriving from through the registry
1055 GType derived_type
= g_type_parent(SCINTILLA_TYPE_OBJECT
);
1056 AtkObjectFactory
*factory
= atk_registry_get_factory(atk_get_default_registry(), derived_type
);
1057 GType derived_atk_type
= atk_object_factory_get_accessible_type(factory
);
1058 # else // ! HAVE_GTK_FACTORY
1059 // We're kind of screwed and can't determine the parent (no registry, and no public type)
1060 // Hack your way around by requiring the caller to give us our parent type. The caller
1061 // might be able to trick its way into doing that, by e.g. instantiating the parent's
1062 // accessible type and get its GType. It's ugly but we can't do better on GTK 3.2 to 3.6.
1063 g_assert(parent_type
!= 0);
1065 GType derived_atk_type
= parent_type
;
1066 # endif // ! HAVE_GTK_FACTORY
1069 g_type_query(derived_atk_type
, &query
);
1070 tinfo
.class_size
= query
.class_size
;
1071 tinfo
.instance_size
= query
.instance_size
;
1072 #endif // ! HAVE_GTK_A11Y_H
1074 GType type_id
= g_type_register_static(derived_atk_type
, "ScintillaObjectAccessible", &tinfo
, (GTypeFlags
) 0);
1075 g_type_add_interface_static(type_id
, ATK_TYPE_TEXT
, &atk_text_info
);
1076 g_type_add_interface_static(type_id
, ATK_TYPE_EDITABLE_TEXT
, &atk_editable_text_info
);
1078 g_once_init_leave(&type_id_result
, type_id
);
1081 return type_id_result
;
1084 static AtkObject
*scintilla_object_accessible_new(GType parent_type
, GObject
*obj
) {
1085 g_return_val_if_fail(SCINTILLA_IS_OBJECT(obj
), NULL
);
1087 AtkObject
*accessible
= (AtkObject
*) g_object_new(scintilla_object_accessible_get_type(parent_type
),
1088 #if HAVE_WIDGET_SET_UNSET
1092 atk_object_initialize(accessible
, obj
);
1097 // implementation for gtk_widget_get_accessible().
1098 // See the comment at the top of the file for details on the implementation
1099 // @p widget the widget.
1100 // @p cache pointer to store the AtkObject between repeated calls. Might or might not be filled.
1101 // @p widget_parent_class pointer to the widget's parent class (to chain up method calls).
1102 AtkObject
*ScintillaGTKAccessible::WidgetGetAccessibleImpl(GtkWidget
*widget
, AtkObject
**cache
, gpointer widget_parent_class G_GNUC_UNUSED
) {
1103 if (*cache
!= NULL
) {
1107 #if HAVE_GTK_A11Y_H // just instantiate the accessible
1108 *cache
= scintilla_object_accessible_new(0, G_OBJECT(widget
));
1109 #elif HAVE_GTK_FACTORY // register in the factory and let GTK instantiate
1110 static volatile gsize registered
= 0;
1112 if (g_once_init_enter(®istered
)) {
1113 // Figure out whether accessibility is enabled by looking at the type of the accessible
1114 // object which would be created for the parent type of ScintillaObject.
1115 GType derived_type
= g_type_parent(SCINTILLA_TYPE_OBJECT
);
1117 AtkRegistry
*registry
= atk_get_default_registry();
1118 AtkObjectFactory
*factory
= atk_registry_get_factory(registry
, derived_type
);
1119 GType derived_atk_type
= atk_object_factory_get_accessible_type(factory
);
1120 if (g_type_is_a(derived_atk_type
, GTK_TYPE_ACCESSIBLE
)) {
1121 atk_registry_set_factory_type(registry
, SCINTILLA_TYPE_OBJECT
,
1122 scintilla_object_accessible_factory_get_type());
1124 g_once_init_leave(®istered
, 1);
1126 AtkObject
*obj
= GTK_WIDGET_CLASS(widget_parent_class
)->get_accessible(widget
);
1127 *cache
= static_cast<AtkObject
*>(g_object_ref(obj
));
1128 #else // no public API, no factory, so guess from the parent and instantiate
1129 static GType parent_atk_type
= 0;
1131 if (parent_atk_type
== 0) {
1132 AtkObject
*parent_obj
= GTK_WIDGET_CLASS(widget_parent_class
)->get_accessible(widget
);
1134 GType parent_atk_type
= G_OBJECT_TYPE(parent_obj
);
1136 // Figure out whether accessibility is enabled by looking at the type of the accessible
1137 // object which would be created for the parent type of ScintillaObject.
1138 if (g_type_is_a(parent_atk_type
, GTK_TYPE_ACCESSIBLE
)) {
1139 *cache
= scintilla_object_accessible_new(parent_atk_type
, G_OBJECT(widget
));
1141 *cache
= static_cast<AtkObject
*>(g_object_ref(parent_obj
));
1149 static AtkStateSet
*scintilla_object_accessible_ref_state_set(AtkObject
*accessible
) {
1150 AtkStateSet
*state_set
= ATK_OBJECT_CLASS(scintilla_object_accessible_parent_class
)->ref_state_set(accessible
);
1152 GtkWidget
*widget
= gtk_accessible_get_widget(GTK_ACCESSIBLE(accessible
));
1153 if (widget
== NULL
) {
1154 atk_state_set_add_state(state_set
, ATK_STATE_DEFUNCT
);
1156 if (! scintilla_send_message(SCINTILLA_OBJECT(widget
), SCI_GETREADONLY
, 0, 0))
1157 atk_state_set_add_state(state_set
, ATK_STATE_EDITABLE
);
1158 #if ATK_CHECK_VERSION(2, 16, 0)
1160 atk_state_set_add_state(state_set
, ATK_STATE_READ_ONLY
);
1162 atk_state_set_add_state(state_set
, ATK_STATE_MULTI_LINE
);
1163 atk_state_set_add_state(state_set
, ATK_STATE_MULTISELECTABLE
);
1164 atk_state_set_add_state(state_set
, ATK_STATE_SELECTABLE_TEXT
);
1165 /*atk_state_set_add_state(state_set, ATK_STATE_SUPPORTS_AUTOCOMPLETION);*/
1171 static void scintilla_object_accessible_widget_set(GtkAccessible
*accessible
) {
1172 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
1176 ScintillaObjectAccessiblePrivate
*priv
= SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible
);
1177 if (priv
->pscin
!= 0)
1179 priv
->pscin
= new ScintillaGTKAccessible(accessible
, widget
);
1182 #if HAVE_WIDGET_SET_UNSET
1183 static void scintilla_object_accessible_widget_unset(GtkAccessible
*accessible
) {
1184 GtkWidget
*widget
= gtk_accessible_get_widget(accessible
);
1188 ScintillaObjectAccessiblePrivate
*priv
= SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible
);
1194 static void scintilla_object_accessible_initialize(AtkObject
*obj
, gpointer data
) {
1195 ATK_OBJECT_CLASS(scintilla_object_accessible_parent_class
)->initialize(obj
, data
);
1197 #if ! HAVE_WIDGET_SET_UNSET
1198 scintilla_object_accessible_widget_set(GTK_ACCESSIBLE(obj
));
1201 obj
->role
= ATK_ROLE_TEXT
;
1204 static void scintilla_object_accessible_finalize(GObject
*object
) {
1205 ScintillaObjectAccessiblePrivate
*priv
= SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(object
);
1212 G_OBJECT_CLASS(scintilla_object_accessible_parent_class
)->finalize(object
);
1215 static void scintilla_object_accessible_class_init(ScintillaObjectAccessibleClass
*klass
) {
1216 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
1217 AtkObjectClass
*object_class
= ATK_OBJECT_CLASS(klass
);
1219 #if HAVE_WIDGET_SET_UNSET
1220 GtkAccessibleClass
*accessible_class
= GTK_ACCESSIBLE_CLASS(klass
);
1221 accessible_class
->widget_set
= scintilla_object_accessible_widget_set
;
1222 accessible_class
->widget_unset
= scintilla_object_accessible_widget_unset
;
1225 object_class
->ref_state_set
= scintilla_object_accessible_ref_state_set
;
1226 object_class
->initialize
= scintilla_object_accessible_initialize
;
1228 gobject_class
->finalize
= scintilla_object_accessible_finalize
;
1230 scintilla_object_accessible_parent_class
= g_type_class_peek_parent(klass
);
1232 g_type_class_add_private(klass
, sizeof (ScintillaObjectAccessiblePrivate
));
1235 static void scintilla_object_accessible_init(ScintillaObjectAccessible
*accessible
) {
1236 ScintillaObjectAccessiblePrivate
*priv
= SCINTILLA_OBJECT_ACCESSIBLE_GET_PRIVATE(accessible
);
1241 #if HAVE_GTK_FACTORY
1243 typedef AtkObjectFactory ScintillaObjectAccessibleFactory
;
1244 typedef AtkObjectFactoryClass ScintillaObjectAccessibleFactoryClass
;
1246 G_DEFINE_TYPE(ScintillaObjectAccessibleFactory
, scintilla_object_accessible_factory
, ATK_TYPE_OBJECT_FACTORY
)
1248 static void scintilla_object_accessible_factory_init(ScintillaObjectAccessibleFactory
*) {
1251 static GType
scintilla_object_accessible_factory_get_accessible_type(void) {
1252 return SCINTILLA_TYPE_OBJECT_ACCESSIBLE
;
1255 static AtkObject
*scintilla_object_accessible_factory_create_accessible(GObject
*obj
) {
1256 return scintilla_object_accessible_new(0, obj
);
1259 static void scintilla_object_accessible_factory_class_init(AtkObjectFactoryClass
* klass
) {
1260 klass
->create_accessible
= scintilla_object_accessible_factory_create_accessible
;
1261 klass
->get_accessible_type
= scintilla_object_accessible_factory_get_accessible_type
;