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