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