]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtktextviewaccessible.c
Convert GailContainer to GtkContainerAccessible
[~andy/gtk] / gtk / a11y / gtktextviewaccessible.c
1 /* GAIL - The GNOME Accessibility Implementation Library
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <glib-object.h>
27 #include <glib/gstdio.h>
28 #include <gtk/gtk.h>
29 #include "gtktextviewaccessible.h"
30 #include "gailmisc.h"
31
32
33 static void setup_buffer (GtkTextView           *view,GtkTextViewAccessible *accessible);
34 static void       insert_text_cb       (GtkTextBuffer    *buffer,
35                                                         GtkTextIter      *arg1,
36                                                         gchar            *arg2,
37                                                         gint             arg3,
38                                                         gpointer         user_data);
39 static void       delete_range_cb      (GtkTextBuffer    *buffer,
40                                                         GtkTextIter      *arg1,
41                                                         GtkTextIter      *arg2,
42                                                         gpointer         user_data);
43 static void       changed_cb           (GtkTextBuffer    *buffer,
44                                                         gpointer         user_data);
45 static void       mark_set_cb          (GtkTextBuffer    *buffer,
46                                                         GtkTextIter      *arg1,
47                                                         GtkTextMark      *arg2,
48                                                         gpointer         user_data);
49 static gint             insert_idle_handler            (gpointer         data);
50
51
52 static void atk_editable_text_interface_init      (AtkEditableTextIface      *iface);
53 static void atk_text_interface_init               (AtkTextIface              *iface);
54 static void atk_streamable_content_interface_init (AtkStreamableContentIface *iface);
55
56 G_DEFINE_TYPE_WITH_CODE (GtkTextViewAccessible, gtk_text_view_accessible, GTK_TYPE_CONTAINER_ACCESSIBLE,
57                          G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init)
58                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init)
59                          G_IMPLEMENT_INTERFACE (ATK_TYPE_STREAMABLE_CONTENT, atk_streamable_content_interface_init))
60
61
62 static void
63 gtk_text_view_accessible_initialize (AtkObject *obj,
64                                      gpointer   data)
65 {
66   ATK_OBJECT_CLASS (gtk_text_view_accessible_parent_class)->initialize (obj, data);
67
68   setup_buffer (GTK_TEXT_VIEW (data), GTK_TEXT_VIEW_ACCESSIBLE (obj));
69
70   obj->role = ATK_ROLE_TEXT;
71 }
72
73 static void
74 gtk_text_view_accessible_finalize (GObject *object)
75 {
76   GtkTextViewAccessible *text_view = GTK_TEXT_VIEW_ACCESSIBLE (object);
77
78   if (text_view->insert_notify_handler)
79     g_source_remove (text_view->insert_notify_handler);
80
81   G_OBJECT_CLASS (gtk_text_view_accessible_parent_class)->finalize (object);
82 }
83
84 static void
85 gtk_text_view_accessible_notify_gtk (GObject    *obj,
86                                      GParamSpec *pspec)
87 {
88   AtkObject *atk_obj;
89
90   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (obj));
91
92   if (!strcmp (pspec->name, "editable"))
93     {
94       gboolean editable;
95
96       editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (obj));
97       atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, editable);
98     }
99   else if (!strcmp (pspec->name, "buffer"))
100     {
101       setup_buffer (GTK_TEXT_VIEW (obj), GTK_TEXT_VIEW_ACCESSIBLE (atk_obj));
102     }
103   else
104     GAIL_WIDGET_CLASS (gtk_text_view_accessible_parent_class)->notify_gtk (obj, pspec);
105 }
106
107 static AtkStateSet*
108 gtk_text_view_accessible_ref_state_set (AtkObject *accessible)
109 {
110   AtkStateSet *state_set;
111   GtkWidget *widget;
112
113   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
114   if (widget == NULL)
115     return NULL;
116
117   state_set = ATK_OBJECT_CLASS (gtk_text_view_accessible_parent_class)->ref_state_set (accessible);
118
119   if (gtk_text_view_get_editable (GTK_TEXT_VIEW (widget)))
120     atk_state_set_add_state (state_set, ATK_STATE_EDITABLE);
121   atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE);
122
123   return state_set;
124 }
125
126 static void
127 gtk_text_view_accessible_class_init (GtkTextViewAccessibleClass *klass)
128 {
129   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
130   AtkObjectClass  *class = ATK_OBJECT_CLASS (klass);
131   GailWidgetClass *widget_class;
132
133   widget_class = (GailWidgetClass*)klass;
134
135   gobject_class->finalize = gtk_text_view_accessible_finalize;
136
137   class->ref_state_set = gtk_text_view_accessible_ref_state_set;
138   class->initialize = gtk_text_view_accessible_initialize;
139
140   widget_class->notify_gtk = gtk_text_view_accessible_notify_gtk;
141 }
142
143 static void
144 gtk_text_view_accessible_init (GtkTextViewAccessible *accessible)
145 {
146   accessible->signal_name = NULL;
147   accessible->previous_insert_offset = -1;
148   accessible->previous_selection_bound = -1;
149   accessible->insert_notify_handler = 0;
150 }
151
152 static void
153 setup_buffer (GtkTextView           *view,
154               GtkTextViewAccessible *accessible)
155 {
156   GtkTextBuffer *buffer;
157
158   buffer = gtk_text_view_get_buffer (view);
159
160   /* Set up signal callbacks */
161   g_signal_connect_data (buffer, "insert-text",
162                          (GCallback) insert_text_cb, view, NULL, 0);
163   g_signal_connect_data (buffer, "delete-range",
164                          (GCallback) delete_range_cb, view, NULL, 0);
165   g_signal_connect_data (buffer, "mark-set",
166                          (GCallback) mark_set_cb, view, NULL, 0);
167   g_signal_connect_data (buffer, "changed",
168                          (GCallback) changed_cb, view, NULL, 0);
169
170 }
171
172 static gchar *
173 gtk_text_view_accessible_get_text (AtkText *text,
174                                    gint     start_offset,
175                                    gint     end_offset)
176 {
177   GtkTextView *view;
178   GtkTextBuffer *buffer;
179   GtkTextIter start, end;
180   GtkWidget *widget;
181
182   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
183   if (widget == NULL)
184     return NULL;
185
186   view = GTK_TEXT_VIEW (widget);
187   buffer = gtk_text_view_get_buffer (view);
188   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
189   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
190
191   return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
192 }
193
194 static gchar *
195 gtk_text_view_accessible_get_text_after_offset (AtkText         *text,
196                                                 gint             offset,
197                                                 AtkTextBoundary  boundary_type,
198                                                 gint            *start_offset,
199                                                 gint            *end_offset)
200 {
201   GtkWidget *widget;
202   GtkTextBuffer *buffer;
203   GtkTextIter pos;
204   GtkTextIter start, end;
205
206   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
207   if (widget == NULL)
208     return NULL;
209
210   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
211   gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset);
212   _gtk_text_buffer_get_text_after (buffer, boundary_type,
213                                    &pos, &start, &end);
214   *start_offset = gtk_text_iter_get_offset (&start);
215   *end_offset = gtk_text_iter_get_offset (&end);
216   return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
217 }
218
219 static gchar *
220 gtk_text_view_accessible_get_text_at_offset (AtkText         *text,
221                                              gint             offset,
222                                              AtkTextBoundary  boundary_type,
223                                              gint            *start_offset,
224                                              gint            *end_offset)
225 {
226   GtkWidget *widget;
227   GtkTextBuffer *buffer;
228   GtkTextIter pos;
229   GtkTextIter start, end;
230
231   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
232   if (widget == NULL)
233     return NULL;
234
235   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
236   gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset);
237   _gtk_text_buffer_get_text_at (buffer, boundary_type,
238                                 &pos, &start, &end);
239   *start_offset = gtk_text_iter_get_offset (&start);
240   *end_offset = gtk_text_iter_get_offset (&end);
241   return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
242 }
243
244 static gchar *
245 gtk_text_view_accessible_get_text_before_offset (AtkText         *text,
246                                                  gint             offset,
247                                                  AtkTextBoundary  boundary_type,
248                                                  gint            *start_offset,
249                                                  gint            *end_offset)
250 {
251   GtkWidget *widget;
252   GtkTextBuffer *buffer;
253   GtkTextIter pos;
254   GtkTextIter start, end;
255
256   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
257   if (widget == NULL)
258     return NULL;
259
260   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
261   gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset);
262   _gtk_text_buffer_get_text_before (buffer, boundary_type,
263                                     &pos, &start, &end);
264   *start_offset = gtk_text_iter_get_offset (&start);
265   *end_offset = gtk_text_iter_get_offset (&end);
266   return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
267 }
268
269 static gunichar
270 gtk_text_view_accessible_get_character_at_offset (AtkText *text,
271                                                   gint     offset)
272 {
273   GtkWidget *widget;
274   GtkTextIter start, end;
275   GtkTextBuffer *buffer;
276   gchar *string;
277   gunichar unichar;
278
279   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
280   if (widget == NULL)
281     return '\0';
282
283   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
284   if (offset >= gtk_text_buffer_get_char_count (buffer))
285     return '\0';
286
287   gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
288   end = start;
289   gtk_text_iter_forward_char (&end);
290   string = gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
291   unichar = g_utf8_get_char (string);
292   g_free (string);
293
294   return unichar;
295 }
296
297 static gint
298 gtk_text_view_accessible_get_character_count (AtkText *text)
299 {
300   GtkWidget *widget;
301   GtkTextBuffer *buffer;
302
303   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
304   if (widget == NULL)
305     return 0;
306
307   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
308   return gtk_text_buffer_get_char_count (buffer);
309 }
310
311 static gint
312 get_insert_offset (GtkTextBuffer *buffer)
313 {
314   GtkTextMark *insert;
315   GtkTextIter iter;
316
317   insert = gtk_text_buffer_get_insert (buffer);
318   gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
319   return gtk_text_iter_get_offset (&iter);
320 }
321
322 static gint
323 gtk_text_view_accessible_get_caret_offset (AtkText *text)
324 {
325   GtkWidget *widget;
326   GtkTextBuffer *buffer;
327
328   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
329   if (widget == NULL)
330     return 0;
331
332   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
333   return get_insert_offset (buffer);
334 }
335
336 static gboolean
337 gtk_text_view_accessible_set_caret_offset (AtkText *text,
338                                            gint     offset)
339 {
340   GtkTextView *view;
341   GtkWidget *widget;
342   GtkTextBuffer *buffer;
343   GtkTextIter iter;
344
345   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
346   if (widget == NULL)
347     return FALSE;
348
349   view = GTK_TEXT_VIEW (widget);
350   buffer = gtk_text_view_get_buffer (view);
351
352   gtk_text_buffer_get_iter_at_offset (buffer,  &iter, offset);
353   gtk_text_buffer_place_cursor (buffer, &iter);
354   gtk_text_view_scroll_to_iter (view, &iter, 0, FALSE, 0, 0);
355
356   return TRUE;
357 }
358
359 static gint
360 gtk_text_view_accessible_get_offset_at_point (AtkText      *text,
361                                               gint          x,
362                                               gint          y,
363                                               AtkCoordType  coords)
364 {
365   GtkTextView *view;
366   GtkTextIter iter;
367   gint x_widget, y_widget, x_window, y_window, buff_x, buff_y;
368   GtkWidget *widget;
369   GdkWindow *window;
370   GdkRectangle rect;
371
372   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
373   if (widget == NULL)
374     return -1;
375
376   view = GTK_TEXT_VIEW (widget);
377   window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_WIDGET);
378   gdk_window_get_origin (window, &x_widget, &y_widget);
379
380   if (coords == ATK_XY_SCREEN)
381     {
382       x = x - x_widget;
383       y = y - y_widget;
384     }
385   else if (coords == ATK_XY_WINDOW)
386     {
387       window = gdk_window_get_toplevel (window);
388       gdk_window_get_origin (window, &x_window, &y_window);
389
390       x = x - x_widget + x_window;
391       y = y - y_widget + y_window;
392     }
393   else
394     return -1;
395
396   gtk_text_view_window_to_buffer_coords (view, GTK_TEXT_WINDOW_WIDGET,
397                                          x, y, &buff_x, &buff_y);
398   gtk_text_view_get_visible_rect (view, &rect);
399
400   /* Clamp point to visible rectangle */
401   buff_x = CLAMP (buff_x, rect.x, rect.x + rect.width - 1);
402   buff_y = CLAMP (buff_y, rect.y, rect.y + rect.height - 1);
403
404   gtk_text_view_get_iter_at_location (view, &iter, buff_x, buff_y);
405
406   /* The iter at a location sometimes points to the next character.
407    * See bug 111031. We work around that
408    */
409   gtk_text_view_get_iter_location (view, &iter, &rect);
410   if (buff_x < rect.x)
411     gtk_text_iter_backward_char (&iter);
412   return gtk_text_iter_get_offset (&iter);
413 }
414
415 static void
416 gtk_text_view_accessible_get_character_extents (AtkText      *text,
417                                                 gint          offset,
418                                                 gint         *x,
419                                                 gint         *y,
420                                                 gint         *width,
421                                                 gint         *height,
422                                                 AtkCoordType  coords)
423 {
424   GtkTextView *view;
425   GtkTextBuffer *buffer;
426   GtkTextIter iter;
427   GtkWidget *widget;
428   GdkRectangle rectangle;
429   GdkWindow *window;
430   gint x_widget, y_widget, x_window, y_window;
431
432   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
433   if (widget == NULL)
434     return;
435
436   view = GTK_TEXT_VIEW (widget);
437   buffer = gtk_text_view_get_buffer (view);
438   gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
439   gtk_text_view_get_iter_location (view, &iter, &rectangle);
440
441   window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_WIDGET);
442   gdk_window_get_origin (window, &x_widget, &y_widget);
443
444   *height = rectangle.height;
445   *width = rectangle.width;
446
447   gtk_text_view_buffer_to_window_coords (view, GTK_TEXT_WINDOW_WIDGET,
448     rectangle.x, rectangle.y, x, y);
449   if (coords == ATK_XY_WINDOW)
450     {
451       window = gdk_window_get_toplevel (window);
452       gdk_window_get_origin (window, &x_window, &y_window);
453       *x += x_widget - x_window;
454       *y += y_widget - y_window;
455     }
456   else if (coords == ATK_XY_SCREEN)
457     {
458       *x += x_widget;
459       *y += y_widget;
460     }
461   else
462     {
463       *x = 0;
464       *y = 0;
465       *height = 0;
466       *width = 0;
467     }
468 }
469
470 static AtkAttributeSet *
471 gtk_text_view_accessible_get_run_attributes (AtkText *text,
472                                              gint     offset,
473                                              gint    *start_offset,
474                                              gint    *end_offset)
475 {
476   GtkTextView *view;
477   GtkTextBuffer *buffer;
478   GtkWidget *widget;
479
480   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
481   if (widget == NULL)
482     return NULL;
483
484   view = GTK_TEXT_VIEW (widget);
485   buffer = gtk_text_view_get_buffer (view);
486
487   return gail_misc_buffer_get_run_attributes (buffer, offset,
488                                               start_offset, end_offset);
489 }
490
491 static AtkAttributeSet *
492 add_text_attribute (AtkAttributeSet  *attributes,
493                     AtkTextAttribute  attr,
494                     gint              i)
495 {
496   AtkAttribute *at;
497
498   at = g_new (AtkAttribute, 1);
499
500   at->name = g_strdup (atk_text_attribute_get_name (attr));
501   at->value = g_strdup (atk_text_attribute_get_value (attr, i));
502
503   return g_slist_prepend (attributes, at);
504 }
505
506 static AtkAttributeSet *
507 gtk_text_view_accessible_get_default_attributes (AtkText *text)
508 {
509   GtkTextView *view;
510   GtkWidget *widget;
511   GtkTextAttributes *text_attrs;
512   AtkAttributeSet *attributes;
513   PangoFontDescription *font;
514   gchar *value;
515
516   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
517   if (widget == NULL)
518     return NULL;
519
520   view = GTK_TEXT_VIEW (widget);
521   text_attrs = gtk_text_view_get_default_attributes (view);
522
523   attributes = NULL;
524
525   font = text_attrs->font;
526
527   if (font)
528     {
529       attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_STYLE,
530                                        pango_font_description_get_style (font));
531
532       attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
533                                        pango_font_description_get_variant (font));
534
535       attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
536                                        pango_font_description_get_stretch (font));
537
538       value = g_strdup (pango_font_description_get_family (font));
539       attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME, value);
540
541       value = g_strdup_printf ("%d", pango_font_description_get_weight (font));
542       attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, value);
543
544       value = g_strdup_printf ("%i", pango_font_description_get_size (font) / PANGO_SCALE);
545       attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_SIZE, value);
546     }
547
548   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_JUSTIFICATION, text_attrs->justification);
549   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION, text_attrs->direction);
550   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_WRAP_MODE, text_attrs->wrap_mode);
551   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_EDITABLE, text_attrs->editable);
552   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_INVISIBLE, text_attrs->invisible);
553   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_BG_FULL_HEIGHT, text_attrs->bg_full_height);
554
555   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
556                                    text_attrs->appearance.strikethrough);
557   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
558                                    text_attrs->appearance.underline);
559
560   value = g_strdup_printf ("%u,%u,%u",
561                            text_attrs->appearance.bg_color.red,
562                            text_attrs->appearance.bg_color.green,
563                            text_attrs->appearance.bg_color.blue);
564   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
565
566   value = g_strdup_printf ("%u,%u,%u",
567                            text_attrs->appearance.fg_color.red,
568                            text_attrs->appearance.fg_color.green,
569                            text_attrs->appearance.fg_color.blue);
570   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
571
572   value = g_strdup_printf ("%g", text_attrs->font_scale);
573   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_SCALE, value);
574
575   value = g_strdup ((gchar *)(text_attrs->language));
576   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE, value);
577
578   value = g_strdup_printf ("%i", text_attrs->appearance.rise);
579   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_RISE, value);
580
581   value = g_strdup_printf ("%i", text_attrs->pixels_inside_wrap);
582   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, value);
583
584   value = g_strdup_printf ("%i", text_attrs->pixels_below_lines);
585   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, value);
586
587   value = g_strdup_printf ("%i", text_attrs->pixels_above_lines);
588   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, value);
589
590   value = g_strdup_printf ("%i", text_attrs->indent);
591   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_INDENT, value);
592
593   value = g_strdup_printf ("%i", text_attrs->left_margin);
594   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_LEFT_MARGIN, value);
595
596   value = g_strdup_printf ("%i", text_attrs->right_margin);
597   attributes = gail_misc_add_attribute (attributes, ATK_TEXT_ATTR_RIGHT_MARGIN, value);
598
599   gtk_text_attributes_unref (text_attrs);
600   return attributes;
601 }
602
603 static gint
604 gtk_text_view_accessible_get_n_selections (AtkText *text)
605 {
606   GtkWidget *widget;
607   GtkTextBuffer *buffer;
608
609   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
610   if (widget == NULL)
611     return 0;
612
613   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
614   if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
615     return 1;
616
617   return 0;
618 }
619
620 static gchar *
621 gtk_text_view_accessible_get_selection (AtkText *atk_text,
622                                         gint     selection_num,
623                                         gint    *start_pos,
624                                         gint    *end_pos)
625 {
626   GtkTextView *view;
627   GtkWidget *widget;
628   GtkTextBuffer *buffer;
629   GtkTextIter start, end;
630   gchar *text;
631
632   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
633   if (widget == NULL)
634     return NULL;
635
636   if (selection_num != 0)
637     return NULL;
638
639   view = GTK_TEXT_VIEW (widget);
640   buffer = gtk_text_view_get_buffer (view);
641
642   if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
643     text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
644   else
645     text = NULL;
646
647   *start_pos = gtk_text_iter_get_offset (&start);
648   *end_pos = gtk_text_iter_get_offset (&end);
649
650   return text;
651 }
652
653 static gboolean
654 gtk_text_view_accessible_add_selection (AtkText *text,
655                                         gint     start_pos,
656                                         gint     end_pos)
657 {
658   GtkWidget *widget;
659   GtkTextBuffer *buffer;
660   GtkTextIter start, end;
661
662   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
663   if (widget == NULL)
664     return FALSE;
665
666   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
667
668   if (!gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
669     {
670       gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
671       gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
672       gtk_text_buffer_select_range (buffer, &end, &start);
673
674       return TRUE;
675     }
676   else
677     return FALSE;
678 }
679
680 static gboolean
681 gtk_text_view_accessible_remove_selection (AtkText *text,
682                                            gint     selection_num)
683 {
684   GtkWidget *widget;
685   GtkTextBuffer *buffer;
686   GtkTextMark *insert;
687   GtkTextIter iter;
688   GtkTextIter start, end;
689
690   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
691   if (widget == NULL)
692     return FALSE;
693
694   if (selection_num != 0)
695      return FALSE;
696
697   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
698
699   if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
700     {
701       insert = gtk_text_buffer_get_insert (buffer);
702       gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
703       gtk_text_buffer_place_cursor (buffer, &iter);
704       return TRUE;
705     }
706   else
707     return FALSE;
708 }
709
710 static gboolean
711 gtk_text_view_accessible_set_selection (AtkText *text,
712                                         gint     selection_num,
713                                         gint     start_pos,
714                                         gint     end_pos)
715 {
716   GtkWidget *widget;
717   GtkTextBuffer *buffer;
718   GtkTextIter start, end;
719
720   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
721   if (widget == NULL)
722     return FALSE;
723
724   if (selection_num != 0)
725     return FALSE;
726
727   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
728
729   if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
730     {
731       gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
732       gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
733       gtk_text_buffer_select_range (buffer, &end, &start);
734
735       return TRUE;
736     }
737   else
738     return FALSE;
739 }
740
741 static void
742 atk_text_interface_init (AtkTextIface *iface)
743 {
744   iface->get_text = gtk_text_view_accessible_get_text;
745   iface->get_text_after_offset = gtk_text_view_accessible_get_text_after_offset;
746   iface->get_text_at_offset = gtk_text_view_accessible_get_text_at_offset;
747   iface->get_text_before_offset = gtk_text_view_accessible_get_text_before_offset;
748   iface->get_character_at_offset = gtk_text_view_accessible_get_character_at_offset;
749   iface->get_character_count = gtk_text_view_accessible_get_character_count;
750   iface->get_caret_offset = gtk_text_view_accessible_get_caret_offset;
751   iface->set_caret_offset = gtk_text_view_accessible_set_caret_offset;
752   iface->get_offset_at_point = gtk_text_view_accessible_get_offset_at_point;
753   iface->get_character_extents = gtk_text_view_accessible_get_character_extents;
754   iface->get_n_selections = gtk_text_view_accessible_get_n_selections;
755   iface->get_selection = gtk_text_view_accessible_get_selection;
756   iface->add_selection = gtk_text_view_accessible_add_selection;
757   iface->remove_selection = gtk_text_view_accessible_remove_selection;
758   iface->set_selection = gtk_text_view_accessible_set_selection;
759   iface->get_run_attributes = gtk_text_view_accessible_get_run_attributes;
760   iface->get_default_attributes = gtk_text_view_accessible_get_default_attributes;
761 }
762
763 /* atkeditabletext.h */
764
765 static gboolean
766 gtk_text_view_accessible_set_run_attributes (AtkEditableText *text,
767                                              AtkAttributeSet *attributes,
768                                              gint             start_offset,
769                                              gint             end_offset)
770 {
771   GtkTextView *view;
772   GtkTextBuffer *buffer;
773   GtkWidget *widget;
774   GtkTextTag *tag;
775   GtkTextIter start;
776   GtkTextIter end;
777   gint j;
778   GdkColor *color;
779   gchar** RGB_vals;
780   GSList *l;
781
782   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
783   if (widget == NULL)
784     return FALSE;
785
786   view = GTK_TEXT_VIEW (widget);
787   if (!gtk_text_view_get_editable (view))
788     return FALSE;
789
790   buffer = gtk_text_view_get_buffer (view);
791
792   if (attributes == NULL)
793     return FALSE;
794
795   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
796   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
797
798   tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
799
800   for (l = attributes; l; l = l->next)
801     {
802       gchar *name;
803       gchar *value;
804       AtkAttribute *at;
805
806       at = l->data;
807
808       name = at->name;
809       value = at->value;
810
811       if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_LEFT_MARGIN)))
812         g_object_set (G_OBJECT (tag), "left_margin", atoi (value), NULL);
813
814       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_RIGHT_MARGIN)))
815         g_object_set (G_OBJECT (tag), "right_margin", atoi (value), NULL);
816
817       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_INDENT)))
818         g_object_set (G_OBJECT (tag), "indent", atoi (value), NULL);
819
820       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_ABOVE_LINES)))
821         g_object_set (G_OBJECT (tag), "pixels_above_lines", atoi (value), NULL);
822
823       else if (!strcmp(name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_BELOW_LINES)))
824         g_object_set (G_OBJECT (tag), "pixels_below_lines", atoi (value), NULL);
825
826       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP)))
827         g_object_set (G_OBJECT (tag), "pixels_inside_wrap", atoi (value), NULL);
828
829       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_SIZE)))
830         g_object_set (G_OBJECT (tag), "size", atoi (value), NULL);
831
832       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_RISE)))
833         g_object_set (G_OBJECT (tag), "rise", atoi (value), NULL);
834
835       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_WEIGHT)))
836         g_object_set (G_OBJECT (tag), "weight", atoi (value), NULL);
837
838       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_BG_FULL_HEIGHT)))
839         {
840           g_object_set (G_OBJECT (tag), "bg_full_height",
841                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_BG_FULL_HEIGHT, 0))),
842                    NULL);
843         }
844
845       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_LANGUAGE)))
846         g_object_set (G_OBJECT (tag), "language", value, NULL);
847
848       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_FAMILY_NAME)))
849         g_object_set (G_OBJECT (tag), "family", value, NULL);
850
851       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_EDITABLE)))
852         {
853           g_object_set (G_OBJECT (tag), "editable",
854                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0))),
855                    NULL);
856         }
857
858       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_INVISIBLE)))
859         {
860           g_object_set (G_OBJECT (tag), "invisible",
861                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0))),
862                    NULL);
863         }
864
865       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_UNDERLINE)))
866         {
867           for (j = 0; j < 3; j++)
868             {
869               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, j)))
870                 {
871                   g_object_set (G_OBJECT (tag), "underline", j, NULL);
872                   break;
873                 }
874             }
875         }
876
877       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STRIKETHROUGH)))
878         {
879           g_object_set (G_OBJECT (tag), "strikethrough",
880                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 0))),
881                    NULL);
882         }
883
884       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_BG_COLOR)))
885         {
886           RGB_vals = g_strsplit (value, ",", 3);
887           color = g_malloc (sizeof (GdkColor));
888           color->red = atoi (RGB_vals[0]);
889           color->green = atoi (RGB_vals[1]);
890           color->blue = atoi (RGB_vals[2]);
891           g_object_set (G_OBJECT (tag), "background_gdk", color, NULL);
892         }
893  
894       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_FG_COLOR)))
895         {
896           RGB_vals = g_strsplit (value, ",", 3);
897           color = g_malloc (sizeof (GdkColor));
898           color->red = atoi (RGB_vals[0]);
899           color->green = atoi (RGB_vals[1]);
900           color->blue = atoi (RGB_vals[2]);
901           g_object_set (G_OBJECT (tag), "foreground_gdk", color, NULL);
902         }
903
904       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STRETCH)))
905         {
906           for (j = 0; j < 9; j++)
907             {
908               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, j)))
909                 {
910                   g_object_set (G_OBJECT (tag), "stretch", j, NULL);
911                   break;
912                 }
913             }
914         }
915
916       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_JUSTIFICATION)))
917         {
918           for (j = 0; j < 4; j++)
919             {
920               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, j)))
921                 {
922                   g_object_set (G_OBJECT (tag), "justification", j, NULL);
923                   break;
924                 }
925             }
926         }
927
928       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_DIRECTION)))
929         {
930           for (j = 0; j < 3; j++)
931             {
932               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, j)))
933                 {
934                   g_object_set (G_OBJECT (tag), "direction", j, NULL);
935                   break;
936                 }
937             }
938         }
939
940       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_VARIANT)))
941         {
942           for (j = 0; j < 2; j++)
943             {
944               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, j)))
945                 {
946                   g_object_set (G_OBJECT (tag), "variant", j, NULL);
947                   break;
948                 }
949             }
950         }
951
952       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_WRAP_MODE)))
953         {
954           for (j = 0; j < 3; j++)
955             {
956               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, j)))
957                 {
958                   g_object_set (G_OBJECT (tag), "wrap_mode", j, NULL);
959                   break;
960                 }
961             }
962         }
963
964       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STYLE)))
965         {
966           for (j = 0; j < 3; j++)
967             {
968               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, j)))
969                 {
970                   g_object_set (G_OBJECT (tag), "style", j, NULL);
971                   break;
972               }
973             }
974         }
975
976       else
977         return FALSE;
978     }
979
980   gtk_text_buffer_apply_tag (buffer, tag, &start, &end);
981
982   return TRUE;
983 }
984
985 static void
986 gtk_text_view_accessible_set_text_contents (AtkEditableText *text,
987                                             const gchar     *string)
988 {
989   GtkTextView *view;
990   GtkWidget *widget;
991   GtkTextBuffer *buffer;
992
993   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
994   if (widget == NULL)
995     return;
996
997   view = GTK_TEXT_VIEW (widget);
998   if (!gtk_text_view_get_editable (view))
999     return;
1000
1001   buffer = gtk_text_view_get_buffer (view);
1002   gtk_text_buffer_set_text (buffer, string, -1);
1003 }
1004
1005 static void
1006 gtk_text_view_accessible_insert_text (AtkEditableText *text,
1007                                       const gchar     *string,
1008                                       gint             length,
1009                                       gint            *position)
1010 {
1011   GtkTextView *view;
1012   GtkWidget *widget;
1013   GtkTextBuffer *buffer;
1014   GtkTextIter iter;
1015
1016   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1017   if (widget == NULL)
1018     return;
1019
1020   view = GTK_TEXT_VIEW (widget);
1021   if (!gtk_text_view_get_editable (view))
1022     return;
1023
1024   buffer = gtk_text_view_get_buffer (view);
1025   gtk_text_buffer_get_iter_at_offset (buffer, &iter, *position);
1026   gtk_text_buffer_insert (buffer, &iter, string, length);
1027 }
1028
1029 static void
1030 gtk_text_view_accessible_copy_text (AtkEditableText *text,
1031                                     gint             start_pos,
1032                                     gint             end_pos)
1033 {
1034   GtkWidget *widget;
1035   GtkTextBuffer *buffer;
1036   GtkTextIter start, end;
1037   gchar *str;
1038   GtkClipboard *clipboard;
1039
1040   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1041   if (widget == NULL)
1042     return;
1043
1044   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1045
1046   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
1047   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
1048   str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1049
1050   clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
1051   gtk_clipboard_set_text (clipboard, str, -1);
1052 }
1053
1054 static void
1055 gtk_text_view_accessible_cut_text (AtkEditableText *text,
1056                                    gint             start_pos,
1057                                    gint             end_pos)
1058 {
1059   GtkTextView *view;
1060   GtkWidget *widget;
1061   GtkTextBuffer *buffer;
1062   GtkTextIter start, end;
1063   gchar *str;
1064   GtkClipboard *clipboard;
1065
1066   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1067   if (widget == NULL)
1068     return;
1069
1070   view = GTK_TEXT_VIEW (widget);
1071   if (!gtk_text_view_get_editable (view))
1072     return;
1073   buffer = gtk_text_view_get_buffer (view);
1074
1075   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
1076   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
1077   str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1078   clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
1079   gtk_clipboard_set_text (clipboard, str, -1);
1080   gtk_text_buffer_delete (buffer, &start, &end);
1081 }
1082
1083 static void
1084 gtk_text_view_accessible_delete_text (AtkEditableText *text,
1085                                       gint             start_pos,
1086                                       gint             end_pos)
1087 {
1088   GtkTextView *view;
1089   GtkWidget *widget;
1090   GtkTextBuffer *buffer;
1091   GtkTextIter start_itr;
1092   GtkTextIter end_itr;
1093
1094   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1095   if (widget == NULL)
1096     return;
1097
1098   view = GTK_TEXT_VIEW (widget);
1099   if (!gtk_text_view_get_editable (view))
1100     return;
1101   buffer = gtk_text_view_get_buffer (view);
1102
1103   gtk_text_buffer_get_iter_at_offset (buffer, &start_itr, start_pos);
1104   gtk_text_buffer_get_iter_at_offset (buffer, &end_itr, end_pos);
1105   gtk_text_buffer_delete (buffer, &start_itr, &end_itr);
1106 }
1107
1108 typedef struct
1109 {
1110   GtkTextBuffer* buffer;
1111   gint position;
1112 } PasteData;
1113
1114 static void
1115 paste_received (GtkClipboard *clipboard,
1116                 const gchar  *text,
1117                 gpointer      data)
1118 {
1119   PasteData* paste = data;
1120   GtkTextIter pos_itr;
1121
1122   if (text)
1123     {
1124       gtk_text_buffer_get_iter_at_offset (paste->buffer, &pos_itr, paste->position);
1125       gtk_text_buffer_insert (paste->buffer, &pos_itr, text, -1);
1126     }
1127
1128   g_object_unref (paste->buffer);
1129 }
1130
1131 static void
1132 gtk_text_view_accessible_paste_text (AtkEditableText *text,
1133                                      gint             position)
1134 {
1135   GtkTextView *view;
1136   GtkWidget *widget;
1137   GtkTextBuffer *buffer;
1138   PasteData paste;
1139   GtkClipboard *clipboard;
1140
1141   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1142   if (widget == NULL)
1143     return;
1144
1145   view = GTK_TEXT_VIEW (widget);
1146   if (!gtk_text_view_get_editable (view))
1147     return;
1148   buffer = gtk_text_view_get_buffer (view);
1149
1150   paste.buffer = buffer;
1151   paste.position = position;
1152
1153   g_object_ref (paste.buffer);
1154   clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
1155   gtk_clipboard_request_text (clipboard, paste_received, &paste);
1156 }
1157
1158 static void
1159 atk_editable_text_interface_init (AtkEditableTextIface *iface)
1160 {
1161   iface->set_text_contents = gtk_text_view_accessible_set_text_contents;
1162   iface->insert_text = gtk_text_view_accessible_insert_text;
1163   iface->copy_text = gtk_text_view_accessible_copy_text;
1164   iface->cut_text = gtk_text_view_accessible_cut_text;
1165   iface->delete_text = gtk_text_view_accessible_delete_text;
1166   iface->paste_text = gtk_text_view_accessible_paste_text;
1167   iface->set_run_attributes = gtk_text_view_accessible_set_run_attributes;
1168 }
1169
1170 /* Callbacks */
1171
1172 static void
1173 insert_text_cb (GtkTextBuffer *buffer,
1174                 GtkTextIter   *iter,
1175                 gchar         *text,
1176                 gint           len,
1177                 gpointer       data)
1178 {
1179   GtkTextView *view = data;
1180   GtkTextViewAccessible *accessible;
1181   gint position;
1182   gint length;
1183
1184   accessible = GTK_TEXT_VIEW_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (view)));
1185
1186   accessible->signal_name = "text_changed::insert";
1187   position = gtk_text_iter_get_offset (iter);
1188   length = g_utf8_strlen (text, len);
1189  
1190   if (accessible->length == 0)
1191     {
1192       accessible->position = position;
1193       accessible->length = length;
1194     }
1195   else if (accessible->position + accessible->length == position)
1196     {
1197       accessible->length += length;
1198     }
1199   else
1200     {
1201       /*
1202        * We have a non-contiguous insert so report what we have
1203        */
1204       if (accessible->insert_notify_handler)
1205         {
1206           g_source_remove (accessible->insert_notify_handler);
1207         }
1208       accessible->insert_notify_handler = 0;
1209       insert_idle_handler (accessible);
1210       accessible->position = position;
1211       accessible->length = length;
1212     }
1213 }
1214
1215 static void
1216 delete_range_cb (GtkTextBuffer *buffer,
1217                  GtkTextIter   *start,
1218                  GtkTextIter   *end,
1219                  gpointer       data)
1220 {
1221   GtkTextView *text = data;
1222   GtkTextViewAccessible *accessible;
1223   gint offset;
1224   gint length;
1225
1226
1227   offset = gtk_text_iter_get_offset (start);
1228   length = gtk_text_iter_get_offset (end) - offset;
1229
1230   accessible = GTK_TEXT_VIEW_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (text)));
1231   if (accessible->insert_notify_handler)
1232     {
1233       g_source_remove (accessible->insert_notify_handler);
1234       accessible->insert_notify_handler = 0;
1235       if (accessible->position == offset &&
1236           accessible->length == length)
1237         {
1238         /*
1239          * Do not bother with insert and delete notifications
1240          */
1241           accessible->signal_name = NULL;
1242           accessible->position = 0;
1243           accessible->length = 0;
1244           return;
1245         }
1246
1247       insert_idle_handler (accessible);
1248     }
1249   g_signal_emit_by_name (accessible, "text_changed::delete", offset, length);
1250 }
1251
1252 static gint
1253 get_selection_bound (GtkTextBuffer *buffer)
1254 {
1255   GtkTextMark *bound;
1256   GtkTextIter iter;
1257
1258   bound = gtk_text_buffer_get_selection_bound (buffer);
1259   gtk_text_buffer_get_iter_at_mark (buffer, &iter, bound);
1260   return gtk_text_iter_get_offset (&iter);
1261 }
1262
1263 static void
1264 emit_text_caret_moved (GtkTextViewAccessible *accessible,
1265                        gint                   offset)
1266 {
1267   /*
1268    * If we have text which has been inserted notify the user
1269    */
1270   if (accessible->insert_notify_handler)
1271     {
1272       g_source_remove (accessible->insert_notify_handler);
1273       accessible->insert_notify_handler = 0;
1274       insert_idle_handler (accessible);
1275     }
1276
1277   if (offset != accessible->previous_insert_offset)
1278     {
1279       /*
1280        * If the caret position has not changed then don't bother notifying
1281        *
1282        * When mouse click is used to change caret position, notification
1283        * is received on button down and button up.
1284        */
1285       g_signal_emit_by_name (accessible, "text_caret_moved", offset);
1286       accessible->previous_insert_offset = offset;
1287     }
1288 }
1289
1290 static void
1291 mark_set_cb (GtkTextBuffer *buffer,
1292              GtkTextIter   *location,
1293              GtkTextMark   *mark,
1294              gpointer       data)
1295 {
1296   GtkTextView *text = data;
1297   GtkTextViewAccessible *accessible;
1298   const char *mark_name;
1299
1300   mark_name = gtk_text_mark_get_name (mark);
1301
1302   accessible = GTK_TEXT_VIEW_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (text)));
1303
1304   /*
1305    * Only generate the signal for the "insert" mark, which
1306    * represents the cursor.
1307    */
1308   if (g_strcmp0 (mark_name, "insert") == 0)
1309     {
1310       gint insert_offset, selection_bound;
1311       gboolean selection_changed;
1312
1313       insert_offset = gtk_text_iter_get_offset (location);
1314
1315       selection_bound = get_selection_bound (buffer);
1316       if (selection_bound != insert_offset)
1317         {
1318           if (selection_bound != accessible->previous_selection_bound ||
1319               insert_offset != accessible->previous_insert_offset)
1320             selection_changed = TRUE;
1321           else
1322             selection_changed = FALSE;
1323         }
1324       else if (accessible->previous_selection_bound != accessible->previous_insert_offset)
1325         selection_changed = TRUE;
1326       else
1327         selection_changed = FALSE;
1328
1329       emit_text_caret_moved (accessible, insert_offset);
1330       /*
1331        * insert and selection_bound marks are different to a selection
1332        * has changed
1333        */
1334       if (selection_changed)
1335         g_signal_emit_by_name (accessible, "text_selection_changed");
1336       accessible->previous_selection_bound = selection_bound;
1337     }
1338 }
1339
1340 static void
1341 changed_cb (GtkTextBuffer *buffer,
1342             gpointer       data)
1343 {
1344   GtkTextView *text = data;
1345   GtkTextViewAccessible *accessible;
1346
1347   accessible = GTK_TEXT_VIEW_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (text)));
1348   if (accessible->signal_name)
1349     {
1350       if (!accessible->insert_notify_handler)
1351         {
1352           accessible->insert_notify_handler = gdk_threads_add_idle (insert_idle_handler, accessible);
1353         }
1354       return;
1355     }
1356   emit_text_caret_moved (accessible, get_insert_offset (buffer));
1357   accessible->previous_selection_bound = get_selection_bound (buffer);
1358 }
1359
1360 static gint
1361 insert_idle_handler (gpointer data)
1362 {
1363   GtkTextViewAccessible *accessible = data;
1364   GtkWidget *widget;
1365   GtkTextBuffer *buffer;
1366
1367   g_signal_emit_by_name (data,
1368                          accessible->signal_name,
1369                          accessible->position,
1370                          accessible->length);
1371   accessible->signal_name = NULL;
1372   accessible->position = 0;
1373   accessible->length = 0;
1374
1375   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
1376   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1377   if (accessible->insert_notify_handler)
1378     {
1379     /*
1380      * If called from idle handler notify caret moved
1381      */
1382       accessible->insert_notify_handler = 0;
1383       emit_text_caret_moved (accessible, get_insert_offset (buffer));
1384       accessible->previous_selection_bound = get_selection_bound (buffer);
1385     }
1386
1387   return FALSE;
1388 }
1389
1390 static gint
1391 gail_streamable_content_get_n_mime_types (AtkStreamableContent *streamable)
1392 {
1393   GtkWidget *widget;
1394   GtkTextBuffer *buffer;
1395   gint n_mime_types = 0;
1396
1397   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (streamable));
1398   if (widget == NULL)
1399     return 0;
1400
1401   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1402   if (buffer)
1403     {
1404       gint i;
1405       gboolean advertises_plaintext = FALSE;
1406       GdkAtom *atoms;
1407
1408       atoms = gtk_text_buffer_get_serialize_formats (buffer, &n_mime_types);
1409       for (i = 0; i < n_mime_types-1; ++i)
1410         if (!strcmp ("text/plain", gdk_atom_name (atoms[i])))
1411             advertises_plaintext = TRUE;
1412       if (!advertises_plaintext)
1413         n_mime_types++;
1414     }
1415
1416   return n_mime_types;
1417 }
1418
1419 static const gchar *
1420 gail_streamable_content_get_mime_type (AtkStreamableContent *streamable,
1421                                        gint                  i)
1422 {
1423   GtkWidget *widget;
1424   GtkTextBuffer *buffer;
1425
1426   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (streamable));
1427   if (widget == NULL)
1428     return 0;
1429
1430   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1431   if (buffer)
1432     {
1433       gint n_mime_types = 0;
1434       GdkAtom *atoms;
1435
1436       atoms = gtk_text_buffer_get_serialize_formats (buffer, &n_mime_types);
1437       if (i < n_mime_types)
1438         return gdk_atom_name (atoms [i]);
1439       else if (i == n_mime_types)
1440         return "text/plain";
1441     }
1442
1443   return NULL;
1444 }
1445
1446 static GIOChannel *
1447 gail_streamable_content_get_stream (AtkStreamableContent *streamable,
1448                                     const gchar          *mime_type)
1449 {
1450   GtkWidget *widget;
1451   GtkTextBuffer *buffer;
1452   gint i, n_mime_types = 0;
1453   GdkAtom *atoms;
1454
1455   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (streamable));
1456   if (widget == NULL)
1457     return 0;
1458
1459   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
1460   if (!buffer)
1461     return NULL;
1462
1463   atoms = gtk_text_buffer_get_serialize_formats (buffer, &n_mime_types);
1464
1465   for (i = 0; i < n_mime_types; ++i)
1466     {
1467       if (!strcmp ("text/plain", mime_type) ||
1468           !strcmp (gdk_atom_name (atoms[i]), mime_type))
1469         {
1470           guint8 *cbuf;
1471           GError *err = NULL;
1472           gsize len, written;
1473           gchar tname[80];
1474           GtkTextIter start, end;
1475           GIOChannel *gio = NULL;
1476           int fd;
1477
1478           gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
1479           gtk_text_buffer_get_iter_at_offset (buffer, &end, -1);
1480           if (!strcmp ("text/plain", mime_type))
1481             {
1482               cbuf = (guint8*) gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1483               len = strlen ((const char *) cbuf);
1484             }
1485           else
1486             {
1487               cbuf = gtk_text_buffer_serialize (buffer, buffer, atoms[i], &start, &end, &len);
1488             }
1489           g_snprintf (tname, 20, "streamXXXXXX");
1490           fd = g_mkstemp (tname);
1491           gio = g_io_channel_unix_new (fd);
1492           g_io_channel_set_encoding (gio, NULL, &err);
1493           if (!err)
1494             g_io_channel_write_chars (gio, (const char *) cbuf, (gssize) len, &written, &err);
1495           else
1496             g_message ("%s", err->message);
1497           if (!err)
1498             g_io_channel_seek_position (gio, 0, G_SEEK_SET, &err);
1499           else
1500             g_message ("%s", err->message);
1501           if (!err)
1502             g_io_channel_flush (gio, &err);
1503           else
1504             g_message ("%s", err->message);
1505           if (err)
1506             {
1507               g_message ("<error writing to stream [%s]>", tname);
1508               g_error_free (err);
1509             }
1510           /* make sure the file is removed on unref of the giochannel */
1511           else
1512             {
1513               g_unlink (tname);
1514               return gio;
1515             }
1516         }
1517     }
1518
1519   return NULL;
1520 }
1521
1522 static void
1523 atk_streamable_content_interface_init (AtkStreamableContentIface *iface)
1524 {
1525   iface->get_n_mime_types = gail_streamable_content_get_n_mime_types;
1526   iface->get_mime_type = gail_streamable_content_get_mime_type;
1527   iface->get_stream = gail_streamable_content_get_stream;
1528 }