1 /* GTK+ - accessibility implementations
2  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <sys/types.h>
21 
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
25 
26 #include <string.h>
27 #include <stdlib.h>
28 #include <glib-object.h>
29 #include <glib/gstdio.h>
30 #include <gtk/gtk.h>
31 #include "gtktextviewaccessibleprivate.h"
32 #include "gtktextbufferprivate.h"
33 #include "gtk/gtkwidgetprivate.h"
34 
35 struct _GtkTextViewAccessiblePrivate
36 {
37   gint insert_offset;
38   gint selection_bound;
39 };
40 
41 static void       insert_text_cb        (GtkTextBuffer    *buffer,
42                                                          GtkTextIter      *arg1,
43                                                          gchar            *arg2,
44                                                          gint             arg3,
45                                                          gpointer         user_data);
46 static void       delete_range_cb       (GtkTextBuffer    *buffer,
47                                                          GtkTextIter      *arg1,
48                                                          GtkTextIter      *arg2,
49                                                          gpointer         user_data);
50 static void       delete_range_after_cb (GtkTextBuffer    *buffer,
51                                                          GtkTextIter      *arg1,
52                                                          GtkTextIter      *arg2,
53                                                          gpointer         user_data);
54 static void       mark_set_cb           (GtkTextBuffer    *buffer,
55                                                          GtkTextIter      *arg1,
56                                                          GtkTextMark      *arg2,
57                                                          gpointer         user_data);
58 
59 
60 static void atk_editable_text_interface_init      (AtkEditableTextIface      *iface);
61 static void atk_text_interface_init               (AtkTextIface              *iface);
62 static void atk_streamable_content_interface_init (AtkStreamableContentIface *iface);
63 
G_DEFINE_TYPE_WITH_CODE(GtkTextViewAccessible,gtk_text_view_accessible,GTK_TYPE_CONTAINER_ACCESSIBLE,G_ADD_PRIVATE (GtkTextViewAccessible)G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT,atk_editable_text_interface_init)G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,atk_text_interface_init)G_IMPLEMENT_INTERFACE (ATK_TYPE_STREAMABLE_CONTENT,atk_streamable_content_interface_init))64 G_DEFINE_TYPE_WITH_CODE (GtkTextViewAccessible, gtk_text_view_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
65                          G_ADD_PRIVATE (GtkTextViewAccessible)
66                          G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init)
67                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init)
68                          G_IMPLEMENT_INTERFACE (ATK_TYPE_STREAMABLE_CONTENT, atk_streamable_content_interface_init))
69 
70 
71 static void
72 gtk_text_view_accessible_initialize (AtkObject *obj,
73                                      gpointer   data)
74 {
75   ATK_OBJECT_CLASS (gtk_text_view_accessible_parent_class)->initialize (obj, data);
76 
77   obj->role = ATK_ROLE_TEXT;
78 }
79 
80 static void
gtk_text_view_accessible_notify_gtk(GObject * obj,GParamSpec * pspec)81 gtk_text_view_accessible_notify_gtk (GObject    *obj,
82                                      GParamSpec *pspec)
83 {
84   AtkObject *atk_obj;
85 
86   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (obj));
87 
88   if (!strcmp (pspec->name, "editable"))
89     {
90       gboolean editable;
91 
92       editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (obj));
93       atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, editable);
94     }
95   else
96     GTK_WIDGET_ACCESSIBLE_CLASS (gtk_text_view_accessible_parent_class)->notify_gtk (obj, pspec);
97 }
98 
99 static AtkStateSet*
gtk_text_view_accessible_ref_state_set(AtkObject * accessible)100 gtk_text_view_accessible_ref_state_set (AtkObject *accessible)
101 {
102   AtkStateSet *state_set;
103   GtkWidget *widget;
104 
105   state_set = ATK_OBJECT_CLASS (gtk_text_view_accessible_parent_class)->ref_state_set (accessible);
106 
107   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
108   if (widget == NULL)
109     {
110       atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
111       return state_set;
112     }
113 
114   if (gtk_text_view_get_editable (GTK_TEXT_VIEW (widget)))
115     atk_state_set_add_state (state_set, ATK_STATE_EDITABLE);
116   atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE);
117 
118   return state_set;
119 }
120 
121 static void
gtk_text_view_accessible_change_buffer(GtkTextViewAccessible * accessible,GtkTextBuffer * old_buffer,GtkTextBuffer * new_buffer)122 gtk_text_view_accessible_change_buffer (GtkTextViewAccessible *accessible,
123                                         GtkTextBuffer         *old_buffer,
124                                         GtkTextBuffer         *new_buffer)
125 {
126   if (old_buffer)
127     {
128       g_signal_handlers_disconnect_matched (old_buffer, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, accessible);
129 
130       g_signal_emit_by_name (accessible,
131                              "text-changed::delete",
132                              0,
133                              gtk_text_buffer_get_char_count (old_buffer));
134     }
135 
136   if (new_buffer)
137     {
138       g_signal_connect_after (new_buffer, "insert-text", G_CALLBACK (insert_text_cb), accessible);
139       g_signal_connect (new_buffer, "delete-range", G_CALLBACK (delete_range_cb), accessible);
140       g_signal_connect_after (new_buffer, "delete-range", G_CALLBACK (delete_range_after_cb), accessible);
141       g_signal_connect_after (new_buffer, "mark-set", G_CALLBACK (mark_set_cb), accessible);
142 
143       g_signal_emit_by_name (accessible,
144                              "text-changed::insert",
145                              0,
146                              gtk_text_buffer_get_char_count (new_buffer));
147     }
148 }
149 
150 static void
gtk_text_view_accessible_widget_set(GtkAccessible * accessible)151 gtk_text_view_accessible_widget_set (GtkAccessible *accessible)
152 {
153   gtk_text_view_accessible_change_buffer (GTK_TEXT_VIEW_ACCESSIBLE (accessible),
154                                           NULL,
155                                           gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_accessible_get_widget (accessible))));
156 }
157 
158 static void
gtk_text_view_accessible_widget_unset(GtkAccessible * accessible)159 gtk_text_view_accessible_widget_unset (GtkAccessible *accessible)
160 {
161   gtk_text_view_accessible_change_buffer (GTK_TEXT_VIEW_ACCESSIBLE (accessible),
162                                           gtk_text_view_get_buffer (GTK_TEXT_VIEW (gtk_accessible_get_widget (accessible))),
163                                           NULL);
164 }
165 
166 static void
gtk_text_view_accessible_class_init(GtkTextViewAccessibleClass * klass)167 gtk_text_view_accessible_class_init (GtkTextViewAccessibleClass *klass)
168 {
169   AtkObjectClass  *class = ATK_OBJECT_CLASS (klass);
170   GtkAccessibleClass *accessible_class = GTK_ACCESSIBLE_CLASS (klass);
171   GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
172 
173   accessible_class->widget_set = gtk_text_view_accessible_widget_set;
174   accessible_class->widget_unset = gtk_text_view_accessible_widget_unset;
175 
176   class->ref_state_set = gtk_text_view_accessible_ref_state_set;
177   class->initialize = gtk_text_view_accessible_initialize;
178 
179   widget_class->notify_gtk = gtk_text_view_accessible_notify_gtk;
180 }
181 
182 static void
gtk_text_view_accessible_init(GtkTextViewAccessible * accessible)183 gtk_text_view_accessible_init (GtkTextViewAccessible *accessible)
184 {
185   accessible->priv = gtk_text_view_accessible_get_instance_private (accessible);
186 }
187 
188 static gchar *
gtk_text_view_accessible_get_text(AtkText * text,gint start_offset,gint end_offset)189 gtk_text_view_accessible_get_text (AtkText *text,
190                                    gint     start_offset,
191                                    gint     end_offset)
192 {
193   GtkTextView *view;
194   GtkTextBuffer *buffer;
195   GtkTextIter start, end;
196   GtkWidget *widget;
197 
198   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
199   if (widget == NULL)
200     return NULL;
201 
202   view = GTK_TEXT_VIEW (widget);
203   buffer = gtk_text_view_get_buffer (view);
204   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
205   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
206 
207   return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
208 }
209 
210 static gchar *
gtk_text_view_accessible_get_text_after_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)211 gtk_text_view_accessible_get_text_after_offset (AtkText         *text,
212                                                 gint             offset,
213                                                 AtkTextBoundary  boundary_type,
214                                                 gint            *start_offset,
215                                                 gint            *end_offset)
216 {
217   GtkWidget *widget;
218   GtkTextView *view;
219   GtkTextBuffer *buffer;
220   GtkTextIter pos;
221   GtkTextIter start, end;
222 
223   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
224   if (widget == NULL)
225     return NULL;
226 
227   view = GTK_TEXT_VIEW (widget);
228   buffer = gtk_text_view_get_buffer (view);
229   gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset);
230   start = end = pos;
231   if (boundary_type == ATK_TEXT_BOUNDARY_LINE_START)
232     {
233       gtk_text_view_forward_display_line (view, &end);
234       start = end;
235       gtk_text_view_forward_display_line (view, &end);
236     }
237   else if (boundary_type == ATK_TEXT_BOUNDARY_LINE_END)
238     {
239       gtk_text_view_forward_display_line_end (view, &end);
240       start = end;
241       gtk_text_view_forward_display_line (view, &end);
242       gtk_text_view_forward_display_line_end (view, &end);
243     }
244   else
245     _gtk_text_buffer_get_text_after (buffer, boundary_type, &pos, &start, &end);
246 
247   *start_offset = gtk_text_iter_get_offset (&start);
248   *end_offset = gtk_text_iter_get_offset (&end);
249 
250   return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
251 }
252 
253 static gchar *
gtk_text_view_accessible_get_text_at_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)254 gtk_text_view_accessible_get_text_at_offset (AtkText         *text,
255                                              gint             offset,
256                                              AtkTextBoundary  boundary_type,
257                                              gint            *start_offset,
258                                              gint            *end_offset)
259 {
260   GtkWidget *widget;
261   GtkTextView *view;
262   GtkTextBuffer *buffer;
263   GtkTextIter pos;
264   GtkTextIter start, end;
265 
266   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
267   if (widget == NULL)
268     return NULL;
269 
270   view = GTK_TEXT_VIEW (widget);
271   buffer = gtk_text_view_get_buffer (view);
272   gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset);
273   start = end = pos;
274   if (boundary_type == ATK_TEXT_BOUNDARY_LINE_START)
275     {
276       gtk_text_view_backward_display_line_start (view, &start);
277       gtk_text_view_forward_display_line (view, &end);
278     }
279   else if (boundary_type == ATK_TEXT_BOUNDARY_LINE_END)
280     {
281       gtk_text_view_backward_display_line_start (view, &start);
282       if (!gtk_text_iter_is_start (&start))
283         {
284           gtk_text_view_backward_display_line (view, &start);
285           gtk_text_view_forward_display_line_end (view, &start);
286         }
287       gtk_text_view_forward_display_line_end (view, &end);
288     }
289   else
290     _gtk_text_buffer_get_text_at (buffer, boundary_type, &pos, &start, &end);
291 
292   *start_offset = gtk_text_iter_get_offset (&start);
293   *end_offset = gtk_text_iter_get_offset (&end);
294 
295   return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
296 }
297 
298 static gchar *
gtk_text_view_accessible_get_text_before_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)299 gtk_text_view_accessible_get_text_before_offset (AtkText         *text,
300                                                  gint             offset,
301                                                  AtkTextBoundary  boundary_type,
302                                                  gint            *start_offset,
303                                                  gint            *end_offset)
304 {
305   GtkWidget *widget;
306   GtkTextView *view;
307   GtkTextBuffer *buffer;
308   GtkTextIter pos;
309   GtkTextIter start, end;
310 
311   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
312   if (widget == NULL)
313     return NULL;
314 
315   view = GTK_TEXT_VIEW (widget);
316   buffer = gtk_text_view_get_buffer (view);
317   gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset);
318   start = end = pos;
319 
320   if (boundary_type == ATK_TEXT_BOUNDARY_LINE_START)
321     {
322       gtk_text_view_backward_display_line_start (view, &start);
323       end = start;
324       gtk_text_view_backward_display_line (view, &start);
325       gtk_text_view_backward_display_line_start (view, &start);
326     }
327   else if (boundary_type == ATK_TEXT_BOUNDARY_LINE_END)
328     {
329       gtk_text_view_backward_display_line_start (view, &start);
330       if (!gtk_text_iter_is_start (&start))
331         {
332           gtk_text_view_backward_display_line (view, &start);
333           end = start;
334           gtk_text_view_forward_display_line_end (view, &end);
335           if (!gtk_text_iter_is_start (&start))
336             {
337               if (gtk_text_view_backward_display_line (view, &start))
338                 gtk_text_view_forward_display_line_end (view, &start);
339               else
340                 gtk_text_iter_set_offset (&start, 0);
341             }
342         }
343       else
344         end = start;
345     }
346   else
347     _gtk_text_buffer_get_text_before (buffer, boundary_type, &pos, &start, &end);
348 
349   *start_offset = gtk_text_iter_get_offset (&start);
350   *end_offset = gtk_text_iter_get_offset (&end);
351 
352   return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
353 }
354 
355 static gunichar
gtk_text_view_accessible_get_character_at_offset(AtkText * text,gint offset)356 gtk_text_view_accessible_get_character_at_offset (AtkText *text,
357                                                   gint     offset)
358 {
359   GtkWidget *widget;
360   GtkTextIter start, end;
361   GtkTextBuffer *buffer;
362   gchar *string;
363   gunichar unichar;
364 
365   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
366   if (widget == NULL)
367     return '\0';
368 
369   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
370   if (offset >= gtk_text_buffer_get_char_count (buffer))
371     return '\0';
372 
373   gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
374   end = start;
375   gtk_text_iter_forward_char (&end);
376   string = gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
377   unichar = g_utf8_get_char (string);
378   g_free (string);
379 
380   return unichar;
381 }
382 
383 static gint
gtk_text_view_accessible_get_character_count(AtkText * text)384 gtk_text_view_accessible_get_character_count (AtkText *text)
385 {
386   GtkWidget *widget;
387   GtkTextBuffer *buffer;
388 
389   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
390   if (widget == NULL)
391     return 0;
392 
393   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
394   return gtk_text_buffer_get_char_count (buffer);
395 }
396 
397 static gint
get_insert_offset(GtkTextBuffer * buffer)398 get_insert_offset (GtkTextBuffer *buffer)
399 {
400   GtkTextMark *insert;
401   GtkTextIter iter;
402 
403   insert = gtk_text_buffer_get_insert (buffer);
404   gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
405   return gtk_text_iter_get_offset (&iter);
406 }
407 
408 static gint
gtk_text_view_accessible_get_caret_offset(AtkText * text)409 gtk_text_view_accessible_get_caret_offset (AtkText *text)
410 {
411   GtkWidget *widget;
412   GtkTextBuffer *buffer;
413 
414   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
415   if (widget == NULL)
416     return 0;
417 
418   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
419   return get_insert_offset (buffer);
420 }
421 
422 static gboolean
gtk_text_view_accessible_set_caret_offset(AtkText * text,gint offset)423 gtk_text_view_accessible_set_caret_offset (AtkText *text,
424                                            gint     offset)
425 {
426   GtkTextView *view;
427   GtkWidget *widget;
428   GtkTextBuffer *buffer;
429   GtkTextIter iter;
430 
431   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
432   if (widget == NULL)
433     return FALSE;
434 
435   view = GTK_TEXT_VIEW (widget);
436   buffer = gtk_text_view_get_buffer (view);
437 
438   gtk_text_buffer_get_iter_at_offset (buffer,  &iter, offset);
439   gtk_text_buffer_place_cursor (buffer, &iter);
440   gtk_text_view_scroll_to_iter (view, &iter, 0, FALSE, 0, 0);
441 
442   return TRUE;
443 }
444 
445 static gint
gtk_text_view_accessible_get_offset_at_point(AtkText * text,gint x,gint y,AtkCoordType coords)446 gtk_text_view_accessible_get_offset_at_point (AtkText      *text,
447                                               gint          x,
448                                               gint          y,
449                                               AtkCoordType  coords)
450 {
451   GtkTextView *view;
452   GtkTextIter iter;
453   gint x_widget, y_widget, x_window, y_window, buff_x, buff_y;
454   GtkWidget *widget;
455   GdkWindow *window;
456   GdkRectangle rect;
457 
458   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
459   if (widget == NULL)
460     return -1;
461 
462   view = GTK_TEXT_VIEW (widget);
463   window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_WIDGET);
464   gdk_window_get_origin (window, &x_widget, &y_widget);
465 
466   if (coords == ATK_XY_SCREEN)
467     {
468       x = x - x_widget;
469       y = y - y_widget;
470     }
471   else if (coords == ATK_XY_WINDOW)
472     {
473       window = gdk_window_get_toplevel (window);
474       gdk_window_get_origin (window, &x_window, &y_window);
475 
476       x = x - x_widget + x_window;
477       y = y - y_widget + y_window;
478     }
479   else
480     return -1;
481 
482   gtk_text_view_window_to_buffer_coords (view, GTK_TEXT_WINDOW_WIDGET,
483                                          x, y, &buff_x, &buff_y);
484   gtk_text_view_get_visible_rect (view, &rect);
485 
486   /* Clamp point to visible rectangle */
487   buff_x = CLAMP (buff_x, rect.x, rect.x + rect.width - 1);
488   buff_y = CLAMP (buff_y, rect.y, rect.y + rect.height - 1);
489 
490   gtk_text_view_get_iter_at_location (view, &iter, buff_x, buff_y);
491 
492   /* The iter at a location sometimes points to the next character.
493    * See bug 111031. We work around that
494    */
495   gtk_text_view_get_iter_location (view, &iter, &rect);
496   if (buff_x < rect.x)
497     gtk_text_iter_backward_char (&iter);
498   return gtk_text_iter_get_offset (&iter);
499 }
500 
501 static void
gtk_text_view_accessible_get_character_extents(AtkText * text,gint offset,gint * x,gint * y,gint * width,gint * height,AtkCoordType coords)502 gtk_text_view_accessible_get_character_extents (AtkText      *text,
503                                                 gint          offset,
504                                                 gint         *x,
505                                                 gint         *y,
506                                                 gint         *width,
507                                                 gint         *height,
508                                                 AtkCoordType  coords)
509 {
510   GtkTextView *view;
511   GtkTextBuffer *buffer;
512   GtkTextIter iter;
513   GtkWidget *widget;
514   GdkRectangle rectangle;
515   GdkWindow *window;
516   gint x_widget, y_widget, x_window, y_window;
517 
518   *x = 0;
519   *y = 0;
520   *width = 0;
521   *height = 0;
522 
523   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
524   if (widget == NULL)
525     return;
526 
527   view = GTK_TEXT_VIEW (widget);
528   buffer = gtk_text_view_get_buffer (view);
529   gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
530   gtk_text_view_get_iter_location (view, &iter, &rectangle);
531 
532   window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_WIDGET);
533   if (window == NULL)
534     return;
535 
536   gdk_window_get_origin (window, &x_widget, &y_widget);
537 
538   *height = rectangle.height;
539   *width = rectangle.width;
540 
541   gtk_text_view_buffer_to_window_coords (view, GTK_TEXT_WINDOW_WIDGET,
542     rectangle.x, rectangle.y, x, y);
543   if (coords == ATK_XY_WINDOW)
544     {
545       window = gdk_window_get_toplevel (window);
546       gdk_window_get_origin (window, &x_window, &y_window);
547       *x += x_widget - x_window;
548       *y += y_widget - y_window;
549     }
550   else if (coords == ATK_XY_SCREEN)
551     {
552       *x += x_widget;
553       *y += y_widget;
554     }
555   else
556     {
557       *x = 0;
558       *y = 0;
559       *height = 0;
560       *width = 0;
561     }
562 }
563 
564 static AtkAttributeSet *
add_text_attribute(AtkAttributeSet * attributes,AtkTextAttribute attr,gchar * value)565 add_text_attribute (AtkAttributeSet  *attributes,
566                     AtkTextAttribute  attr,
567                     gchar            *value)
568 {
569   AtkAttribute *at;
570 
571   at = g_new (AtkAttribute, 1);
572   at->name = g_strdup (atk_text_attribute_get_name (attr));
573   at->value = value;
574 
575   return g_slist_prepend (attributes, at);
576 }
577 
578 static AtkAttributeSet *
add_text_int_attribute(AtkAttributeSet * attributes,AtkTextAttribute attr,gint i)579 add_text_int_attribute (AtkAttributeSet  *attributes,
580                         AtkTextAttribute  attr,
581                         gint              i)
582 
583 {
584   gchar *value;
585 
586   value = g_strdup (atk_text_attribute_get_value (attr, i));
587 
588   return add_text_attribute (attributes, attr, value);
589 }
590 
591 static AtkAttributeSet *
gtk_text_view_accessible_get_run_attributes(AtkText * text,gint offset,gint * start_offset,gint * end_offset)592 gtk_text_view_accessible_get_run_attributes (AtkText *text,
593                                              gint     offset,
594                                              gint    *start_offset,
595                                              gint    *end_offset)
596 {
597   GtkTextView *view;
598   GtkTextBuffer *buffer;
599   GtkWidget *widget;
600   GtkTextIter iter;
601   AtkAttributeSet *attrib_set = NULL;
602   GSList *tags, *temp_tags;
603   gdouble scale = 1;
604   gboolean val_set = FALSE;
605 
606   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
607   if (widget == NULL)
608     return NULL;
609 
610   view = GTK_TEXT_VIEW (widget);
611   buffer = gtk_text_view_get_buffer (view);
612 
613   gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
614 
615   gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
616   *end_offset = gtk_text_iter_get_offset (&iter);
617 
618   gtk_text_iter_backward_to_tag_toggle (&iter, NULL);
619   *start_offset = gtk_text_iter_get_offset (&iter);
620 
621   gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
622 
623   tags = gtk_text_iter_get_tags (&iter);
624   tags = g_slist_reverse (tags);
625 
626   temp_tags = tags;
627   while (temp_tags && !val_set)
628     {
629       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
630 
631       g_object_get (tag, "style-set", &val_set, NULL);
632       if (val_set)
633         {
634           PangoStyle style;
635           g_object_get (tag, "style", &style, NULL);
636           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_STYLE, style);
637         }
638       temp_tags = temp_tags->next;
639     }
640   val_set = FALSE;
641 
642   temp_tags = tags;
643   while (temp_tags && !val_set)
644     {
645       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
646 
647       g_object_get (tag, "variant-set", &val_set, NULL);
648       if (val_set)
649         {
650           PangoVariant variant;
651           g_object_get (tag, "variant", &variant, NULL);
652           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_VARIANT, variant);
653         }
654       temp_tags = temp_tags->next;
655     }
656   val_set = FALSE;
657 
658   temp_tags = tags;
659   while (temp_tags && !val_set)
660     {
661       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
662 
663       g_object_get (tag, "stretch-set", &val_set, NULL);
664       if (val_set)
665         {
666           PangoStretch stretch;
667           g_object_get (tag, "stretch", &stretch, NULL);
668           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_STRETCH, stretch);
669         }
670       temp_tags = temp_tags->next;
671     }
672   val_set = FALSE;
673 
674   temp_tags = tags;
675   while (temp_tags && !val_set)
676     {
677       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
678 
679       g_object_get (tag, "justification-set", &val_set, NULL);
680       if (val_set)
681         {
682           GtkJustification justification;
683           g_object_get (tag, "justification", &justification, NULL);
684           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_JUSTIFICATION, justification);
685         }
686       temp_tags = temp_tags->next;
687     }
688   val_set = FALSE;
689 
690   temp_tags = tags;
691   while (temp_tags && !val_set)
692     {
693       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
694       GtkTextDirection direction;
695 
696       g_object_get (tag, "direction", &direction, NULL);
697 
698       if (direction != GTK_TEXT_DIR_NONE)
699         {
700           val_set = TRUE;
701           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_DIRECTION, direction);
702         }
703       temp_tags = temp_tags->next;
704     }
705   val_set = FALSE;
706 
707   temp_tags = tags;
708   while (temp_tags && !val_set)
709     {
710       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
711 
712       g_object_get (tag, "wrap-mode-set", &val_set, NULL);
713       if (val_set)
714         {
715           GtkWrapMode wrap_mode;
716           g_object_get (tag, "wrap-mode", &wrap_mode, NULL);
717           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_WRAP_MODE, wrap_mode);
718         }
719       temp_tags = temp_tags->next;
720     }
721   val_set = FALSE;
722 
723   temp_tags = tags;
724   while (temp_tags && !val_set)
725     {
726       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
727 
728       g_object_get (tag, "foreground-set", &val_set, NULL);
729       if (val_set)
730         {
731           GdkRGBA *rgba;
732           gchar *value;
733 
734           g_object_get (tag, "foreground-rgba", &rgba, NULL);
735           value = g_strdup_printf ("%u,%u,%u",
736                                    (guint) rgba->red * 65535,
737                                    (guint) rgba->green * 65535,
738                                    (guint) rgba->blue * 65535);
739           gdk_rgba_free (rgba);
740           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_FG_COLOR, value);
741         }
742       temp_tags = temp_tags->next;
743     }
744   val_set = FALSE;
745 
746   temp_tags = tags;
747   while (temp_tags && !val_set)
748     {
749       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
750 
751       g_object_get (tag, "background-set", &val_set, NULL);
752       if (val_set)
753         {
754           GdkRGBA *rgba;
755           gchar *value;
756 
757           g_object_get (tag, "background-rgba", &rgba, NULL);
758           value = g_strdup_printf ("%u,%u,%u",
759                                    (guint) rgba->red * 65535,
760                                    (guint) rgba->green * 65535,
761                                    (guint) rgba->blue * 65535);
762           gdk_rgba_free (rgba);
763           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_BG_COLOR, value);
764         }
765       temp_tags = temp_tags->next;
766     }
767   val_set = FALSE;
768 
769   temp_tags = tags;
770   while (temp_tags && !val_set)
771     {
772       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
773 
774       g_object_get (tag, "family-set", &val_set, NULL);
775 
776       if (val_set)
777         {
778           gchar *value;
779           g_object_get (tag, "family", &value, NULL);
780           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_FAMILY_NAME, value);
781         }
782       temp_tags = temp_tags->next;
783     }
784   val_set = FALSE;
785 
786   temp_tags = tags;
787   while (temp_tags && !val_set)
788     {
789       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
790 
791       g_object_get (tag, "language-set", &val_set, NULL);
792 
793       if (val_set)
794         {
795           gchar *value;
796           g_object_get (tag, "language", &value, NULL);
797           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_LANGUAGE, value);
798         }
799       temp_tags = temp_tags->next;
800     }
801   val_set = FALSE;
802 
803   temp_tags = tags;
804   while (temp_tags && !val_set)
805     {
806       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
807 
808       g_object_get (tag, "weight-set", &val_set, NULL);
809 
810       if (val_set)
811         {
812           gint weight;
813           gchar *value;
814           g_object_get (tag, "weight", &weight, NULL);
815           value = g_strdup_printf ("%d", weight);
816           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_WEIGHT, value);
817         }
818       temp_tags = temp_tags->next;
819     }
820   val_set = FALSE;
821 
822   /* scale is special as the effective value is the product
823    * of all specified values
824    */
825   temp_tags = tags;
826   while (temp_tags)
827     {
828       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
829       gboolean scale_set;
830 
831       g_object_get (tag, "scale-set", &scale_set, NULL);
832       if (scale_set)
833         {
834           gdouble font_scale;
835           g_object_get (tag, "scale", &font_scale, NULL);
836           val_set = TRUE;
837           scale *= font_scale;
838         }
839       temp_tags = temp_tags->next;
840     }
841   if (val_set)
842     {
843       gchar *value;
844       value = g_strdup_printf ("%g", scale);
845       attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_SCALE, value);
846     }
847   val_set = FALSE;
848 
849   temp_tags = tags;
850   while (temp_tags && !val_set)
851     {
852       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
853 
854       g_object_get (tag, "size-set", &val_set, NULL);
855       if (val_set)
856         {
857           gint size;
858           gchar *value;
859           g_object_get (tag, "size", &size, NULL);
860           value = g_strdup_printf ("%i", size);
861           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_SIZE, value);
862         }
863       temp_tags = temp_tags->next;
864     }
865   val_set = FALSE;
866 
867   temp_tags = tags;
868   while (temp_tags && !val_set)
869     {
870       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
871 
872       g_object_get (tag, "strikethrough-set", &val_set, NULL);
873       if (val_set)
874         {
875           gboolean strikethrough;
876           g_object_get (tag, "strikethrough", &strikethrough, NULL);
877           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_STRIKETHROUGH, strikethrough);
878         }
879       temp_tags = temp_tags->next;
880     }
881   val_set = FALSE;
882 
883   temp_tags = tags;
884   while (temp_tags && !val_set)
885     {
886       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
887 
888       g_object_get (tag, "underline-set", &val_set, NULL);
889       if (val_set)
890         {
891           PangoUnderline underline;
892           g_object_get (tag, "underline", &underline, NULL);
893           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_UNDERLINE, underline);
894         }
895       temp_tags = temp_tags->next;
896     }
897   val_set = FALSE;
898 
899   temp_tags = tags;
900   while (temp_tags && !val_set)
901     {
902       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
903 
904       g_object_get (tag, "rise-set", &val_set, NULL);
905       if (val_set)
906         {
907           gint rise;
908           gchar *value;
909           g_object_get (tag, "rise", &rise, NULL);
910           value = g_strdup_printf ("%i", rise);
911           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_RISE, value);
912         }
913       temp_tags = temp_tags->next;
914     }
915   val_set = FALSE;
916 
917   temp_tags = tags;
918   while (temp_tags && !val_set)
919     {
920       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
921 
922       g_object_get (tag, "background-full-height-set", &val_set, NULL);
923       if (val_set)
924         {
925           gboolean bg_full_height;
926           g_object_get (tag, "background-full-height", &bg_full_height, NULL);
927           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_BG_FULL_HEIGHT, bg_full_height);
928         }
929       temp_tags = temp_tags->next;
930     }
931   val_set = FALSE;
932 
933   temp_tags = tags;
934   while (temp_tags && !val_set)
935     {
936       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
937 
938       g_object_get (tag, "pixels-inside-wrap-set", &val_set, NULL);
939       if (val_set)
940         {
941           gint pixels;
942           gchar *value;
943           g_object_get (tag, "pixels-inside-wrap", &pixels, NULL);
944           value = g_strdup_printf ("%i", pixels);
945           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, value);
946         }
947       temp_tags = temp_tags->next;
948     }
949   val_set = FALSE;
950 
951   temp_tags = tags;
952   while (temp_tags && !val_set)
953     {
954       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
955 
956       g_object_get (tag, "pixels-below-lines-set", &val_set, NULL);
957       if (val_set)
958         {
959           gint pixels;
960           gchar *value;
961           g_object_get (tag, "pixels-below-lines", &pixels, NULL);
962           value = g_strdup_printf ("%i", pixels);
963           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, value);
964         }
965       temp_tags = temp_tags->next;
966     }
967   val_set = FALSE;
968 
969   temp_tags = tags;
970   while (temp_tags && !val_set)
971     {
972       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
973 
974       g_object_get (tag, "pixels-above-lines-set", &val_set, NULL);
975       if (val_set)
976         {
977           gint pixels;
978           gchar *value;
979           g_object_get (tag, "pixels-above-lines", &pixels, NULL);
980           value = g_strdup_printf ("%i", pixels);
981           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, value);
982         }
983       temp_tags = temp_tags->next;
984     }
985   val_set = FALSE;
986 
987   temp_tags = tags;
988   while (temp_tags && !val_set)
989     {
990       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
991 
992       g_object_get (tag, "editable-set", &val_set, NULL);
993       if (val_set)
994         {
995           gboolean editable;
996           g_object_get (tag, "editable", &editable, NULL);
997           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_EDITABLE, editable);
998         }
999       temp_tags = temp_tags->next;
1000     }
1001   val_set = FALSE;
1002 
1003   temp_tags = tags;
1004   while (temp_tags && !val_set)
1005     {
1006       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1007 
1008       g_object_get (tag, "invisible-set", &val_set, NULL);
1009       if (val_set)
1010         {
1011           gboolean invisible;
1012           g_object_get (tag, "invisible", &invisible, NULL);
1013           attrib_set = add_text_int_attribute (attrib_set, ATK_TEXT_ATTR_INVISIBLE, invisible);
1014         }
1015       temp_tags = temp_tags->next;
1016     }
1017   val_set = FALSE;
1018 
1019   temp_tags = tags;
1020   while (temp_tags && !val_set)
1021     {
1022       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1023 
1024       g_object_get (tag, "indent-set", &val_set, NULL);
1025       if (val_set)
1026         {
1027           gint indent;
1028           gchar *value;
1029           g_object_get (tag, "indent", &indent, NULL);
1030           value = g_strdup_printf ("%i", indent);
1031           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_INDENT, value);
1032         }
1033       temp_tags = temp_tags->next;
1034     }
1035   val_set = FALSE;
1036 
1037   temp_tags = tags;
1038   while (temp_tags && !val_set)
1039     {
1040       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1041 
1042       g_object_get (tag, "right-margin-set", &val_set, NULL);
1043       if (val_set)
1044         {
1045           gint margin;
1046           gchar *value;
1047           g_object_get (tag, "right-margin", &margin, NULL);
1048           value = g_strdup_printf ("%i", margin);
1049           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_RIGHT_MARGIN, value);
1050         }
1051       temp_tags = temp_tags->next;
1052     }
1053   val_set = FALSE;
1054 
1055   temp_tags = tags;
1056   while (temp_tags && !val_set)
1057     {
1058       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1059 
1060       g_object_get (tag, "left-margin-set", &val_set, NULL);
1061       if (val_set)
1062         {
1063           gint margin;
1064           gchar *value;
1065           g_object_get (tag, "left-margin", &margin, NULL);
1066           value = g_strdup_printf ("%i", margin);
1067           attrib_set = add_text_attribute (attrib_set, ATK_TEXT_ATTR_LEFT_MARGIN, value);
1068         }
1069       temp_tags = temp_tags->next;
1070     }
1071   val_set = FALSE;
1072 
1073   g_slist_free (tags);
1074   return attrib_set;
1075 }
1076 
1077 static AtkAttributeSet *
gtk_text_view_accessible_get_default_attributes(AtkText * text)1078 gtk_text_view_accessible_get_default_attributes (AtkText *text)
1079 {
1080   GtkTextView *view;
1081   GtkWidget *widget;
1082   GtkTextAttributes *text_attrs;
1083   AtkAttributeSet *attributes;
1084   PangoFontDescription *font;
1085   gchar *value;
1086 
1087   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1088   if (widget == NULL)
1089     return NULL;
1090 
1091   view = GTK_TEXT_VIEW (widget);
1092   text_attrs = gtk_text_view_get_default_attributes (view);
1093 
1094   attributes = NULL;
1095 
1096   font = text_attrs->font;
1097 
1098   if (font)
1099     {
1100       attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_STYLE,
1101                                            pango_font_description_get_style (font));
1102 
1103       attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
1104                                            pango_font_description_get_variant (font));
1105 
1106       attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
1107                                            pango_font_description_get_stretch (font));
1108 
1109       value = g_strdup (pango_font_description_get_family (font));
1110       attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME, value);
1111 
1112       value = g_strdup_printf ("%d", pango_font_description_get_weight (font));
1113       attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, value);
1114 
1115       value = g_strdup_printf ("%i", pango_font_description_get_size (font) / PANGO_SCALE);
1116       attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_SIZE, value);
1117     }
1118 
1119   attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_JUSTIFICATION, text_attrs->justification);
1120   attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_DIRECTION, text_attrs->direction);
1121   attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_WRAP_MODE, text_attrs->wrap_mode);
1122   attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_EDITABLE, text_attrs->editable);
1123   attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_INVISIBLE, text_attrs->invisible);
1124   attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_BG_FULL_HEIGHT, text_attrs->bg_full_height);
1125 
1126   attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
1127                                        text_attrs->appearance.strikethrough);
1128   attributes = add_text_int_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
1129                                        text_attrs->appearance.underline);
1130 
1131   value = g_strdup_printf ("%u,%u,%u",
1132                            text_attrs->appearance.bg_color.red,
1133                            text_attrs->appearance.bg_color.green,
1134                            text_attrs->appearance.bg_color.blue);
1135   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
1136 
1137   value = g_strdup_printf ("%u,%u,%u",
1138                            text_attrs->appearance.fg_color.red,
1139                            text_attrs->appearance.fg_color.green,
1140                            text_attrs->appearance.fg_color.blue);
1141   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
1142 
1143   value = g_strdup_printf ("%g", text_attrs->font_scale);
1144   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_SCALE, value);
1145 
1146   value = g_strdup ((gchar *)(text_attrs->language));
1147   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE, value);
1148 
1149   value = g_strdup_printf ("%i", text_attrs->appearance.rise);
1150   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_RISE, value);
1151 
1152   value = g_strdup_printf ("%i", text_attrs->pixels_inside_wrap);
1153   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, value);
1154 
1155   value = g_strdup_printf ("%i", text_attrs->pixels_below_lines);
1156   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, value);
1157 
1158   value = g_strdup_printf ("%i", text_attrs->pixels_above_lines);
1159   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, value);
1160 
1161   value = g_strdup_printf ("%i", text_attrs->indent);
1162   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_INDENT, value);
1163 
1164   value = g_strdup_printf ("%i", text_attrs->left_margin);
1165   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_LEFT_MARGIN, value);
1166 
1167   value = g_strdup_printf ("%i", text_attrs->right_margin);
1168   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_RIGHT_MARGIN, value);
1169 
1170   gtk_text_attributes_unref (text_attrs);
1171   return attributes;
1172 }
1173 
1174 static gint
gtk_text_view_accessible_get_n_selections(AtkText * text)1175 gtk_text_view_accessible_get_n_selections (AtkText *text)
1176 {
1177   GtkWidget *widget;
1178   GtkTextBuffer *buffer;
1179 
1180   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1181   if (widget == NULL)
1182     return 0;
1183 
1184   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1185   if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
1186     return 1;
1187 
1188   return 0;
1189 }
1190 
1191 static gchar *
gtk_text_view_accessible_get_selection(AtkText * atk_text,gint selection_num,gint * start_pos,gint * end_pos)1192 gtk_text_view_accessible_get_selection (AtkText *atk_text,
1193                                         gint     selection_num,
1194                                         gint    *start_pos,
1195                                         gint    *end_pos)
1196 {
1197   GtkTextView *view;
1198   GtkWidget *widget;
1199   GtkTextBuffer *buffer;
1200   GtkTextIter start, end;
1201   gchar *text;
1202 
1203   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
1204   if (widget == NULL)
1205     return NULL;
1206 
1207   if (selection_num != 0)
1208     return NULL;
1209 
1210   view = GTK_TEXT_VIEW (widget);
1211   buffer = gtk_text_view_get_buffer (view);
1212 
1213   if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
1214     text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1215   else
1216     text = NULL;
1217 
1218   *start_pos = gtk_text_iter_get_offset (&start);
1219   *end_pos = gtk_text_iter_get_offset (&end);
1220 
1221   return text;
1222 }
1223 
1224 static gboolean
gtk_text_view_accessible_add_selection(AtkText * text,gint start_pos,gint end_pos)1225 gtk_text_view_accessible_add_selection (AtkText *text,
1226                                         gint     start_pos,
1227                                         gint     end_pos)
1228 {
1229   GtkWidget *widget;
1230   GtkTextBuffer *buffer;
1231   GtkTextIter start, end;
1232 
1233   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1234   if (widget == NULL)
1235     return FALSE;
1236 
1237   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1238 
1239   if (!gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
1240     {
1241       gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
1242       gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
1243       gtk_text_buffer_select_range (buffer, &end, &start);
1244 
1245       return TRUE;
1246     }
1247   else
1248     return FALSE;
1249 }
1250 
1251 static gboolean
gtk_text_view_accessible_remove_selection(AtkText * text,gint selection_num)1252 gtk_text_view_accessible_remove_selection (AtkText *text,
1253                                            gint     selection_num)
1254 {
1255   GtkWidget *widget;
1256   GtkTextBuffer *buffer;
1257   GtkTextMark *insert;
1258   GtkTextIter iter;
1259   GtkTextIter start, end;
1260 
1261   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1262   if (widget == NULL)
1263     return FALSE;
1264 
1265   if (selection_num != 0)
1266      return FALSE;
1267 
1268   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1269 
1270   if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
1271     {
1272       insert = gtk_text_buffer_get_insert (buffer);
1273       gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
1274       gtk_text_buffer_place_cursor (buffer, &iter);
1275       return TRUE;
1276     }
1277   else
1278     return FALSE;
1279 }
1280 
1281 static gboolean
gtk_text_view_accessible_set_selection(AtkText * text,gint selection_num,gint start_pos,gint end_pos)1282 gtk_text_view_accessible_set_selection (AtkText *text,
1283                                         gint     selection_num,
1284                                         gint     start_pos,
1285                                         gint     end_pos)
1286 {
1287   GtkWidget *widget;
1288   GtkTextBuffer *buffer;
1289   GtkTextIter start, end;
1290 
1291   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1292   if (widget == NULL)
1293     return FALSE;
1294 
1295   if (selection_num != 0)
1296     return FALSE;
1297 
1298   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1299 
1300   if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
1301     {
1302       gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
1303       gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
1304       gtk_text_buffer_select_range (buffer, &end, &start);
1305 
1306       return TRUE;
1307     }
1308   else
1309     return FALSE;
1310 }
1311 
1312 static gboolean
gtk_text_view_accessible_scroll_substring_to(AtkText * text,gint start_offset,gint end_offset,AtkScrollType type)1313 gtk_text_view_accessible_scroll_substring_to(AtkText *text,
1314                                              gint start_offset,
1315                                              gint end_offset,
1316                                              AtkScrollType type)
1317 {
1318   GtkTextView *view;
1319   GtkWidget *widget;
1320   GtkTextBuffer *buffer;
1321   GtkTextIter iter;
1322   gdouble xalign = -1.0, yalign = -1.0;
1323   gboolean use_align = TRUE;
1324   gint offset, rtl = 0;
1325 
1326   if (end_offset < start_offset)
1327     return FALSE;
1328 
1329   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1330   if (widget == NULL)
1331     return FALSE;
1332 
1333   view = GTK_TEXT_VIEW (widget);
1334   buffer = gtk_text_view_get_buffer (view);
1335 
1336   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1337     rtl = 1;
1338 
1339   /*
1340    * Opportunistically pick which offset should be used to calculate
1341    * the scrolling factor.
1342    *
1343    * Considering only an extremity of the substring is good enough when
1344    * the selected string doesn't include line break and isn't larger than
1345    * the visible rectangle.
1346    */
1347   switch (type)
1348     {
1349     case ATK_SCROLL_TOP_LEFT:
1350       offset = (rtl) ? end_offset : start_offset;
1351       xalign = 0.0;
1352       yalign = 0.0;
1353       break;
1354     case ATK_SCROLL_BOTTOM_RIGHT:
1355       offset = (rtl) ? start_offset : end_offset;
1356       xalign = 1.0;
1357       yalign = 1.0;
1358       break;
1359     case ATK_SCROLL_TOP_EDGE:
1360       offset = start_offset;
1361       yalign = 0.0;
1362       break;
1363     case ATK_SCROLL_BOTTOM_EDGE:
1364       offset = end_offset;
1365       yalign = 1.0;
1366       break;
1367     case ATK_SCROLL_LEFT_EDGE:
1368       offset = (rtl) ? end_offset : start_offset;
1369       xalign = 0.0;
1370       break;
1371     case ATK_SCROLL_RIGHT_EDGE:
1372       offset = (rtl) ? start_offset : end_offset;
1373       xalign = 1.0;
1374       break;
1375     case ATK_SCROLL_ANYWHERE:
1376       offset = start_offset;
1377       use_align = FALSE;
1378       xalign = yalign = 0.0;
1379       break;
1380     default:
1381       return FALSE;
1382     }
1383 
1384   gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
1385 
1386   /* Get current iter location to be able to scroll in a single direction. */
1387   if (use_align && (xalign == -1.0 || yalign == -1.0))
1388     {
1389       GdkRectangle rect, irect;
1390 
1391       gtk_text_view_get_visible_rect (view, &rect);
1392       gtk_text_view_get_iter_location (view, &iter, &irect);
1393 
1394       if (xalign == -1.0)
1395         xalign = ((gdouble) (irect.x - rect.x)) / (rect.width - 1);
1396       if (yalign == -1.0)
1397         yalign = ((gdouble) (irect.y - rect.y)) / (rect.height - 1);
1398     }
1399 
1400   gtk_text_view_scroll_to_iter (view, &iter, 0, use_align, xalign, yalign);
1401 
1402   return TRUE;
1403 }
1404 
1405 static void
atk_text_interface_init(AtkTextIface * iface)1406 atk_text_interface_init (AtkTextIface *iface)
1407 {
1408   iface->get_text = gtk_text_view_accessible_get_text;
1409   iface->get_text_after_offset = gtk_text_view_accessible_get_text_after_offset;
1410   iface->get_text_at_offset = gtk_text_view_accessible_get_text_at_offset;
1411   iface->get_text_before_offset = gtk_text_view_accessible_get_text_before_offset;
1412   iface->get_character_at_offset = gtk_text_view_accessible_get_character_at_offset;
1413   iface->get_character_count = gtk_text_view_accessible_get_character_count;
1414   iface->get_caret_offset = gtk_text_view_accessible_get_caret_offset;
1415   iface->set_caret_offset = gtk_text_view_accessible_set_caret_offset;
1416   iface->get_offset_at_point = gtk_text_view_accessible_get_offset_at_point;
1417   iface->get_character_extents = gtk_text_view_accessible_get_character_extents;
1418   iface->get_n_selections = gtk_text_view_accessible_get_n_selections;
1419   iface->get_selection = gtk_text_view_accessible_get_selection;
1420   iface->add_selection = gtk_text_view_accessible_add_selection;
1421   iface->remove_selection = gtk_text_view_accessible_remove_selection;
1422   iface->set_selection = gtk_text_view_accessible_set_selection;
1423   iface->get_run_attributes = gtk_text_view_accessible_get_run_attributes;
1424   iface->get_default_attributes = gtk_text_view_accessible_get_default_attributes;
1425   iface->scroll_substring_to = gtk_text_view_accessible_scroll_substring_to;
1426 }
1427 
1428 /* atkeditabletext.h */
1429 
1430 static gboolean
gtk_text_view_accessible_set_run_attributes(AtkEditableText * text,AtkAttributeSet * attributes,gint start_offset,gint end_offset)1431 gtk_text_view_accessible_set_run_attributes (AtkEditableText *text,
1432                                              AtkAttributeSet *attributes,
1433                                              gint             start_offset,
1434                                              gint             end_offset)
1435 {
1436   GtkTextView *view;
1437   GtkTextBuffer *buffer;
1438   GtkWidget *widget;
1439   GtkTextTag *tag;
1440   GtkTextIter start;
1441   GtkTextIter end;
1442   gint j;
1443   GdkColor *color;
1444   gchar** RGB_vals;
1445   GSList *l;
1446 
1447   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1448   if (widget == NULL)
1449     return FALSE;
1450 
1451   view = GTK_TEXT_VIEW (widget);
1452   if (!gtk_text_view_get_editable (view))
1453     return FALSE;
1454 
1455   buffer = gtk_text_view_get_buffer (view);
1456 
1457   if (attributes == NULL)
1458     return FALSE;
1459 
1460   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
1461   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
1462 
1463   tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
1464 
1465   for (l = attributes; l; l = l->next)
1466     {
1467       gchar *name;
1468       gchar *value;
1469       AtkAttribute *at;
1470 
1471       at = l->data;
1472 
1473       name = at->name;
1474       value = at->value;
1475 
1476       if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_LEFT_MARGIN)))
1477         g_object_set (G_OBJECT (tag), "left-margin", atoi (value), NULL);
1478 
1479       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_RIGHT_MARGIN)))
1480         g_object_set (G_OBJECT (tag), "right-margin", atoi (value), NULL);
1481 
1482       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_INDENT)))
1483         g_object_set (G_OBJECT (tag), "indent", atoi (value), NULL);
1484 
1485       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_ABOVE_LINES)))
1486         g_object_set (G_OBJECT (tag), "pixels-above-lines", atoi (value), NULL);
1487 
1488       else if (!strcmp(name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_BELOW_LINES)))
1489         g_object_set (G_OBJECT (tag), "pixels-below-lines", atoi (value), NULL);
1490 
1491       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP)))
1492         g_object_set (G_OBJECT (tag), "pixels-inside-wrap", atoi (value), NULL);
1493 
1494       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_SIZE)))
1495         g_object_set (G_OBJECT (tag), "size", atoi (value), NULL);
1496 
1497       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_RISE)))
1498         g_object_set (G_OBJECT (tag), "rise", atoi (value), NULL);
1499 
1500       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_WEIGHT)))
1501         g_object_set (G_OBJECT (tag), "weight", atoi (value), NULL);
1502 
1503       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_BG_FULL_HEIGHT)))
1504         {
1505           g_object_set (G_OBJECT (tag), "bg-full-height",
1506                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_BG_FULL_HEIGHT, 0))),
1507                    NULL);
1508         }
1509 
1510       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_LANGUAGE)))
1511         g_object_set (G_OBJECT (tag), "language", value, NULL);
1512 
1513       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_FAMILY_NAME)))
1514         g_object_set (G_OBJECT (tag), "family", value, NULL);
1515 
1516       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_EDITABLE)))
1517         {
1518           g_object_set (G_OBJECT (tag), "editable",
1519                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0))),
1520                    NULL);
1521         }
1522 
1523       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_INVISIBLE)))
1524         {
1525           g_object_set (G_OBJECT (tag), "invisible",
1526                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0))),
1527                    NULL);
1528         }
1529 
1530       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_UNDERLINE)))
1531         {
1532           for (j = 0; j < 3; j++)
1533             {
1534               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, j)))
1535                 {
1536                   g_object_set (G_OBJECT (tag), "underline", j, NULL);
1537                   break;
1538                 }
1539             }
1540         }
1541 
1542       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STRIKETHROUGH)))
1543         {
1544           g_object_set (G_OBJECT (tag), "strikethrough",
1545                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 0))),
1546                    NULL);
1547         }
1548 
1549       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_BG_COLOR)))
1550         {
1551           RGB_vals = g_strsplit (value, ",", 3);
1552           color = g_malloc (sizeof (GdkColor));
1553           color->red = atoi (RGB_vals[0]);
1554           color->green = atoi (RGB_vals[1]);
1555           color->blue = atoi (RGB_vals[2]);
1556           g_object_set (G_OBJECT (tag), "background-gdk", color, NULL);
1557         }
1558 
1559       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_FG_COLOR)))
1560         {
1561           RGB_vals = g_strsplit (value, ",", 3);
1562           color = g_malloc (sizeof (GdkColor));
1563           color->red = atoi (RGB_vals[0]);
1564           color->green = atoi (RGB_vals[1]);
1565           color->blue = atoi (RGB_vals[2]);
1566           g_object_set (G_OBJECT (tag), "foreground-gdk", color, NULL);
1567         }
1568 
1569       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STRETCH)))
1570         {
1571           for (j = 0; j < 9; j++)
1572             {
1573               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, j)))
1574                 {
1575                   g_object_set (G_OBJECT (tag), "stretch", j, NULL);
1576                   break;
1577                 }
1578             }
1579         }
1580 
1581       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_JUSTIFICATION)))
1582         {
1583           for (j = 0; j < 4; j++)
1584             {
1585               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, j)))
1586                 {
1587                   g_object_set (G_OBJECT (tag), "justification", j, NULL);
1588                   break;
1589                 }
1590             }
1591         }
1592 
1593       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_DIRECTION)))
1594         {
1595           for (j = 0; j < 3; j++)
1596             {
1597               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, j)))
1598                 {
1599                   g_object_set (G_OBJECT (tag), "direction", j, NULL);
1600                   break;
1601                 }
1602             }
1603         }
1604 
1605       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_VARIANT)))
1606         {
1607           for (j = 0; j < 2; j++)
1608             {
1609               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, j)))
1610                 {
1611                   g_object_set (G_OBJECT (tag), "variant", j, NULL);
1612                   break;
1613                 }
1614             }
1615         }
1616 
1617       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_WRAP_MODE)))
1618         {
1619           for (j = 0; j < 3; j++)
1620             {
1621               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, j)))
1622                 {
1623                   g_object_set (G_OBJECT (tag), "wrap-mode", j, NULL);
1624                   break;
1625                 }
1626             }
1627         }
1628 
1629       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STYLE)))
1630         {
1631           for (j = 0; j < 3; j++)
1632             {
1633               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, j)))
1634                 {
1635                   g_object_set (G_OBJECT (tag), "style", j, NULL);
1636                   break;
1637               }
1638             }
1639         }
1640 
1641       else
1642         return FALSE;
1643     }
1644 
1645   gtk_text_buffer_apply_tag (buffer, tag, &start, &end);
1646 
1647   return TRUE;
1648 }
1649 
1650 static void
gtk_text_view_accessible_set_text_contents(AtkEditableText * text,const gchar * string)1651 gtk_text_view_accessible_set_text_contents (AtkEditableText *text,
1652                                             const gchar     *string)
1653 {
1654   GtkTextView *view;
1655   GtkWidget *widget;
1656   GtkTextBuffer *buffer;
1657 
1658   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1659   if (widget == NULL)
1660     return;
1661 
1662   view = GTK_TEXT_VIEW (widget);
1663   if (!gtk_text_view_get_editable (view))
1664     return;
1665 
1666   buffer = gtk_text_view_get_buffer (view);
1667   gtk_text_buffer_set_text (buffer, string, -1);
1668 }
1669 
1670 static void
gtk_text_view_accessible_insert_text(AtkEditableText * text,const gchar * string,gint length,gint * position)1671 gtk_text_view_accessible_insert_text (AtkEditableText *text,
1672                                       const gchar     *string,
1673                                       gint             length,
1674                                       gint            *position)
1675 {
1676   GtkTextView *view;
1677   GtkWidget *widget;
1678   GtkTextBuffer *buffer;
1679   GtkTextIter iter;
1680 
1681   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1682   if (widget == NULL)
1683     return;
1684 
1685   view = GTK_TEXT_VIEW (widget);
1686   if (!gtk_text_view_get_editable (view))
1687     return;
1688 
1689   buffer = gtk_text_view_get_buffer (view);
1690   gtk_text_buffer_get_iter_at_offset (buffer, &iter, *position);
1691   gtk_text_buffer_insert (buffer, &iter, string, length);
1692 }
1693 
1694 static void
gtk_text_view_accessible_copy_text(AtkEditableText * text,gint start_pos,gint end_pos)1695 gtk_text_view_accessible_copy_text (AtkEditableText *text,
1696                                     gint             start_pos,
1697                                     gint             end_pos)
1698 {
1699   GtkWidget *widget;
1700   GtkTextBuffer *buffer;
1701   GtkTextIter start, end;
1702   gchar *str;
1703   GtkClipboard *clipboard;
1704 
1705   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1706   if (widget == NULL)
1707     return;
1708 
1709   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1710 
1711   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
1712   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
1713   str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1714 
1715   clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
1716   gtk_clipboard_set_text (clipboard, str, -1);
1717 }
1718 
1719 static void
gtk_text_view_accessible_cut_text(AtkEditableText * text,gint start_pos,gint end_pos)1720 gtk_text_view_accessible_cut_text (AtkEditableText *text,
1721                                    gint             start_pos,
1722                                    gint             end_pos)
1723 {
1724   GtkTextView *view;
1725   GtkWidget *widget;
1726   GtkTextBuffer *buffer;
1727   GtkTextIter start, end;
1728   gchar *str;
1729   GtkClipboard *clipboard;
1730 
1731   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1732   if (widget == NULL)
1733     return;
1734 
1735   view = GTK_TEXT_VIEW (widget);
1736   if (!gtk_text_view_get_editable (view))
1737     return;
1738   buffer = gtk_text_view_get_buffer (view);
1739 
1740   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
1741   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
1742   str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1743   clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
1744   gtk_clipboard_set_text (clipboard, str, -1);
1745   gtk_text_buffer_delete (buffer, &start, &end);
1746 }
1747 
1748 static void
gtk_text_view_accessible_delete_text(AtkEditableText * text,gint start_pos,gint end_pos)1749 gtk_text_view_accessible_delete_text (AtkEditableText *text,
1750                                       gint             start_pos,
1751                                       gint             end_pos)
1752 {
1753   GtkTextView *view;
1754   GtkWidget *widget;
1755   GtkTextBuffer *buffer;
1756   GtkTextIter start_itr;
1757   GtkTextIter end_itr;
1758 
1759   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1760   if (widget == NULL)
1761     return;
1762 
1763   view = GTK_TEXT_VIEW (widget);
1764   if (!gtk_text_view_get_editable (view))
1765     return;
1766   buffer = gtk_text_view_get_buffer (view);
1767 
1768   gtk_text_buffer_get_iter_at_offset (buffer, &start_itr, start_pos);
1769   gtk_text_buffer_get_iter_at_offset (buffer, &end_itr, end_pos);
1770   gtk_text_buffer_delete (buffer, &start_itr, &end_itr);
1771 }
1772 
1773 typedef struct
1774 {
1775   GtkTextBuffer* buffer;
1776   gint position;
1777 } PasteData;
1778 
1779 static void
paste_received(GtkClipboard * clipboard,const gchar * text,gpointer data)1780 paste_received (GtkClipboard *clipboard,
1781                 const gchar  *text,
1782                 gpointer      data)
1783 {
1784   PasteData* paste = data;
1785   GtkTextIter pos_itr;
1786 
1787   if (text)
1788     {
1789       gtk_text_buffer_get_iter_at_offset (paste->buffer, &pos_itr, paste->position);
1790       gtk_text_buffer_insert (paste->buffer, &pos_itr, text, -1);
1791     }
1792 
1793   g_object_unref (paste->buffer);
1794 }
1795 
1796 static void
gtk_text_view_accessible_paste_text(AtkEditableText * text,gint position)1797 gtk_text_view_accessible_paste_text (AtkEditableText *text,
1798                                      gint             position)
1799 {
1800   GtkTextView *view;
1801   GtkWidget *widget;
1802   GtkTextBuffer *buffer;
1803   PasteData paste;
1804   GtkClipboard *clipboard;
1805 
1806   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1807   if (widget == NULL)
1808     return;
1809 
1810   view = GTK_TEXT_VIEW (widget);
1811   if (!gtk_text_view_get_editable (view))
1812     return;
1813   buffer = gtk_text_view_get_buffer (view);
1814 
1815   paste.buffer = buffer;
1816   paste.position = position;
1817 
1818   g_object_ref (paste.buffer);
1819   clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
1820   gtk_clipboard_request_text (clipboard, paste_received, &paste);
1821 }
1822 
1823 static void
atk_editable_text_interface_init(AtkEditableTextIface * iface)1824 atk_editable_text_interface_init (AtkEditableTextIface *iface)
1825 {
1826   iface->set_text_contents = gtk_text_view_accessible_set_text_contents;
1827   iface->insert_text = gtk_text_view_accessible_insert_text;
1828   iface->copy_text = gtk_text_view_accessible_copy_text;
1829   iface->cut_text = gtk_text_view_accessible_cut_text;
1830   iface->delete_text = gtk_text_view_accessible_delete_text;
1831   iface->paste_text = gtk_text_view_accessible_paste_text;
1832   iface->set_run_attributes = gtk_text_view_accessible_set_run_attributes;
1833 }
1834 
1835 /* Callbacks */
1836 
1837 static void
gtk_text_view_accessible_update_cursor(GtkTextViewAccessible * accessible,GtkTextBuffer * buffer)1838 gtk_text_view_accessible_update_cursor (GtkTextViewAccessible *accessible,
1839                                         GtkTextBuffer *        buffer)
1840 {
1841   int prev_insert_offset, prev_selection_bound;
1842   int insert_offset, selection_bound;
1843   GtkTextIter iter;
1844 
1845   prev_insert_offset = accessible->priv->insert_offset;
1846   prev_selection_bound = accessible->priv->selection_bound;
1847 
1848   gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer));
1849   insert_offset = gtk_text_iter_get_offset (&iter);
1850   gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_selection_bound (buffer));
1851   selection_bound = gtk_text_iter_get_offset (&iter);
1852 
1853   if (prev_insert_offset == insert_offset && prev_selection_bound == selection_bound)
1854     return;
1855 
1856   accessible->priv->insert_offset = insert_offset;
1857   accessible->priv->selection_bound = selection_bound;
1858 
1859   if (prev_insert_offset != insert_offset)
1860     g_signal_emit_by_name (accessible, "text-caret-moved", insert_offset);
1861 
1862   if (prev_insert_offset != prev_selection_bound || insert_offset != selection_bound)
1863     g_signal_emit_by_name (accessible, "text-selection-changed");
1864 }
1865 
1866 static void
insert_text_cb(GtkTextBuffer * buffer,GtkTextIter * iter,gchar * text,gint len,gpointer data)1867 insert_text_cb (GtkTextBuffer *buffer,
1868                 GtkTextIter   *iter,
1869                 gchar         *text,
1870                 gint           len,
1871                 gpointer       data)
1872 {
1873   GtkTextViewAccessible *accessible = data;
1874   gint position;
1875   gint length;
1876 
1877   position = gtk_text_iter_get_offset (iter);
1878   length = g_utf8_strlen (text, len);
1879 
1880   g_signal_emit_by_name (accessible, "text-changed::insert", position - length, length);
1881 
1882   gtk_text_view_accessible_update_cursor (accessible, buffer);
1883 }
1884 
1885 static void
delete_range_cb(GtkTextBuffer * buffer,GtkTextIter * start,GtkTextIter * end,gpointer data)1886 delete_range_cb (GtkTextBuffer *buffer,
1887                  GtkTextIter   *start,
1888                  GtkTextIter   *end,
1889                  gpointer       data)
1890 {
1891   GtkTextViewAccessible *accessible = data;
1892   gint offset, length;
1893 
1894   offset = gtk_text_iter_get_offset (start);
1895   length = gtk_text_iter_get_offset (end) - offset;
1896 
1897   g_signal_emit_by_name (accessible,
1898                          "text-changed::delete",
1899                          offset,
1900                          length);
1901 }
1902 
1903 static void
delete_range_after_cb(GtkTextBuffer * buffer,GtkTextIter * start,GtkTextIter * end,gpointer data)1904 delete_range_after_cb (GtkTextBuffer *buffer,
1905                        GtkTextIter   *start,
1906                        GtkTextIter   *end,
1907                        gpointer       data)
1908 {
1909   GtkTextViewAccessible *accessible = data;
1910 
1911   gtk_text_view_accessible_update_cursor (accessible, buffer);
1912 }
1913 
1914 static void
mark_set_cb(GtkTextBuffer * buffer,GtkTextIter * location,GtkTextMark * mark,gpointer data)1915 mark_set_cb (GtkTextBuffer *buffer,
1916              GtkTextIter   *location,
1917              GtkTextMark   *mark,
1918              gpointer       data)
1919 {
1920   GtkTextViewAccessible *accessible = data;
1921 
1922   /*
1923    * Only generate the signal for the "insert" mark, which
1924    * represents the cursor.
1925    */
1926   if (mark == gtk_text_buffer_get_insert (buffer))
1927     {
1928       gtk_text_view_accessible_update_cursor (accessible, buffer);
1929     }
1930   else if (mark == gtk_text_buffer_get_selection_bound (buffer))
1931     {
1932       gtk_text_view_accessible_update_cursor (accessible, buffer);
1933     }
1934 }
1935 
1936 static gint
gail_streamable_content_get_n_mime_types(AtkStreamableContent * streamable)1937 gail_streamable_content_get_n_mime_types (AtkStreamableContent *streamable)
1938 {
1939   GtkWidget *widget;
1940   GtkTextBuffer *buffer;
1941   gint n_mime_types = 0;
1942 
1943   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (streamable));
1944   if (widget == NULL)
1945     return 0;
1946 
1947   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1948   if (buffer)
1949     {
1950       gint i;
1951       gboolean advertises_plaintext = FALSE;
1952       GdkAtom *atoms;
1953 
1954       atoms = gtk_text_buffer_get_serialize_formats (buffer, &n_mime_types);
1955       for (i = 0; i < n_mime_types-1; ++i)
1956         if (!strcmp ("text/plain", gdk_atom_name (atoms[i])))
1957             advertises_plaintext = TRUE;
1958       if (!advertises_plaintext)
1959         n_mime_types++;
1960     }
1961 
1962   return n_mime_types;
1963 }
1964 
1965 static const gchar *
gail_streamable_content_get_mime_type(AtkStreamableContent * streamable,gint i)1966 gail_streamable_content_get_mime_type (AtkStreamableContent *streamable,
1967                                        gint                  i)
1968 {
1969   GtkWidget *widget;
1970   GtkTextBuffer *buffer;
1971 
1972   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (streamable));
1973   if (widget == NULL)
1974     return 0;
1975 
1976   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1977   if (buffer)
1978     {
1979       gint n_mime_types = 0;
1980       GdkAtom *atoms;
1981 
1982       atoms = gtk_text_buffer_get_serialize_formats (buffer, &n_mime_types);
1983       if (i < n_mime_types)
1984         return gdk_atom_name (atoms [i]);
1985       else if (i == n_mime_types)
1986         return "text/plain";
1987     }
1988 
1989   return NULL;
1990 }
1991 
1992 static GIOChannel *
gail_streamable_content_get_stream(AtkStreamableContent * streamable,const gchar * mime_type)1993 gail_streamable_content_get_stream (AtkStreamableContent *streamable,
1994                                     const gchar          *mime_type)
1995 {
1996   GtkWidget *widget;
1997   GtkTextBuffer *buffer;
1998   gint i, n_mime_types = 0;
1999   GdkAtom *atoms;
2000 
2001   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (streamable));
2002   if (widget == NULL)
2003     return 0;
2004 
2005   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
2006   if (!buffer)
2007     return NULL;
2008 
2009   atoms = gtk_text_buffer_get_serialize_formats (buffer, &n_mime_types);
2010 
2011   for (i = 0; i < n_mime_types; ++i)
2012     {
2013       if (!strcmp ("text/plain", mime_type) ||
2014           !strcmp (gdk_atom_name (atoms[i]), mime_type))
2015         {
2016           guint8 *cbuf;
2017           GError *err = NULL;
2018           gsize len, written;
2019           gchar tname[80];
2020           GtkTextIter start, end;
2021           GIOChannel *gio = NULL;
2022           int fd;
2023 
2024           gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
2025           gtk_text_buffer_get_iter_at_offset (buffer, &end, -1);
2026           if (!strcmp ("text/plain", mime_type))
2027             {
2028               cbuf = (guint8*) gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
2029               len = strlen ((const char *) cbuf);
2030             }
2031           else
2032             {
2033               cbuf = gtk_text_buffer_serialize (buffer, buffer, atoms[i], &start, &end, &len);
2034             }
2035           g_snprintf (tname, 20, "streamXXXXXX");
2036           fd = g_mkstemp (tname);
2037           gio = g_io_channel_unix_new (fd);
2038           g_io_channel_set_encoding (gio, NULL, &err);
2039           if (!err)
2040             g_io_channel_write_chars (gio, (const char *) cbuf, (gssize) len, &written, &err);
2041           else
2042             g_message ("%s", err->message);
2043           if (!err)
2044             g_io_channel_seek_position (gio, 0, G_SEEK_SET, &err);
2045           else
2046             g_message ("%s", err->message);
2047           if (!err)
2048             g_io_channel_flush (gio, &err);
2049           else
2050             g_message ("%s", err->message);
2051           if (err)
2052             {
2053               g_message ("<error writing to stream [%s]>", tname);
2054               g_error_free (err);
2055             }
2056           /* make sure the file is removed on unref of the giochannel */
2057           else
2058             {
2059               g_unlink (tname);
2060               return gio;
2061             }
2062         }
2063     }
2064 
2065   return NULL;
2066 }
2067 
2068 static void
atk_streamable_content_interface_init(AtkStreamableContentIface * iface)2069 atk_streamable_content_interface_init (AtkStreamableContentIface *iface)
2070 {
2071   iface->get_n_mime_types = gail_streamable_content_get_n_mime_types;
2072   iface->get_mime_type = gail_streamable_content_get_mime_type;
2073   iface->get_stream = gail_streamable_content_get_stream;
2074 }
2075 
2076 void
_gtk_text_view_accessible_set_buffer(GtkTextView * textview,GtkTextBuffer * old_buffer)2077 _gtk_text_view_accessible_set_buffer (GtkTextView   *textview,
2078                                       GtkTextBuffer *old_buffer)
2079 {
2080   GtkTextViewAccessible *accessible;
2081 
2082   g_return_if_fail (GTK_IS_TEXT_VIEW (textview));
2083   g_return_if_fail (old_buffer == NULL || GTK_IS_TEXT_BUFFER (old_buffer));
2084 
2085   accessible = GTK_TEXT_VIEW_ACCESSIBLE (_gtk_widget_peek_accessible (GTK_WIDGET (textview)));
2086   if (accessible == NULL)
2087     return;
2088 
2089   gtk_text_view_accessible_change_buffer (accessible,
2090                                           old_buffer,
2091                                           gtk_text_view_get_buffer (textview));
2092 }
2093 
2094