1/* gtkatspitext.c: Text interface for GtkAtspiContext
2 *
3 * Copyright 2020 Red Hat, Inc
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "config.h"
22
23#include "gtkatspitextprivate.h"
24
25#include "gtkatspiprivate.h"
26#include "gtkatspiutilsprivate.h"
27#include "gtkatspipangoprivate.h"
28#include "gtkatspitextbufferprivate.h"
29
30#include "a11y/atspi/atspi-text.h"
31
32#include "gtkatcontextprivate.h"
33#include "gtkdebug.h"
34#include "gtkeditable.h"
35#include "gtklabelprivate.h"
36#include "gtkentryprivate.h"
37#include "gtksearchentryprivate.h"
38#include "gtkpasswordentryprivate.h"
39#include "gtkspinbuttonprivate.h"
40#include "gtktextview.h"
41
42#include <gio/gio.h>
43
44/* {{{ GtkLabel */
45
46static void
47label_handle_method (GDBusConnection *connection,
48 const gchar *sender,
49 const gchar *object_path,
50 const gchar *interface_name,
51 const gchar *method_name,
52 GVariant *parameters,
53 GDBusMethodInvocation *invocation,
54 gpointer user_data)
55{
56 GtkATContext *self = user_data;
57 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
58 GtkWidget *widget = GTK_WIDGET (accessible);
59
60 if (g_strcmp0 (str1: method_name, str2: "GetCaretOffset") == 0)
61 {
62 int offset;
63
64 offset = _gtk_label_get_cursor_position (GTK_LABEL (widget));
65
66 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", offset));
67 }
68 else if (g_strcmp0 (str1: method_name, str2: "SetCaretOffset") == 0)
69 {
70 int offset;
71 gboolean ret;
72
73 g_variant_get (value: parameters, format_string: "(i)", &offset);
74
75 ret = gtk_label_get_selectable (GTK_LABEL (widget));
76 if (ret)
77 gtk_label_select_region (GTK_LABEL (widget), start_offset: offset, end_offset: offset);
78
79 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
80 }
81 else if (g_strcmp0 (str1: method_name, str2: "GetText") == 0)
82 {
83 int start, end;
84 const char *text;
85 int len;
86 char *string;
87
88 g_variant_get (value: parameters, format_string: "(ii)", &start, &end);
89
90 text = gtk_label_get_text (GTK_LABEL (widget));
91 len = g_utf8_strlen (p: text, max: -1);
92
93 start = CLAMP (start, 0, len);
94 end = CLAMP (end, 0, len);
95
96 if (end <= start)
97 string = g_strdup (str: "");
98 else
99 {
100 const char *p, *q;
101 p = g_utf8_offset_to_pointer (str: text, offset: start);
102 q = g_utf8_offset_to_pointer (str: text, offset: end);
103 string = g_strndup (str: p, n: q - p);
104 }
105
106 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(s)", string));
107 g_free (mem: string);
108 }
109 else if (g_strcmp0 (str1: method_name, str2: "GetTextBeforeOffset") == 0)
110 {
111 PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
112 int offset;
113 AtspiTextBoundaryType boundary_type;
114 char *string;
115 int start, end;
116
117 g_variant_get (value: parameters, format_string: "(iu)", &offset, &boundary_type);
118
119 string = gtk_pango_get_text_before (layout, offset, boundary_type, start_offset: &start, end_offset: &end);
120
121 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
122 g_free (mem: string);
123 }
124 else if (g_strcmp0 (str1: method_name, str2: "GetTextAtOffset") == 0)
125 {
126 PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
127 int offset;
128 AtspiTextBoundaryType boundary_type;
129 char *string;
130 int start, end;
131
132 g_variant_get (value: parameters, format_string: "(iu)", &offset, &boundary_type);
133
134 string = gtk_pango_get_text_at (layout, offset, boundary_type, start_offset: &start, end_offset: &end);
135
136 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
137 g_free (mem: string);
138 }
139 else if (g_strcmp0 (str1: method_name, str2: "GetTextAfterOffset") == 0)
140 {
141 PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
142 int offset;
143 AtspiTextBoundaryType boundary_type;
144 char *string;
145 int start, end;
146
147 g_variant_get (value: parameters, format_string: "(iu)", &offset, &boundary_type);
148
149 string = gtk_pango_get_text_after (layout, offset, boundary_type, start_offset: &start, end_offset: &end);
150
151 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
152 g_free (mem: string);
153 }
154 else if (g_strcmp0 (str1: method_name, str2: "GetCharacterAtOffset") == 0)
155 {
156 int offset;
157 const char *text;
158 gunichar ch = 0;
159
160 g_variant_get (value: parameters, format_string: "(i)", &offset);
161
162 text = gtk_label_get_text (GTK_LABEL (widget));
163
164 if (0 <= offset && offset < g_utf8_strlen (p: text, max: -1))
165 ch = g_utf8_get_char (p: g_utf8_offset_to_pointer (str: text, offset));
166
167 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", ch));
168 }
169 else if (g_strcmp0 (str1: method_name, str2: "GetStringAtOffset") == 0)
170 {
171 PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
172 int offset;
173 AtspiTextGranularity granularity;
174 char *string;
175 int start, end;
176
177 g_variant_get (value: parameters, format_string: "(iu)", &offset, &granularity);
178
179 string = gtk_pango_get_string_at (layout, offset, granularity, start_offset: &start, end_offset: &end);
180
181 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
182 g_free (mem: string);
183 }
184 else if (g_strcmp0 (str1: method_name, str2: "GetAttributes") == 0)
185 {
186 PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
187 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
188 int offset;
189 int start, end;
190
191 g_variant_get (value: parameters, format_string: "(i)", &offset);
192
193 gtk_pango_get_run_attributes (layout, builder: &builder, offset, start_offset: &start, end_offset: &end);
194
195 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a{ss}ii)", &builder, start, end));
196 }
197 else if (g_strcmp0 (str1: method_name, str2: "GetAttributeValue") == 0)
198 {
199 PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
200 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
201 int offset;
202 const char *name;
203 int start, end;
204 GVariant *attrs;
205 const char *val;
206
207 g_variant_get (value: parameters, format_string: "(i&s)", &offset, &name);
208
209 gtk_pango_get_run_attributes (layout, builder: &builder, offset, start_offset: &start, end_offset: &end);
210
211 attrs = g_variant_builder_end (builder: &builder);
212 if (!g_variant_lookup (dictionary: attrs, key: name, format_string: "&s", &val))
213 val = "";
214
215 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(s)", val));
216 g_variant_unref (value: attrs);
217 }
218 else if (g_strcmp0 (str1: method_name, str2: "GetAttributeRun") == 0)
219 {
220 PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
221 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
222 int offset;
223 gboolean include_defaults;
224 int start, end;
225
226 g_variant_get (value: parameters, format_string: "(ib)", &offset, &include_defaults);
227
228 if (include_defaults)
229 gtk_pango_get_default_attributes (layout, builder: &builder);
230
231 gtk_pango_get_run_attributes (layout, builder: &builder, offset, start_offset: &start, end_offset: &end);
232
233 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a{ss}ii)", &builder, start, end));
234 }
235 else if (g_strcmp0 (str1: method_name, str2: "GetDefaultAttributes") == 0 ||
236 g_strcmp0 (str1: method_name, str2: "GetDefaultAttributeSet") == 0)
237 {
238 PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
239 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
240
241 gtk_pango_get_default_attributes (layout, builder: &builder);
242
243 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a{ss})", &builder));
244 }
245 else if (g_strcmp0 (str1: method_name, str2: "GetNSelections") == 0)
246 {
247 int n = 0;
248
249 if (gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL))
250 n = 1;
251
252 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", n));
253 }
254 else if (g_strcmp0 (str1: method_name, str2: "GetSelection") == 0)
255 {
256 int num;
257 int start, end;
258 gboolean ret = TRUE;
259
260 g_variant_get (value: parameters, format_string: "(i)", &num);
261
262 if (num != 0)
263 ret = FALSE;
264 else
265 {
266 if (!gtk_label_get_selection_bounds (GTK_LABEL (widget), start: &start, end: &end))
267 ret = FALSE;
268 }
269
270 if (!ret)
271 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "Not a valid selection: %d", num);
272 else
273 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(ii)", start, end));
274 }
275 else if (g_strcmp0 (str1: method_name, str2: "AddSelection") == 0)
276 {
277 int start, end;
278 gboolean ret;
279
280 g_variant_get (value: parameters, format_string: "(ii)", &start, &end);
281
282 if (!gtk_label_get_selectable (GTK_LABEL (widget)) ||
283 gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL))
284 {
285 ret = FALSE;
286 }
287 else
288 {
289 gtk_label_select_region (GTK_LABEL (widget), start_offset: start, end_offset: end);
290 ret = TRUE;
291 }
292
293 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
294 }
295 else if (g_strcmp0 (str1: method_name, str2: "RemoveSelection") == 0)
296 {
297 int num;
298 int start, end;
299 gboolean ret;
300
301 g_variant_get (value: parameters, format_string: "(i)", &num);
302
303 if (num != 0)
304 ret = FALSE;
305 else
306 {
307 if (!gtk_label_get_selectable (GTK_LABEL (widget)) ||
308 !gtk_label_get_selection_bounds (GTK_LABEL (widget), start: &start, end: &end))
309 {
310 ret = FALSE;
311 }
312 else
313 {
314 gtk_label_select_region (GTK_LABEL (widget), start_offset: end, end_offset: end);
315 ret = TRUE;
316 }
317 }
318
319 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
320 }
321 else if (g_strcmp0 (str1: method_name, str2: "SetSelection") == 0)
322 {
323 int num;
324 int start, end;
325 gboolean ret;
326
327 g_variant_get (value: parameters, format_string: "(iii)", &num, &start, &end);
328
329 if (num != 0)
330 ret = FALSE;
331 else
332 {
333 if (!gtk_label_get_selectable (GTK_LABEL (widget)) ||
334 !gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL))
335 {
336 ret = FALSE;
337 }
338 else
339 {
340 gtk_label_select_region (GTK_LABEL (widget), start_offset: start, end_offset: end);
341 ret = TRUE;
342 }
343 }
344 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
345 }
346 else if (g_strcmp0 (str1: method_name, str2: "GetCharacterExtents") == 0)
347 {
348 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
349 }
350 else if (g_strcmp0 (str1: method_name, str2: "GetRangeExtents") == 0)
351 {
352 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
353 }
354 else if (g_strcmp0 (str1: method_name, str2: "GetBoundedRanges") == 0)
355 {
356 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
357 }
358 else if (g_strcmp0 (str1: method_name, str2: "ScrollSubstringTo") == 0)
359 {
360 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
361 }
362 else if (g_strcmp0 (str1: method_name, str2: "ScrollSubstringToPoint") == 0)
363 {
364 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
365 }
366}
367
368static GVariant *
369label_get_property (GDBusConnection *connection,
370 const gchar *sender,
371 const gchar *object_path,
372 const gchar *interface_name,
373 const gchar *property_name,
374 GError **error,
375 gpointer user_data)
376{
377 GtkATContext *self = user_data;
378 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
379 GtkWidget *widget = GTK_WIDGET (accessible);
380
381 if (g_strcmp0 (str1: property_name, str2: "CharacterCount") == 0)
382 {
383 const char *text;
384 int len;
385
386 text = gtk_label_get_text (GTK_LABEL (widget));
387 len = g_utf8_strlen (p: text, max: -1);
388
389 return g_variant_new_int32 (value: len);
390 }
391 else if (g_strcmp0 (str1: property_name, str2: "CaretOffset") == 0)
392 {
393 int offset;
394
395 offset = _gtk_label_get_cursor_position (GTK_LABEL (widget));
396
397 return g_variant_new_int32 (value: offset);
398 }
399
400 return NULL;
401}
402
403static const GDBusInterfaceVTable label_vtable = {
404 label_handle_method,
405 label_get_property,
406 NULL,
407};
408
409/* }}} */
410/* {{{ GtkEditable */
411
412static GtkText *
413gtk_editable_get_text_widget (GtkWidget *widget)
414{
415 if (GTK_IS_EDITABLE (widget))
416 {
417 GtkEditable *delegate;
418
419 delegate = gtk_editable_get_delegate (GTK_EDITABLE (widget));
420
421 if (GTK_IS_TEXT (delegate))
422 return GTK_TEXT (delegate);
423 }
424
425 return NULL;
426}
427
428static void
429editable_handle_method (GDBusConnection *connection,
430 const gchar *sender,
431 const gchar *object_path,
432 const gchar *interface_name,
433 const gchar *method_name,
434 GVariant *parameters,
435 GDBusMethodInvocation *invocation,
436 gpointer user_data)
437{
438 GtkATContext *self = user_data;
439 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
440 GtkWidget *widget = GTK_WIDGET (accessible);
441 GtkText *text_widget = gtk_editable_get_text_widget (widget);
442
443 if (g_strcmp0 (str1: method_name, str2: "GetCaretOffset") == 0)
444 {
445 int offset;
446
447 offset = gtk_editable_get_position (GTK_EDITABLE (widget));
448
449 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", offset));
450 }
451 else if (g_strcmp0 (str1: method_name, str2: "SetCaretOffset") == 0)
452 {
453 int offset;
454 gboolean ret;
455
456 g_variant_get (value: parameters, format_string: "(i)", &offset);
457
458 gtk_editable_set_position (GTK_EDITABLE (widget), position: offset);
459 ret = TRUE;
460
461 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
462 }
463 else if (g_strcmp0 (str1: method_name, str2: "GetText") == 0)
464 {
465 int start, end;
466 const char *text;
467 int len;
468 char *string;
469
470 g_variant_get (value: parameters, format_string: "(ii)", &start, &end);
471
472 text = gtk_editable_get_text (GTK_EDITABLE (widget));
473 len = g_utf8_strlen (p: text, max: -1);
474
475 start = CLAMP (start, 0, len);
476 end = CLAMP (end, 0, len);
477
478 if (end <= start)
479 string = g_strdup (str: "");
480 else
481 {
482 const char *p, *q;
483 p = g_utf8_offset_to_pointer (str: text, offset: start);
484 q = g_utf8_offset_to_pointer (str: text, offset: end);
485 string = g_strndup (str: p, n: q - p);
486 }
487
488 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(s)", string));
489 g_free (mem: string);
490 }
491 else if (g_strcmp0 (str1: method_name, str2: "GetTextBeforeOffset") == 0)
492 {
493 PangoLayout *layout = gtk_text_get_layout (entry: text_widget);
494 int offset;
495 AtspiTextBoundaryType boundary_type;
496 char *string;
497 int start, end;
498
499 g_variant_get (value: parameters, format_string: "(iu)", &offset, &boundary_type);
500
501 string = gtk_pango_get_text_before (layout, offset, boundary_type, start_offset: &start, end_offset: &end);
502
503 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
504 g_free (mem: string);
505 }
506 else if (g_strcmp0 (str1: method_name, str2: "GetTextAtOffset") == 0)
507 {
508 PangoLayout *layout = gtk_text_get_layout (entry: text_widget);
509 int offset;
510 AtspiTextBoundaryType boundary_type;
511 char *string;
512 int start, end;
513
514 g_variant_get (value: parameters, format_string: "(iu)", &offset, &boundary_type);
515
516 string = gtk_pango_get_text_at (layout, offset, boundary_type, start_offset: &start, end_offset: &end);
517
518 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
519 g_free (mem: string);
520 }
521 else if (g_strcmp0 (str1: method_name, str2: "GetTextAfterOffset") == 0)
522 {
523 PangoLayout *layout = gtk_text_get_layout (entry: text_widget);
524 int offset;
525 AtspiTextBoundaryType boundary_type;
526 char *string;
527 int start, end;
528
529 g_variant_get (value: parameters, format_string: "(iu)", &offset, &boundary_type);
530
531 string = gtk_pango_get_text_after (layout, offset, boundary_type, start_offset: &start, end_offset: &end);
532
533 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
534 g_free (mem: string);
535 }
536 else if (g_strcmp0 (str1: method_name, str2: "GetCharacterAtOffset") == 0)
537 {
538 int offset;
539 const char *text;
540 gunichar ch = 0;
541
542 g_variant_get (value: parameters, format_string: "(i)", &offset);
543
544 text = gtk_editable_get_text (GTK_EDITABLE (widget));
545 if (0 <= offset && offset < g_utf8_strlen (p: text, max: -1))
546 ch = g_utf8_get_char (p: g_utf8_offset_to_pointer (str: text, offset));
547
548 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", ch));
549 }
550 else if (g_strcmp0 (str1: method_name, str2: "GetStringAtOffset") == 0)
551 {
552 PangoLayout *layout = gtk_text_get_layout (entry: text_widget);
553 int offset;
554 AtspiTextGranularity granularity;
555 char *string;
556 int start, end;
557
558 g_variant_get (value: parameters, format_string: "(iu)", &offset, &granularity);
559
560 string = gtk_pango_get_string_at (layout, offset, granularity, start_offset: &start, end_offset: &end);
561
562 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
563 g_free (mem: string);
564 }
565 else if (g_strcmp0 (str1: method_name, str2: "GetAttributes") == 0)
566 {
567 PangoLayout *layout = gtk_text_get_layout (entry: text_widget);
568 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
569 int offset;
570 int start, end;
571
572 g_variant_get (value: parameters, format_string: "(i)", &offset);
573
574 gtk_pango_get_run_attributes (layout, builder: &builder, offset, start_offset: &start, end_offset: &end);
575
576 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a{ss}ii)", &builder, start, end));
577 }
578 else if (g_strcmp0 (str1: method_name, str2: "GetAttributeValue") == 0)
579 {
580 PangoLayout *layout = gtk_text_get_layout (entry: text_widget);
581 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
582 int offset;
583 const char *name;
584 int start, end;
585 GVariant *attrs;
586 const char *val;
587
588 g_variant_get (value: parameters, format_string: "(i&s)", &offset, &name);
589
590 gtk_pango_get_run_attributes (layout, builder: &builder, offset, start_offset: &start, end_offset: &end);
591 attrs = g_variant_builder_end (builder: &builder);
592 if (!g_variant_lookup (dictionary: attrs, key: name, format_string: "&s", &val))
593 val = "";
594
595 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(s)", val));
596 g_variant_unref (value: attrs);
597 }
598 else if (g_strcmp0 (str1: method_name, str2: "GetAttributeRun") == 0)
599 {
600 PangoLayout *layout = gtk_text_get_layout (entry: text_widget);
601 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
602 int offset;
603 gboolean include_defaults;
604 int start, end;
605
606 g_variant_get (value: parameters, format_string: "(ib)", &offset, &include_defaults);
607
608 if (include_defaults)
609 gtk_pango_get_default_attributes (layout, builder: &builder);
610
611 gtk_pango_get_run_attributes (layout, builder: &builder, offset, start_offset: &start, end_offset: &end);
612
613 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a{ss}ii)", &builder, start, end));
614 }
615 else if (g_strcmp0 (str1: method_name, str2: "GetDefaultAttributes") == 0 ||
616 g_strcmp0 (str1: method_name, str2: "GetDefaultAttributeSet") == 0)
617 {
618 PangoLayout *layout = gtk_text_get_layout (entry: text_widget);
619 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
620
621 gtk_pango_get_default_attributes (layout, builder: &builder);
622
623 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a{ss})", &builder));
624 }
625 else if (g_strcmp0 (str1: method_name, str2: "GetNSelections") == 0)
626 {
627 int n = 0;
628
629 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), NULL, NULL))
630 n = 1;
631
632 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", n));
633 }
634 else if (g_strcmp0 (str1: method_name, str2: "GetSelection") == 0)
635 {
636 int num;
637 int start, end;
638 gboolean ret = TRUE;
639
640 g_variant_get (value: parameters, format_string: "(i)", &num);
641
642 if (num != 0)
643 ret = FALSE;
644 else
645 {
646 if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), start_pos: &start, end_pos: &end))
647 ret = FALSE;
648 }
649
650 if (!ret)
651 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "Not a valid selection: %d", num);
652 else
653 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(ii)", start, end));
654 }
655 else if (g_strcmp0 (str1: method_name, str2: "AddSelection") == 0)
656 {
657 int start, end;
658 gboolean ret;
659
660 g_variant_get (value: parameters, format_string: "(ii)", &start, &end);
661
662 if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), NULL, NULL))
663 {
664 ret = FALSE;
665 }
666 else
667 {
668 gtk_editable_select_region (GTK_EDITABLE (widget), start_pos: start, end_pos: end);
669 ret = TRUE;
670 }
671
672 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
673 }
674 else if (g_strcmp0 (str1: method_name, str2: "RemoveSelection") == 0)
675 {
676 int num;
677 int start, end;
678 gboolean ret;
679
680 g_variant_get (value: parameters, format_string: "(i)", &num);
681
682 if (num != 0)
683 ret = FALSE;
684 else
685 {
686 if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), start_pos: &start, end_pos: &end))
687 {
688 ret = FALSE;
689 }
690 else
691 {
692 gtk_editable_select_region (GTK_EDITABLE (widget), start_pos: end, end_pos: end);
693 ret = TRUE;
694 }
695 }
696
697 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
698 }
699 else if (g_strcmp0 (str1: method_name, str2: "SetSelection") == 0)
700 {
701 int num;
702 int start, end;
703 gboolean ret;
704
705 g_variant_get (value: parameters, format_string: "(iii)", &num, &start, &end);
706
707 if (num != 0)
708 ret = FALSE;
709 else
710 {
711 if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), NULL, NULL))
712 {
713 ret = FALSE;
714 }
715 else
716 {
717 gtk_editable_select_region (GTK_EDITABLE (widget), start_pos: start, end_pos: end);
718 ret = TRUE;
719 }
720 }
721 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
722 }
723 else if (g_strcmp0 (str1: method_name, str2: "GetCharacterExtents") == 0)
724 {
725 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
726 }
727 else if (g_strcmp0 (str1: method_name, str2: "GetRangeExtents") == 0)
728 {
729 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
730 }
731 else if (g_strcmp0 (str1: method_name, str2: "GetBoundedRanges") == 0)
732 {
733 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
734 }
735 else if (g_strcmp0 (str1: method_name, str2: "ScrollSubstringTo") == 0)
736 {
737 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
738 }
739 else if (g_strcmp0 (str1: method_name, str2: "ScrollSubstringToPoint") == 0)
740 {
741 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
742 }
743}
744
745static GVariant *
746editable_get_property (GDBusConnection *connection,
747 const gchar *sender,
748 const gchar *object_path,
749 const gchar *interface_name,
750 const gchar *property_name,
751 GError **error,
752 gpointer user_data)
753{
754 GtkATContext *self = user_data;
755 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
756 GtkWidget *widget = GTK_WIDGET (accessible);
757
758 if (g_strcmp0 (str1: property_name, str2: "CharacterCount") == 0)
759 {
760 const char *text;
761 int len;
762
763 text = gtk_editable_get_text (GTK_EDITABLE (widget));
764 len = g_utf8_strlen (p: text, max: -1);
765
766 return g_variant_new_int32 (value: len);
767 }
768 else if (g_strcmp0 (str1: property_name, str2: "CaretOffset") == 0)
769 {
770 int offset;
771
772 offset = gtk_editable_get_position (GTK_EDITABLE (widget));
773
774 return g_variant_new_int32 (value: offset);
775 }
776
777 return NULL;
778}
779
780static const GDBusInterfaceVTable editable_vtable = {
781 editable_handle_method,
782 editable_get_property,
783 NULL,
784};
785
786/* }}} */
787/* {{{ GtkTextView */
788
789static void
790text_view_handle_method (GDBusConnection *connection,
791 const gchar *sender,
792 const gchar *object_path,
793 const gchar *interface_name,
794 const gchar *method_name,
795 GVariant *parameters,
796 GDBusMethodInvocation *invocation,
797 gpointer user_data)
798{
799 GtkATContext *self = user_data;
800 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
801 GtkWidget *widget = GTK_WIDGET (accessible);
802
803 if (g_strcmp0 (str1: method_name, str2: "GetCaretOffset") == 0)
804 {
805 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
806 GtkTextMark *insert;
807 GtkTextIter iter;
808 int offset;
809
810 insert = gtk_text_buffer_get_insert (buffer);
811 gtk_text_buffer_get_iter_at_mark (buffer, iter: &iter, mark: insert);
812 offset = gtk_text_iter_get_offset (iter: &iter);
813
814 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", offset));
815 }
816 else if (g_strcmp0 (str1: method_name, str2: "SetCaretOffset") == 0)
817 {
818 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
819 GtkTextIter iter;
820 int offset;
821
822 g_variant_get (value: parameters, format_string: "(i)", &offset);
823
824 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: offset);
825 gtk_text_buffer_place_cursor (buffer, where: &iter);
826 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (widget), iter: &iter, within_margin: 0, FALSE, xalign: 0, yalign: 0);
827
828 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", TRUE));
829 }
830 else if (g_strcmp0 (str1: method_name, str2: "GetText") == 0)
831 {
832 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
833 GtkTextIter start_iter, end_iter;
834 int start, end;
835 char *string;
836
837 g_variant_get (value: parameters, format_string: "(ii)", &start, &end);
838
839 gtk_text_buffer_get_iter_at_offset (buffer, iter: &start_iter, char_offset: start);
840 gtk_text_buffer_get_iter_at_offset (buffer, iter: &end_iter, char_offset: end);
841
842 string = gtk_text_buffer_get_text (buffer, start: &start_iter, end: &end_iter, FALSE);
843
844 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(s)", string));
845 g_free (mem: string);
846 }
847 else if (g_strcmp0 (str1: method_name, str2: "GetTextBeforeOffset") == 0)
848 {
849 int offset;
850 AtspiTextBoundaryType boundary_type;
851 char *string;
852 int start, end;
853
854 g_variant_get (value: parameters, format_string: "(iu)", &offset, &boundary_type);
855
856 string = gtk_text_view_get_text_before (GTK_TEXT_VIEW (widget), offset, boundary_type, start_offset: &start, end_offset: &end);
857
858 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
859 g_free (mem: string);
860 }
861 else if (g_strcmp0 (str1: method_name, str2: "GetTextAtOffset") == 0)
862 {
863 int offset;
864 AtspiTextBoundaryType boundary_type;
865 char *string;
866 int start, end;
867
868 g_variant_get (value: parameters, format_string: "(iu)", &offset, &boundary_type);
869
870 string = gtk_text_view_get_text_at (GTK_TEXT_VIEW (widget), offset, boundary_type, start_offset: &start, end_offset: &end);
871
872 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
873 g_free (mem: string);
874 }
875 else if (g_strcmp0 (str1: method_name, str2: "GetTextAfterOffset") == 0)
876 {
877 int offset;
878 AtspiTextBoundaryType boundary_type;
879 char *string;
880 int start, end;
881
882 g_variant_get (value: parameters, format_string: "(iu)", &offset, &boundary_type);
883
884 string = gtk_text_view_get_text_after (GTK_TEXT_VIEW (widget), offset, boundary_type, start_offset: &start, end_offset: &end);
885
886 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
887 g_free (mem: string);
888 }
889 else if (g_strcmp0 (str1: method_name, str2: "GetCharacterAtOffset") == 0)
890 {
891 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
892 int offset;
893 gunichar ch = 0;
894
895 g_variant_get (value: parameters, format_string: "(i)", &offset);
896
897 if (offset >= 0 && offset < gtk_text_buffer_get_char_count (buffer))
898 {
899 GtkTextIter start, end;
900 char *string;
901 gtk_text_buffer_get_iter_at_offset (buffer, iter: &start, char_offset: offset);
902 end = start;
903 gtk_text_iter_forward_char (iter: &end);
904 string = gtk_text_buffer_get_slice (buffer, start: &start, end: &end, FALSE);
905 ch = g_utf8_get_char (p: string);
906 g_free (mem: string);
907 }
908
909 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", ch));
910 }
911 else if (g_strcmp0 (str1: method_name, str2: "GetStringAtOffset") == 0)
912 {
913 int offset;
914 AtspiTextGranularity granularity;
915 char *string;
916 int start, end;
917
918 g_variant_get (value: parameters, format_string: "(iu)", &offset, &granularity);
919
920 string = gtk_text_view_get_string_at (GTK_TEXT_VIEW (widget), offset, granularity, start_offset: &start, end_offset: &end);
921
922 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(sii)", string, start, end));
923 g_free (mem: string);
924 }
925 else if (g_strcmp0 (str1: method_name, str2: "GetAttributes") == 0)
926 {
927 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
928 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
929 int offset;
930 int start, end;
931
932 g_variant_get (value: parameters, format_string: "(i)", &offset);
933
934 gtk_text_buffer_get_run_attributes (buffer, builder: &builder, offset, start_offset: &start, end_offset: &end);
935
936 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a{ss}ii)", &builder, start, end));
937 }
938 else if (g_strcmp0 (str1: method_name, str2: "GetAttributeValue") == 0)
939 {
940 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
941 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
942 int offset;
943 const char *name;
944 int start, end;
945 GVariant *attrs;
946 const char *val;
947
948 g_variant_get (value: parameters, format_string: "(i&s)", &offset, &name);
949
950 gtk_text_buffer_get_run_attributes (buffer, builder: &builder, offset, start_offset: &start, end_offset: &end);
951
952 attrs = g_variant_builder_end (builder: &builder);
953 if (!g_variant_lookup (dictionary: attrs, key: name, format_string: "&s", &val))
954 val = "";
955
956 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(s)", val));
957 g_variant_unref (value: attrs);
958 }
959 else if (g_strcmp0 (str1: method_name, str2: "GetAttributeRun") == 0)
960 {
961 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
962 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
963 int offset;
964 gboolean include_defaults;
965 int start, end;
966
967 g_variant_get (value: parameters, format_string: "(ib)", &offset, &include_defaults);
968
969 if (include_defaults)
970 gtk_text_view_add_default_attributes (GTK_TEXT_VIEW (widget), builder: &builder);
971
972 gtk_text_buffer_get_run_attributes (buffer, builder: &builder, offset, start_offset: &start, end_offset: &end);
973
974 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a{ss}ii)", &builder, start, end));
975 }
976 else if (g_strcmp0 (str1: method_name, str2: "GetDefaultAttributes") == 0 ||
977 g_strcmp0 (str1: method_name, str2: "GetDefaultAttributeSet") == 0)
978 {
979 GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
980
981 gtk_text_view_add_default_attributes (GTK_TEXT_VIEW (widget), builder: &builder);
982
983 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(a{ss})", &builder));
984 }
985 else if (g_strcmp0 (str1: method_name, str2: "GetNSelections") == 0)
986 {
987 int n = 0;
988
989 if (gtk_text_buffer_get_selection_bounds (buffer: gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)), NULL, NULL))
990 n = 1;
991
992 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", n));
993 }
994 else if (g_strcmp0 (str1: method_name, str2: "GetSelection") == 0)
995 {
996 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
997 GtkTextIter start_iter, end_iter;
998 int num;
999 int start, end;
1000 gboolean ret = TRUE;
1001
1002 g_variant_get (value: parameters, format_string: "(i)", &num);
1003
1004 if (num != 0)
1005 ret = FALSE;
1006 else
1007 {
1008 if (!gtk_text_buffer_get_selection_bounds (buffer, start: &start_iter, end: &end_iter))
1009 ret = FALSE;
1010 else
1011 {
1012 start = gtk_text_iter_get_offset (iter: &start_iter);
1013 end = gtk_text_iter_get_offset (iter: &end_iter);
1014 }
1015 }
1016
1017 if (!ret)
1018 g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_INVALID_ARGS, format: "Not a valid selection: %d", num);
1019 else
1020 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(ii)", start, end));
1021 }
1022 else if (g_strcmp0 (str1: method_name, str2: "AddSelection") == 0)
1023 {
1024 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1025 GtkTextIter start_iter, end_iter;
1026 int start, end;
1027 gboolean ret;
1028
1029 g_variant_get (value: parameters, format_string: "(ii)", &start, &end);
1030
1031 if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
1032 {
1033 ret = FALSE;
1034 }
1035 else
1036 {
1037 gtk_text_buffer_get_iter_at_offset (buffer, iter: &start_iter, char_offset: start);
1038 gtk_text_buffer_get_iter_at_offset (buffer, iter: &end_iter, char_offset: end);
1039 gtk_text_buffer_select_range (buffer, ins: &start_iter, bound: &end_iter);
1040 ret = TRUE;
1041 }
1042
1043 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(i)", ret));
1044 }
1045 else if (g_strcmp0 (str1: method_name, str2: "RemoveSelection") == 0)
1046 {
1047 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1048 GtkTextIter start_iter, end_iter;
1049 int num;
1050 gboolean ret;
1051
1052 g_variant_get (value: parameters, format_string: "(i)", &num);
1053
1054 if (num != 0)
1055 ret = FALSE;
1056 else
1057 {
1058 if (!gtk_text_buffer_get_selection_bounds (buffer, start: &start_iter, end: &end_iter))
1059 {
1060 ret = FALSE;
1061 }
1062 else
1063 {
1064 gtk_text_buffer_select_range (buffer, ins: &end_iter, bound: &end_iter);
1065 ret = TRUE;
1066 }
1067 }
1068
1069 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
1070 }
1071 else if (g_strcmp0 (str1: method_name, str2: "SetSelection") == 0)
1072 {
1073 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1074 GtkTextIter start_iter, end_iter;
1075 int num;
1076 int start, end;
1077 gboolean ret;
1078
1079 g_variant_get (value: parameters, format_string: "(iii)", &num, &start, &end);
1080
1081 if (num != 0)
1082 ret = FALSE;
1083 else
1084 {
1085 if (!gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
1086 {
1087 ret = FALSE;
1088 }
1089 else
1090 {
1091 gtk_text_buffer_get_iter_at_offset (buffer, iter: &start_iter, char_offset: start);
1092 gtk_text_buffer_get_iter_at_offset (buffer, iter: &end_iter, char_offset: end);
1093 gtk_text_buffer_select_range (buffer, ins: &end_iter, bound: &end_iter);
1094 ret = TRUE;
1095 }
1096 }
1097 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
1098 }
1099 else if (g_strcmp0 (str1: method_name, str2: "GetCharacterExtents") == 0)
1100 {
1101 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
1102 }
1103 else if (g_strcmp0 (str1: method_name, str2: "GetRangeExtents") == 0)
1104 {
1105 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
1106 }
1107 else if (g_strcmp0 (str1: method_name, str2: "GetBoundedRanges") == 0)
1108 {
1109 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
1110 }
1111 else if (g_strcmp0 (str1: method_name, str2: "ScrollSubstringTo") == 0)
1112 {
1113 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1114 GtkTextIter iter;
1115 int start_offset = 0;
1116 int end_offset = 0;
1117 int offset = 0;
1118 double x_align = -1.0, y_align = -1.0;
1119 AtspiScrollType scroll_type;
1120 gboolean is_rtl = FALSE, use_align = TRUE;
1121 gboolean ret = FALSE;
1122
1123 g_variant_get (value: parameters, format_string: "(iiu)", &start_offset, &end_offset, &scroll_type);
1124
1125 if (end_offset < start_offset)
1126 {
1127 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
1128 code: G_DBUS_ERROR_INVALID_ARGS,
1129 message: "Negative offset is not supported");
1130 return;
1131 }
1132
1133 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1134 is_rtl = TRUE;
1135
1136 switch (scroll_type)
1137 {
1138 case ATSPI_SCROLL_TOP_LEFT:
1139 offset = (is_rtl) ? end_offset : start_offset;
1140 x_align = 0.0;
1141 y_align = 0.0;
1142 break;
1143
1144 case ATSPI_SCROLL_BOTTOM_RIGHT:
1145 offset = (is_rtl) ? start_offset : end_offset;
1146 x_align = 1.0;
1147 y_align = 1.0;
1148 break;
1149
1150 case ATSPI_SCROLL_TOP_EDGE:
1151 offset = start_offset;
1152 y_align = 0.0;
1153 break;
1154
1155 case ATSPI_SCROLL_BOTTOM_EDGE:
1156 offset = end_offset;
1157 y_align = 1.0;
1158 break;
1159
1160 case ATSPI_SCROLL_LEFT_EDGE:
1161 offset = (is_rtl) ? end_offset : start_offset;
1162 x_align = 0.0;
1163 break;
1164
1165 case ATSPI_SCROLL_RIGHT_EDGE:
1166 offset = (is_rtl) ? start_offset : end_offset;
1167 x_align = 1.0;
1168 break;
1169
1170 case ATSPI_SCROLL_ANYWHERE:
1171 offset = start_offset;
1172 use_align = FALSE;
1173 x_align = 0.0;
1174 y_align = 0.0;
1175 break;
1176
1177 default:
1178 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
1179 code: G_DBUS_ERROR_INVALID_ARGS,
1180 message: "Invalid scroll type");
1181 return;
1182 }
1183
1184 gtk_text_buffer_get_iter_at_offset (buffer, iter: &iter, char_offset: offset);
1185
1186 if (use_align && (x_align != -1.0 || y_align != -1.0))
1187 {
1188 GdkRectangle visible_rect, iter_rect;
1189
1190 gtk_text_view_get_visible_rect (GTK_TEXT_VIEW (widget), visible_rect: &visible_rect);
1191 gtk_text_view_get_iter_location (GTK_TEXT_VIEW (widget), iter: &iter, location: &iter_rect);
1192
1193 if (x_align == -1.0)
1194 x_align = ((double) (iter_rect.x - visible_rect.x)) / (visible_rect.width - 1);
1195 if (y_align == -1.0)
1196 y_align = ((double) (iter_rect.y - visible_rect.y)) / (visible_rect.height - 1);
1197 }
1198
1199 ret = gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (widget), iter: &iter,
1200 within_margin: 0.0,
1201 use_align,
1202 xalign: x_align, yalign: y_align);
1203
1204 g_dbus_method_invocation_return_value (invocation, parameters: g_variant_new (format_string: "(b)", ret));
1205 }
1206 else if (g_strcmp0 (str1: method_name, str2: "ScrollSubstringToPoint") == 0)
1207 {
1208 g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, code: G_DBUS_ERROR_NOT_SUPPORTED, message: "");
1209 }
1210}
1211
1212static GVariant *
1213text_view_get_property (GDBusConnection *connection,
1214 const gchar *sender,
1215 const gchar *object_path,
1216 const gchar *interface_name,
1217 const gchar *property_name,
1218 GError **error,
1219 gpointer user_data)
1220{
1221 GtkATContext *self = user_data;
1222 GtkAccessible *accessible = gtk_at_context_get_accessible (self);
1223 GtkWidget *widget = GTK_WIDGET (accessible);
1224
1225 if (g_strcmp0 (str1: property_name, str2: "CharacterCount") == 0)
1226 {
1227 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1228 int len;
1229
1230 len = gtk_text_buffer_get_char_count (buffer);
1231
1232 return g_variant_new_int32 (value: len);
1233 }
1234 else if (g_strcmp0 (str1: property_name, str2: "CaretOffset") == 0)
1235 {
1236 GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1237 GtkTextMark *insert;
1238 GtkTextIter iter;
1239 int offset;
1240
1241 insert = gtk_text_buffer_get_insert (buffer);
1242 gtk_text_buffer_get_iter_at_mark (buffer, iter: &iter, mark: insert);
1243 offset = gtk_text_iter_get_offset (iter: &iter);
1244
1245 return g_variant_new_int32 (value: offset);
1246 }
1247
1248 return NULL;
1249}
1250
1251static const GDBusInterfaceVTable text_view_vtable = {
1252 text_view_handle_method,
1253 text_view_get_property,
1254 NULL,
1255};
1256
1257/* }}} */
1258
1259const GDBusInterfaceVTable *
1260gtk_atspi_get_text_vtable (GtkAccessible *accessible)
1261{
1262 if (GTK_IS_LABEL (accessible))
1263 return &label_vtable;
1264 else if (GTK_IS_EDITABLE (accessible) &&
1265 GTK_IS_TEXT (gtk_editable_get_delegate (GTK_EDITABLE (accessible))))
1266 return &editable_vtable;
1267 else if (GTK_IS_TEXT_VIEW (accessible))
1268 return &text_view_vtable;
1269
1270 return NULL;
1271}
1272
1273typedef struct {
1274 void (* text_changed) (gpointer data,
1275 const char *kind,
1276 int start,
1277 int end,
1278 const char *text);
1279 void (* selection_changed) (gpointer data,
1280 const char *kind,
1281 int cursor_position);
1282
1283 gpointer data;
1284 GtkTextBuffer *buffer;
1285 int cursor_position;
1286 int selection_bound;
1287} TextChanged;
1288
1289/* {{{ GtkEditable notification */
1290
1291static void
1292insert_text_cb (GtkEditable *editable,
1293 char *new_text,
1294 int new_text_length,
1295 int *position,
1296 TextChanged *changed)
1297{
1298 int length;
1299
1300 if (new_text_length == 0)
1301 return;
1302
1303 length = g_utf8_strlen (p: new_text, max: new_text_length);
1304 changed->text_changed (changed->data, "insert", *position - length, length, new_text);
1305}
1306
1307static void
1308delete_text_cb (GtkEditable *editable,
1309 int start,
1310 int end,
1311 TextChanged *changed)
1312{
1313 char *text;
1314
1315 if (start == end)
1316 return;
1317
1318 text = gtk_editable_get_chars (editable, start_pos: start, end_pos: end);
1319 changed->text_changed (changed->data, "delete", start, end - start, text);
1320 g_free (mem: text);
1321}
1322
1323static void
1324update_selection (TextChanged *changed,
1325 int cursor_position,
1326 int selection_bound)
1327{
1328 gboolean caret_moved, bound_moved;
1329 gboolean had_selection, has_selection;
1330
1331 caret_moved = cursor_position != changed->cursor_position;
1332 bound_moved = selection_bound != changed->selection_bound;
1333 had_selection = changed->cursor_position != changed->selection_bound;
1334 has_selection = cursor_position != selection_bound;
1335
1336 if (!caret_moved && !bound_moved)
1337 return;
1338
1339 changed->cursor_position = cursor_position;
1340 changed->selection_bound = selection_bound;
1341
1342 if (caret_moved)
1343 changed->selection_changed (changed->data, "text-caret-moved", changed->cursor_position);
1344
1345 if (had_selection || has_selection)
1346 changed->selection_changed (changed->data, "text-selection-changed", 0);
1347}
1348
1349static void
1350notify_cb (GObject *object,
1351 GParamSpec *pspec,
1352 TextChanged *changed)
1353{
1354 if (g_strcmp0 (str1: pspec->name, str2: "cursor-position") == 0 ||
1355 g_strcmp0 (str1: pspec->name, str2: "selection-bound") == 0)
1356 {
1357 int cursor_position, selection_bound;
1358
1359 gtk_editable_get_selection_bounds (GTK_EDITABLE (object), start_pos: &cursor_position, end_pos: &selection_bound);
1360 update_selection (changed, cursor_position, selection_bound);
1361 }
1362}
1363
1364static void
1365update_cursor (GtkTextBuffer *buffer,
1366 TextChanged *changed)
1367{
1368 GtkTextIter iter;
1369 int cursor_position, selection_bound;
1370
1371 gtk_text_buffer_get_iter_at_mark (buffer, iter: &iter, mark: gtk_text_buffer_get_insert (buffer));
1372 cursor_position = gtk_text_iter_get_offset (iter: &iter);
1373
1374 gtk_text_buffer_get_iter_at_mark (buffer, iter: &iter, mark: gtk_text_buffer_get_selection_bound (buffer));
1375
1376 selection_bound = gtk_text_iter_get_offset (iter: &iter);
1377
1378 update_selection (changed, cursor_position, selection_bound);
1379}
1380
1381/* }}} */
1382/* {{{ GtkTextView notification */
1383
1384static void
1385insert_range_cb (GtkTextBuffer *buffer,
1386 GtkTextIter *iter,
1387 char *text,
1388 int len,
1389 TextChanged *changed)
1390{
1391 int position;
1392 int length;
1393
1394 position = gtk_text_iter_get_offset (iter);
1395 length = g_utf8_strlen (p: text, max: len);
1396
1397 changed->text_changed (changed->data, "insert", position - length, length, text);
1398
1399 update_cursor (buffer, changed);
1400}
1401
1402static void
1403delete_range_cb (GtkTextBuffer *buffer,
1404 GtkTextIter *start,
1405 GtkTextIter *end,
1406 TextChanged *changed)
1407{
1408 int offset, length;
1409 char *text;
1410
1411 text = gtk_text_buffer_get_slice (buffer, start, end, FALSE);
1412
1413 offset = gtk_text_iter_get_offset (iter: start);
1414 length = gtk_text_iter_get_offset (iter: end) - offset;
1415
1416 changed->text_changed (changed->data, "delete", offset, length, text);
1417
1418 g_free (mem: text);
1419}
1420
1421static void
1422delete_range_after_cb (GtkTextBuffer *buffer,
1423 GtkTextIter *start,
1424 GtkTextIter *end,
1425 TextChanged *changed)
1426{
1427 update_cursor (buffer, changed);
1428}
1429
1430static void
1431mark_set_cb (GtkTextBuffer *buffer,
1432 GtkTextIter *location,
1433 GtkTextMark *mark,
1434 TextChanged *changed)
1435{
1436 if (mark == gtk_text_buffer_get_insert (buffer) ||
1437 mark == gtk_text_buffer_get_selection_bound (buffer))
1438 update_cursor (buffer, changed);
1439}
1440
1441static void
1442buffer_changed (GtkWidget *widget,
1443 GParamSpec *pspec,
1444 TextChanged *changed)
1445{
1446 GtkTextBuffer *buffer;
1447 GtkTextIter start, end;
1448 char *text;
1449
1450 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1451
1452 if (changed->buffer)
1453 {
1454 g_signal_handlers_disconnect_by_func (changed->buffer, insert_range_cb, changed);
1455 g_signal_handlers_disconnect_by_func (changed->buffer, delete_range_cb, changed);
1456 g_signal_handlers_disconnect_by_func (changed->buffer, delete_range_after_cb, changed);
1457 g_signal_handlers_disconnect_by_func (changed->buffer, mark_set_cb, changed);
1458
1459 gtk_text_buffer_get_bounds (buffer: changed->buffer, start: &start, end: &end);
1460 text = gtk_text_buffer_get_slice (buffer: changed->buffer, start: &start, end: &end, FALSE);
1461 changed->text_changed (changed->data, "delete", 0, gtk_text_buffer_get_char_count (buffer: changed->buffer), text);
1462 g_free (mem: text);
1463
1464 update_selection (changed, cursor_position: 0, selection_bound: 0);
1465
1466 g_clear_object (&changed->buffer);
1467 }
1468
1469 changed->buffer = buffer;
1470
1471 if (changed->buffer)
1472 {
1473 g_object_ref (changed->buffer);
1474 g_signal_connect (changed->buffer, "insert-text", G_CALLBACK (insert_range_cb), changed);
1475 g_signal_connect (changed->buffer, "delete-range", G_CALLBACK (delete_range_cb), changed);
1476 g_signal_connect_after (changed->buffer, "delete-range", G_CALLBACK (delete_range_after_cb), changed);
1477 g_signal_connect_after (changed->buffer, "mark-set", G_CALLBACK (mark_set_cb), changed);
1478
1479 gtk_text_buffer_get_bounds (buffer: changed->buffer, start: &start, end: &end);
1480 text = gtk_text_buffer_get_slice (buffer: changed->buffer, start: &start, end: &end, FALSE);
1481 changed->text_changed (changed->data, "insert", 0, gtk_text_buffer_get_char_count (buffer: changed->buffer), text);
1482 g_free (mem: text);
1483
1484 update_cursor (buffer: changed->buffer, changed);
1485 }
1486}
1487
1488/* }}} */
1489
1490void
1491gtk_atspi_connect_text_signals (GtkAccessible *accessible,
1492 GtkAtspiTextChangedCallback text_changed,
1493 GtkAtspiTextSelectionCallback selection_changed,
1494 gpointer data)
1495{
1496 TextChanged *changed;
1497
1498 if (!GTK_IS_EDITABLE (accessible) &&
1499 !GTK_IS_TEXT_VIEW (accessible))
1500 return;
1501
1502 changed = g_new0 (TextChanged, 1);
1503 changed->text_changed = text_changed;
1504 changed->selection_changed = selection_changed;
1505 changed->data = data;
1506
1507 g_object_set_data_full (G_OBJECT (accessible), key: "accessible-text-data", data: changed, destroy: g_free);
1508
1509 if (GTK_IS_EDITABLE (accessible))
1510 {
1511 GtkText *text = gtk_editable_get_text_widget (GTK_WIDGET (accessible));
1512
1513 if (text)
1514 {
1515 g_signal_connect_after (text, "insert-text", G_CALLBACK (insert_text_cb), changed);
1516 g_signal_connect (text, "delete-text", G_CALLBACK (delete_text_cb), changed);
1517 g_signal_connect (text, "notify", G_CALLBACK (notify_cb), changed);
1518
1519 gtk_editable_get_selection_bounds (GTK_EDITABLE (text), start_pos: &changed->cursor_position, end_pos: &changed->selection_bound);
1520 }
1521 }
1522 else if (GTK_IS_TEXT_VIEW (accessible))
1523 {
1524 g_signal_connect (accessible, "notify::buffer", G_CALLBACK (buffer_changed), changed);
1525 buffer_changed (GTK_WIDGET (accessible), NULL, changed);
1526 }
1527}
1528
1529void
1530gtk_atspi_disconnect_text_signals (GtkAccessible *accessible)
1531{
1532 if (!GTK_IS_EDITABLE (accessible) &&
1533 !GTK_IS_TEXT_VIEW (accessible))
1534 return;
1535
1536 TextChanged *changed;
1537
1538 changed = g_object_get_data (G_OBJECT (accessible), key: "accessible-text-data");
1539 if (changed == NULL)
1540 return;
1541
1542 if (GTK_IS_EDITABLE (accessible))
1543 {
1544 GtkText *text = gtk_editable_get_text_widget (GTK_WIDGET (accessible));
1545
1546 if (text)
1547 {
1548 g_signal_handlers_disconnect_by_func (text, insert_text_cb, changed);
1549 g_signal_handlers_disconnect_by_func (text, delete_text_cb, changed);
1550 g_signal_handlers_disconnect_by_func (text, notify_cb, changed);
1551 }
1552 }
1553 else if (GTK_IS_TEXT_VIEW (accessible))
1554 {
1555 g_signal_handlers_disconnect_by_func (accessible, buffer_changed, changed);
1556
1557 if (changed->buffer)
1558 {
1559 g_signal_handlers_disconnect_by_func (changed->buffer, insert_range_cb, changed);
1560 g_signal_handlers_disconnect_by_func (changed->buffer, delete_range_cb, changed);
1561 g_signal_handlers_disconnect_by_func (changed->buffer, delete_range_after_cb, changed);
1562 g_signal_handlers_disconnect_by_func (changed->buffer, mark_set_cb, changed);
1563 }
1564
1565 g_clear_object (&changed->buffer);
1566 }
1567
1568 g_object_set_data (G_OBJECT (accessible), key: "accessible-text-data", NULL);
1569}
1570
1571/* vim:set foldmethod=marker expandtab: */
1572

source code of gtk/gtk/a11y/gtkatspitext.c