]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailtextview.c
a383db5c98da8ff7e8bfd386224705e0bbba8aeb
[~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 (accessible)->widget;
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 (text)->widget;
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 gail_text_view_get_default_attributes (AtkText *text)
645 {
646   GtkTextView *view;
647   GtkWidget *widget;
648   GtkTextAttributes *text_attrs;
649   AtkAttributeSet *attrib_set = NULL;
650   PangoFontDescription *font;
651
652   widget = GTK_ACCESSIBLE (text)->widget;
653   if (widget == NULL)
654     /* State is defunct */
655     return NULL;
656
657   view = GTK_TEXT_VIEW (widget);
658   text_attrs = gtk_text_view_get_default_attributes (view);
659
660   font = text_attrs->font;
661
662   if (font)
663     {
664       attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
665                                               ATK_TEXT_ATTR_STYLE);
666
667       attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
668                                               ATK_TEXT_ATTR_VARIANT);
669
670       attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
671                                               ATK_TEXT_ATTR_STRETCH);
672     }
673
674   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
675                                           ATK_TEXT_ATTR_JUSTIFICATION);
676
677   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
678                                           ATK_TEXT_ATTR_DIRECTION);
679
680   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
681                                           ATK_TEXT_ATTR_WRAP_MODE);
682
683   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
684                                           ATK_TEXT_ATTR_FG_STIPPLE);
685
686   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
687                                           ATK_TEXT_ATTR_BG_STIPPLE);
688
689   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
690                                           ATK_TEXT_ATTR_FG_COLOR);
691
692   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
693                                           ATK_TEXT_ATTR_BG_COLOR);
694
695   if (font)
696     {
697       attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
698                                               ATK_TEXT_ATTR_FAMILY_NAME);
699     }
700
701   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
702                                           ATK_TEXT_ATTR_LANGUAGE);
703
704   if (font)
705     {
706       attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
707                                               ATK_TEXT_ATTR_WEIGHT);
708     }
709
710   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
711                                           ATK_TEXT_ATTR_SCALE);
712
713   if (font)
714     {
715       attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
716                                               ATK_TEXT_ATTR_SIZE);
717     }
718
719   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
720                                           ATK_TEXT_ATTR_STRIKETHROUGH);
721
722   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
723                                           ATK_TEXT_ATTR_UNDERLINE);
724
725   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
726                                           ATK_TEXT_ATTR_RISE);
727
728   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
729                                           ATK_TEXT_ATTR_BG_FULL_HEIGHT);
730
731   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
732                                           ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP);
733
734   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
735                                          ATK_TEXT_ATTR_PIXELS_BELOW_LINES);
736
737   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
738                                           ATK_TEXT_ATTR_PIXELS_ABOVE_LINES);
739
740   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
741                                           ATK_TEXT_ATTR_EDITABLE);
742     
743   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
744                                           ATK_TEXT_ATTR_INVISIBLE);
745
746   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
747                                           ATK_TEXT_ATTR_INDENT);
748
749   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
750                                           ATK_TEXT_ATTR_RIGHT_MARGIN);
751
752   attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, 
753                                           ATK_TEXT_ATTR_LEFT_MARGIN);
754
755   gtk_text_attributes_unref (text_attrs);
756   return attrib_set;
757 }
758
759 static gint
760 gail_text_view_get_n_selections (AtkText *text)
761 {
762   GtkTextView *view;
763   GtkWidget *widget;
764   GtkTextBuffer *buffer;
765   GtkTextIter start, end;
766   gint select_start, select_end;
767
768   widget = GTK_ACCESSIBLE (text)->widget;
769   if (widget == NULL)
770     /* State is defunct */
771     return -1;
772
773   view = GTK_TEXT_VIEW (widget);
774   buffer = gtk_text_view_get_buffer (view);
775
776   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
777   select_start = gtk_text_iter_get_offset (&start);
778   select_end = gtk_text_iter_get_offset (&end);
779
780   if (select_start != select_end)
781      return 1;
782   else
783      return 0;
784 }
785
786 static gchar*
787 gail_text_view_get_selection (AtkText *text,
788                               gint    selection_num,
789                               gint    *start_pos,
790                               gint    *end_pos)
791 {
792   GtkTextView *view;
793   GtkWidget *widget;
794   GtkTextBuffer *buffer;
795   GtkTextIter start, end;
796
797   widget = GTK_ACCESSIBLE (text)->widget;
798   if (widget == NULL)
799     /* State is defunct */
800     return NULL;
801
802  /* Only let the user get the selection if one is set, and if the
803   * selection_num is 0.
804   */
805   if (selection_num != 0)
806      return NULL;
807
808   view = GTK_TEXT_VIEW (widget);
809   buffer = gtk_text_view_get_buffer (view);
810
811   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
812   *start_pos = gtk_text_iter_get_offset (&start);
813   *end_pos = gtk_text_iter_get_offset (&end);
814
815   if (*start_pos != *end_pos)
816     return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
817   else
818     return NULL;
819 }
820
821 static gboolean
822 gail_text_view_add_selection (AtkText *text,
823                               gint    start_pos,
824                               gint    end_pos)
825 {
826   GtkTextView *view;
827   GtkWidget *widget;
828   GtkTextBuffer *buffer;
829   GtkTextIter pos_itr;
830   GtkTextIter start, end;
831   gint select_start, select_end;
832
833   widget = GTK_ACCESSIBLE (text)->widget;
834   if (widget == NULL)
835     /* State is defunct */
836     return FALSE;
837
838   view = GTK_TEXT_VIEW (widget);
839   buffer = gtk_text_view_get_buffer (view);
840
841   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
842   select_start = gtk_text_iter_get_offset (&start);
843   select_end = gtk_text_iter_get_offset (&end);
844
845  /* If there is already a selection, then don't allow another to be added,
846   * since GtkTextView only supports one selected region.
847   */
848   if (select_start == select_end)
849     {
850       gtk_text_buffer_get_iter_at_offset (buffer,  &pos_itr, start_pos);
851       gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &pos_itr);
852       gtk_text_buffer_get_iter_at_offset (buffer,  &pos_itr, end_pos);
853       gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
854       return TRUE;
855     }
856   else
857     return FALSE;
858 }
859
860 static gboolean
861 gail_text_view_remove_selection (AtkText *text,
862                                  gint    selection_num)
863 {
864   GtkTextView *view;
865   GtkWidget *widget;
866   GtkTextBuffer *buffer;
867   GtkTextMark *cursor_mark;
868   GtkTextIter cursor_itr;
869   GtkTextIter start, end;
870   gint select_start, select_end;
871
872   widget = GTK_ACCESSIBLE (text)->widget;
873   if (widget == NULL)
874     /* State is defunct */
875     return FALSE;
876
877   if (selection_num != 0)
878      return FALSE;
879
880   view = GTK_TEXT_VIEW (widget);
881   buffer = gtk_text_view_get_buffer (view);
882
883   gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
884   select_start = gtk_text_iter_get_offset(&start);
885   select_end = gtk_text_iter_get_offset(&end);
886
887   if (select_start != select_end)
888     {
889      /* Setting the start & end of the selected region to the caret position
890       * turns off the selection.
891       */
892       cursor_mark = gtk_text_buffer_get_insert (buffer);
893       gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark);
894       gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &cursor_itr);
895       return TRUE;
896     }
897   else
898     return FALSE;
899 }
900
901 static gboolean
902 gail_text_view_set_selection (AtkText *text,
903                               gint    selection_num,
904                               gint    start_pos,
905                               gint    end_pos)
906 {
907   GtkTextView *view;
908   GtkWidget *widget;
909   GtkTextBuffer *buffer;
910   GtkTextIter pos_itr;
911   GtkTextIter start, end;
912   gint select_start, select_end;
913
914   widget = GTK_ACCESSIBLE (text)->widget;
915   if (widget == NULL)
916   {
917     /* State is defunct */
918     return FALSE;
919   }
920
921  /* Only let the user move the selection if one is set, and if the
922   * selection_num is 0
923   */
924   if (selection_num != 0)
925      return FALSE;
926
927   view = GTK_TEXT_VIEW (widget);
928   buffer = gtk_text_view_get_buffer (view);
929
930   gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
931   select_start = gtk_text_iter_get_offset(&start);
932   select_end = gtk_text_iter_get_offset(&end);
933
934   if (select_start != select_end)
935     {
936       gtk_text_buffer_get_iter_at_offset (buffer,  &pos_itr, start_pos);
937       gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &pos_itr);
938       gtk_text_buffer_get_iter_at_offset (buffer,  &pos_itr, end_pos);
939       gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
940       return TRUE;
941     }
942   else
943     return FALSE;
944 }
945
946 /* atkeditabletext.h */
947
948 static void
949 atk_editable_text_interface_init (AtkEditableTextIface *iface)
950 {
951   iface->set_text_contents = gail_text_view_set_text_contents;
952   iface->insert_text = gail_text_view_insert_text;
953   iface->copy_text = gail_text_view_copy_text;
954   iface->cut_text = gail_text_view_cut_text;
955   iface->delete_text = gail_text_view_delete_text;
956   iface->paste_text = gail_text_view_paste_text;
957   iface->set_run_attributes = gail_text_view_set_run_attributes;
958 }
959
960 static gboolean
961 gail_text_view_set_run_attributes (AtkEditableText *text,
962                                    AtkAttributeSet *attrib_set,
963                                    gint            start_offset,
964                                    gint            end_offset)
965 {
966   GtkTextView *view;
967   GtkTextBuffer *buffer;
968   GtkWidget *widget;
969   GtkTextTag *tag;
970   GtkTextIter start;
971   GtkTextIter end;
972   gint j;
973   GdkColor *color;
974   gchar** RGB_vals;
975   GSList *l;
976
977   widget = GTK_ACCESSIBLE (text)->widget;
978   if (widget == NULL)
979     /* State is defunct */
980     return FALSE;
981
982   view = GTK_TEXT_VIEW (widget);
983   if (!gtk_text_view_get_editable (view))
984     return FALSE;
985
986   buffer = gtk_text_view_get_buffer (view);
987
988   if (attrib_set == NULL)
989     return FALSE;
990
991   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
992   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
993
994   tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
995
996   for (l = attrib_set; l; l = l->next)
997     {
998       gchar *name;
999       gchar *value;
1000       AtkAttribute *at;
1001
1002       at = l->data;
1003
1004       name = at->name;
1005       value = at->value;
1006
1007       if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_LEFT_MARGIN)))
1008         g_object_set (G_OBJECT (tag), "left_margin", atoi (value), NULL);
1009
1010       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_RIGHT_MARGIN)))
1011         g_object_set (G_OBJECT (tag), "right_margin", atoi (value), NULL);
1012
1013       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_INDENT)))
1014         g_object_set (G_OBJECT (tag), "indent", atoi (value), NULL);
1015
1016       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_ABOVE_LINES)))
1017         g_object_set (G_OBJECT (tag), "pixels_above_lines", atoi (value), NULL);
1018
1019       else if (!strcmp(name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_BELOW_LINES)))
1020         g_object_set (G_OBJECT (tag), "pixels_below_lines", atoi (value), NULL);
1021
1022       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP)))
1023         g_object_set (G_OBJECT (tag), "pixels_inside_wrap", atoi (value), NULL);
1024
1025       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_SIZE)))
1026         g_object_set (G_OBJECT (tag), "size", atoi (value), NULL);
1027
1028       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_RISE)))
1029         g_object_set (G_OBJECT (tag), "rise", atoi (value), NULL);
1030
1031       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_WEIGHT)))
1032         g_object_set (G_OBJECT (tag), "weight", atoi (value), NULL);
1033
1034       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_BG_FULL_HEIGHT)))
1035         {
1036           g_object_set (G_OBJECT (tag), "bg_full_height", 
1037                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_BG_FULL_HEIGHT, 0))),
1038                    NULL);
1039         }
1040
1041       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_LANGUAGE)))
1042         g_object_set (G_OBJECT (tag), "language", value, NULL);
1043
1044       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_FAMILY_NAME)))
1045         g_object_set (G_OBJECT (tag), "family", value, NULL);
1046
1047       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_EDITABLE)))
1048         {
1049           g_object_set (G_OBJECT (tag), "editable", 
1050                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0))),
1051                    NULL);
1052         }
1053
1054       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_INVISIBLE)))
1055         {
1056           g_object_set (G_OBJECT (tag), "invisible", 
1057                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0))),
1058                    NULL);
1059         }
1060
1061       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_UNDERLINE)))
1062         {
1063           for (j = 0; j < 3; j++)
1064             {
1065               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, j)))
1066                 {
1067                   g_object_set (G_OBJECT (tag), "underline", j, NULL);
1068                   break;
1069                 }
1070             } 
1071         }
1072
1073       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STRIKETHROUGH)))
1074         {
1075           g_object_set (G_OBJECT (tag), "strikethrough", 
1076                    (strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 0))),
1077                    NULL);
1078         }
1079
1080       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_BG_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), "background_gdk", color, NULL);
1088         }
1089   
1090       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_FG_COLOR)))
1091         {
1092           RGB_vals = g_strsplit (value, ",", 3);
1093           color = g_malloc (sizeof (GdkColor));
1094           color->red = atoi (RGB_vals[0]);
1095           color->green = atoi (RGB_vals[1]);
1096           color->blue = atoi (RGB_vals[2]);
1097           g_object_set (G_OBJECT (tag), "foreground_gdk", color, NULL);
1098         }
1099
1100       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STRETCH)))
1101         {
1102           for (j = 0; j < 9; j++)
1103             {
1104               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, j)))
1105                 {
1106                   g_object_set (G_OBJECT (tag), "stretch", j, NULL);
1107                   break;
1108                 }
1109             }
1110         }
1111
1112       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_JUSTIFICATION)))
1113         {
1114           for (j = 0; j < 4; j++)
1115             {
1116               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, j)))
1117                 {
1118                   g_object_set (G_OBJECT (tag), "justification", j, NULL);
1119                   break;
1120                 }
1121             }
1122         }
1123
1124       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_DIRECTION)))
1125         {
1126           for (j = 0; j < 3; j++)
1127             {
1128               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, j)))
1129                 {
1130                   g_object_set (G_OBJECT (tag), "direction", j, NULL);
1131                   break;
1132                 }
1133             }
1134         }
1135
1136       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_VARIANT)))
1137         {
1138           for (j = 0; j < 2; j++)
1139             {
1140               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, j)))
1141                 {
1142                   g_object_set (G_OBJECT (tag), "variant", j, NULL);
1143                   break;
1144                 }
1145             }
1146         }
1147
1148       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_WRAP_MODE)))
1149         {
1150           for (j = 0; j < 3; j++)
1151             {
1152               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, j)))
1153                 {
1154                   g_object_set (G_OBJECT (tag), "wrap_mode", j, NULL);
1155                   break;
1156                 }
1157             }
1158         }
1159
1160       else if (!strcmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STYLE)))
1161         {
1162           for (j = 0; j < 3; j++)
1163             {
1164               if (!strcmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, j)))
1165                 {
1166                   g_object_set (G_OBJECT (tag), "style", j, NULL);
1167                   break;
1168               }
1169             }
1170         }
1171
1172       else
1173         return FALSE;
1174     }
1175
1176   gtk_text_buffer_apply_tag (buffer, tag, &start, &end);
1177
1178   return TRUE;
1179 }
1180
1181 static void
1182 gail_text_view_set_text_contents (AtkEditableText *text,
1183                                   const gchar     *string)
1184 {
1185   GtkTextView *view;
1186   GtkWidget *widget;
1187   GtkTextBuffer *buffer;
1188
1189   widget = GTK_ACCESSIBLE (text)->widget;
1190   if (widget == NULL)
1191     /* State is defunct */
1192     return;
1193
1194   view = GTK_TEXT_VIEW (widget);
1195   if (!gtk_text_view_get_editable (view))
1196     return;
1197   buffer = gtk_text_view_get_buffer (view);
1198
1199   /* The -1 indicates that the input string must be null-terminated */
1200   gtk_text_buffer_set_text (buffer, string, -1);
1201 }
1202
1203 static void
1204 gail_text_view_insert_text (AtkEditableText *text,
1205                             const gchar     *string,
1206                             gint            length,
1207                             gint            *position)
1208 {
1209   GtkTextView *view;
1210   GtkWidget *widget;
1211   GtkTextBuffer *buffer;
1212   GtkTextIter pos_itr;
1213
1214   widget = GTK_ACCESSIBLE (text)->widget;
1215   if (widget == NULL)
1216     /* State is defunct */
1217     return;
1218
1219   view = GTK_TEXT_VIEW (widget);
1220   if (!gtk_text_view_get_editable (view))
1221     return;
1222   buffer = gtk_text_view_get_buffer (view);
1223
1224   gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, *position);
1225   gtk_text_buffer_insert (buffer, &pos_itr, string, length);
1226 }
1227
1228 static void
1229 gail_text_view_copy_text   (AtkEditableText *text,
1230                             gint            start_pos,
1231                             gint            end_pos)
1232 {
1233   GtkTextView *view;
1234   GtkWidget *widget;
1235   GtkTextBuffer *buffer;
1236   GtkTextIter start, end;
1237   gchar *str;
1238   GtkClipboard *clipboard;
1239
1240   widget = GTK_ACCESSIBLE (text)->widget;
1241   if (widget == NULL)
1242     /* State is defunct */
1243     return;
1244
1245   view = GTK_TEXT_VIEW (widget);
1246   buffer = gtk_text_view_get_buffer (view);
1247
1248   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
1249   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
1250   str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1251   clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (widget),
1252                                              GDK_SELECTION_CLIPBOARD);
1253   gtk_clipboard_set_text (clipboard, str, -1);
1254 }
1255
1256 static void
1257 gail_text_view_cut_text (AtkEditableText *text,
1258                          gint            start_pos,
1259                          gint            end_pos)
1260 {
1261   GtkTextView *view;
1262   GtkWidget *widget;
1263   GtkTextBuffer *buffer;
1264   GtkTextIter start, end;
1265   gchar *str;
1266   GtkClipboard *clipboard;
1267
1268   widget = GTK_ACCESSIBLE (text)->widget;
1269   if (widget == NULL)
1270     /* State is defunct */
1271     return;
1272
1273   view = GTK_TEXT_VIEW (widget);
1274   if (!gtk_text_view_get_editable (view))
1275     return;
1276   buffer = gtk_text_view_get_buffer (view);
1277
1278   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos);
1279   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos);
1280   str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1281   clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (widget),
1282                                              GDK_SELECTION_CLIPBOARD);
1283   gtk_clipboard_set_text (clipboard, str, -1);
1284   gtk_text_buffer_delete (buffer, &start, &end);
1285 }
1286
1287 static void
1288 gail_text_view_delete_text (AtkEditableText *text,
1289                             gint            start_pos,
1290                             gint            end_pos)
1291 {
1292   GtkTextView *view;
1293   GtkWidget *widget;
1294   GtkTextBuffer *buffer;
1295   GtkTextIter start_itr;
1296   GtkTextIter end_itr;
1297
1298   widget = GTK_ACCESSIBLE (text)->widget;
1299   if (widget == NULL)
1300     /* State is defunct */
1301     return;
1302
1303   view = GTK_TEXT_VIEW (widget);
1304   if (!gtk_text_view_get_editable (view))
1305     return;
1306   buffer = gtk_text_view_get_buffer (view);
1307
1308   gtk_text_buffer_get_iter_at_offset (buffer, &start_itr, start_pos);
1309   gtk_text_buffer_get_iter_at_offset (buffer, &end_itr, end_pos);
1310   gtk_text_buffer_delete (buffer, &start_itr, &end_itr);
1311 }
1312
1313 static void
1314 gail_text_view_paste_text (AtkEditableText *text,
1315                            gint            position)
1316 {
1317   GtkTextView *view;
1318   GtkWidget *widget;
1319   GtkTextBuffer *buffer;
1320   GailTextViewPaste paste_struct;
1321   GtkClipboard *clipboard;
1322
1323   widget = GTK_ACCESSIBLE (text)->widget;
1324   if (widget == NULL)
1325     /* State is defunct */
1326     return;
1327
1328   view = GTK_TEXT_VIEW (widget);
1329   if (!gtk_text_view_get_editable (view))
1330     return;
1331   buffer = gtk_text_view_get_buffer (view);
1332
1333   paste_struct.buffer = buffer;
1334   paste_struct.position = position;
1335
1336   g_object_ref (paste_struct.buffer);
1337   clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (widget),
1338                                              GDK_SELECTION_CLIPBOARD);
1339   gtk_clipboard_request_text (clipboard,
1340     gail_text_view_paste_received, &paste_struct);
1341 }
1342
1343 static void
1344 gail_text_view_paste_received (GtkClipboard *clipboard,
1345                                const gchar  *text,
1346                                gpointer     data)
1347 {
1348   GailTextViewPaste* paste_struct = (GailTextViewPaste *)data;
1349   GtkTextIter pos_itr;
1350
1351   if (text)
1352     {
1353       gtk_text_buffer_get_iter_at_offset (paste_struct->buffer, &pos_itr,
1354          paste_struct->position);
1355       gtk_text_buffer_insert (paste_struct->buffer, &pos_itr, text, -1);
1356     }
1357
1358   g_object_unref (paste_struct->buffer);
1359 }
1360
1361 /* Callbacks */
1362
1363 /* Note arg1 returns the start of the insert range, arg3 returns the
1364  * end of the insert range if multiple characters are inserted.  If one
1365  * character is inserted they have the same value, which is the caret
1366  * location.  arg2 returns the begin location of the insert.
1367  */
1368 static void 
1369 _gail_text_view_insert_text_cb (GtkTextBuffer *buffer,
1370                                 GtkTextIter   *arg1, 
1371                                 gchar         *arg2, 
1372                                 gint          arg3,
1373                                 gpointer      user_data)
1374 {
1375   GtkTextView *text = (GtkTextView *) user_data;
1376   AtkObject *accessible;
1377   GailTextView *gail_text_view;
1378   gint position;
1379   gint length;
1380
1381   g_return_if_fail (arg3 > 0);
1382
1383   accessible = gtk_widget_get_accessible(GTK_WIDGET(text));
1384   gail_text_view = GAIL_TEXT_VIEW (accessible);
1385
1386   gail_text_view->signal_name = "text_changed::insert";
1387   position = gtk_text_iter_get_offset (arg1);
1388   length = g_utf8_strlen(arg2, arg3);
1389   
1390   if (gail_text_view->length == 0)
1391     {
1392       gail_text_view->position = position;
1393       gail_text_view->length = length;
1394     }
1395   else if (gail_text_view->position + gail_text_view->length == position)
1396     {
1397       gail_text_view->length += length;
1398     }
1399   else
1400     {
1401       /*
1402        * We have a non-contiguous insert so report what we have
1403        */
1404       if (gail_text_view->insert_notify_handler)
1405         {
1406           g_source_remove (gail_text_view->insert_notify_handler);
1407         }
1408       gail_text_view->insert_notify_handler = 0;
1409       insert_idle_handler (gail_text_view);
1410       gail_text_view->position = position;
1411       gail_text_view->length = length;
1412     }
1413     
1414   /*
1415    * The signal will be emitted when the changed signal is received
1416    */
1417 }
1418
1419 /* Note arg1 returns the start of the delete range, arg2 returns the
1420  * end of the delete range if multiple characters are deleted.  If one
1421  * character is deleted they have the same value, which is the caret
1422  * location.
1423  */
1424 static void 
1425 _gail_text_view_delete_range_cb (GtkTextBuffer *buffer,
1426                                  GtkTextIter   *arg1, 
1427                                  GtkTextIter   *arg2,
1428                                  gpointer      user_data)
1429 {
1430   GtkTextView *text = (GtkTextView *) user_data;
1431   AtkObject *accessible;
1432   GailTextView *gail_text_view;
1433   gint offset = gtk_text_iter_get_offset (arg1);
1434   gint length = gtk_text_iter_get_offset (arg2) - offset;
1435
1436   accessible = gtk_widget_get_accessible(GTK_WIDGET(text));
1437   gail_text_view = GAIL_TEXT_VIEW (accessible);
1438   if (gail_text_view->insert_notify_handler)
1439     {
1440       g_source_remove (gail_text_view->insert_notify_handler);
1441       gail_text_view->insert_notify_handler = 0;
1442       if (gail_text_view->position == offset && 
1443           gail_text_view->length == length)
1444         {
1445         /*
1446          * Do not bother with insert and delete notifications
1447          */
1448           gail_text_view->signal_name = NULL;
1449           gail_text_view->position = 0;
1450           gail_text_view->length = 0;
1451           return;
1452         }
1453
1454       insert_idle_handler (gail_text_view);
1455     }
1456   g_signal_emit_by_name (accessible, "text_changed::delete",
1457                          offset, length);
1458 }
1459
1460 /* Note arg1 and arg2 point to the same offset, which is the caret
1461  * position after the move
1462  */
1463 static void 
1464 _gail_text_view_mark_set_cb (GtkTextBuffer *buffer,
1465                              GtkTextIter   *arg1, 
1466                              GtkTextMark   *arg2,
1467                              gpointer      user_data)
1468 {
1469   GtkTextView *text = (GtkTextView *) user_data;
1470   AtkObject *accessible;
1471   GailTextView *gail_text_view;
1472   const char *mark_name = gtk_text_mark_get_name(arg2);
1473
1474   accessible = gtk_widget_get_accessible(GTK_WIDGET(text));
1475   gail_text_view = GAIL_TEXT_VIEW (accessible);
1476
1477   /*
1478    * Only generate the signal for the "insert" mark, which
1479    * represents the cursor.
1480    */
1481   if (mark_name && !strcmp(mark_name, "insert"))
1482     {
1483       int insert_offset, selection_bound;
1484       gboolean selection_changed;
1485
1486       insert_offset = gtk_text_iter_get_offset (arg1);
1487
1488       selection_bound = get_selection_bound (buffer);
1489       if (selection_bound != insert_offset)
1490         {
1491           if (selection_bound != gail_text_view->previous_selection_bound ||
1492               insert_offset != gail_text_view->previous_insert_offset)
1493             {
1494               selection_changed = TRUE;
1495             }
1496           else
1497             {
1498               selection_changed = FALSE;
1499             }
1500         }
1501       else if (gail_text_view->previous_selection_bound != gail_text_view->previous_insert_offset)
1502         {
1503           selection_changed = TRUE;
1504         }
1505       else
1506         {
1507           selection_changed = FALSE;
1508         }
1509
1510       emit_text_caret_moved (gail_text_view, insert_offset);
1511       /*
1512        * insert and selection_bound marks are different to a selection
1513        * has changed
1514        */
1515       if (selection_changed)
1516         g_signal_emit_by_name (accessible, "text_selection_changed");
1517       gail_text_view->previous_selection_bound = selection_bound;
1518     }
1519 }
1520
1521 static void 
1522 _gail_text_view_changed_cb (GtkTextBuffer *buffer,
1523                             gpointer      user_data)
1524 {
1525   GtkTextView *text = (GtkTextView *) user_data;
1526   AtkObject *accessible;
1527   GailTextView *gail_text_view;
1528
1529   accessible = gtk_widget_get_accessible (GTK_WIDGET (text));
1530   gail_text_view = GAIL_TEXT_VIEW (accessible);
1531   if (gail_text_view->signal_name)
1532     {
1533       if (!gail_text_view->insert_notify_handler)
1534         {
1535           gail_text_view->insert_notify_handler = gdk_threads_add_idle (insert_idle_handler, accessible);
1536         }
1537       return;
1538     }
1539   emit_text_caret_moved (gail_text_view, get_insert_offset (buffer));
1540   gail_text_view->previous_selection_bound = get_selection_bound (buffer);
1541 }
1542
1543 static gchar*
1544 get_text_near_offset (AtkText          *text,
1545                       GailOffsetType   function,
1546                       AtkTextBoundary  boundary_type,
1547                       gint             offset,
1548                       gint             *start_offset,
1549                       gint             *end_offset)
1550 {
1551   GtkTextView *view;
1552   gpointer layout = NULL;
1553
1554   view = GTK_TEXT_VIEW (GTK_ACCESSIBLE (text)->widget);
1555
1556   /*
1557    * Pass the GtkTextView to the function gail_text_util_get_text() 
1558    * so it can find the start and end of the current line on the display.
1559    */
1560   if (boundary_type == ATK_TEXT_BOUNDARY_LINE_START ||
1561       boundary_type == ATK_TEXT_BOUNDARY_LINE_END)
1562     layout = view;
1563
1564   return gail_text_util_get_text (GAIL_TEXT_VIEW (text)->textutil, layout,
1565                                   function, boundary_type, offset, 
1566                                     start_offset, end_offset);
1567 }
1568
1569 static gint
1570 get_insert_offset (GtkTextBuffer *buffer)
1571 {
1572   GtkTextMark *cursor_mark;
1573   GtkTextIter cursor_itr;
1574
1575   cursor_mark = gtk_text_buffer_get_insert (buffer);
1576   gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark);
1577   return gtk_text_iter_get_offset (&cursor_itr);
1578 }
1579
1580 static gint
1581 get_selection_bound (GtkTextBuffer *buffer)
1582 {
1583   GtkTextMark *selection_mark;
1584   GtkTextIter selection_itr;
1585
1586   selection_mark = gtk_text_buffer_get_selection_bound (buffer);
1587   gtk_text_buffer_get_iter_at_mark (buffer, &selection_itr, selection_mark);
1588   return gtk_text_iter_get_offset (&selection_itr);
1589 }
1590
1591 static void
1592 emit_text_caret_moved (GailTextView *gail_text_view,
1593                        gint          insert_offset)
1594 {
1595   /*
1596    * If we have text which has been inserted notify the user
1597    */
1598   if (gail_text_view->insert_notify_handler)
1599     {
1600       g_source_remove (gail_text_view->insert_notify_handler);
1601       gail_text_view->insert_notify_handler = 0;
1602       insert_idle_handler (gail_text_view);
1603     }
1604
1605   if (insert_offset != gail_text_view->previous_insert_offset)
1606     {
1607       /*
1608        * If the caret position has not changed then don't bother notifying
1609        *
1610        * When mouse click is used to change caret position, notification
1611        * is received on button down and button up.
1612        */
1613       g_signal_emit_by_name (gail_text_view, "text_caret_moved", insert_offset);
1614       gail_text_view->previous_insert_offset = insert_offset;
1615     }
1616 }
1617
1618 static gint
1619 insert_idle_handler (gpointer data)
1620 {
1621   GailTextView *gail_text_view;
1622   GtkTextBuffer *buffer;
1623
1624   gail_text_view = GAIL_TEXT_VIEW (data);
1625
1626   g_signal_emit_by_name (data,
1627                          gail_text_view->signal_name,
1628                          gail_text_view->position,
1629                          gail_text_view->length);
1630   gail_text_view->signal_name = NULL;
1631   gail_text_view->position = 0;
1632   gail_text_view->length = 0;
1633
1634   buffer = gail_text_view->textutil->buffer;
1635   if (gail_text_view->insert_notify_handler)
1636     {
1637     /*
1638      * If called from idle handler notify caret moved
1639      */
1640       gail_text_view->insert_notify_handler = 0;
1641       emit_text_caret_moved (gail_text_view, get_insert_offset (buffer));
1642       gail_text_view->previous_selection_bound = get_selection_bound (buffer);
1643     }
1644
1645   return FALSE;
1646 }
1647
1648 static void       
1649 atk_streamable_content_interface_init    (AtkStreamableContentIface *iface)
1650 {
1651   iface->get_n_mime_types = gail_streamable_content_get_n_mime_types;
1652   iface->get_mime_type = gail_streamable_content_get_mime_type;
1653   iface->get_stream = gail_streamable_content_get_stream;
1654 }
1655
1656 static gint       gail_streamable_content_get_n_mime_types (AtkStreamableContent *streamable)
1657 {
1658     gint n_mime_types = 0;
1659
1660     if (GAIL_IS_TEXT_VIEW (streamable) && GAIL_TEXT_VIEW (streamable)->textutil)
1661     {
1662         int i;
1663         gboolean advertises_plaintext = FALSE;
1664         GdkAtom *atoms =
1665              gtk_text_buffer_get_serialize_formats (
1666                 GAIL_TEXT_VIEW (streamable)->textutil->buffer, 
1667                 &n_mime_types);
1668         for (i = 0; i < n_mime_types-1; ++i)
1669             if (!strcmp ("text/plain", gdk_atom_name (atoms[i])))
1670                 advertises_plaintext = TRUE;
1671         if (!advertises_plaintext) ++n_mime_types; 
1672         /* we support text/plain even if the GtkTextBuffer doesn't */
1673     }
1674     return n_mime_types;
1675 }
1676
1677 static G_CONST_RETURN gchar*       
1678 gail_streamable_content_get_mime_type (AtkStreamableContent *streamable, gint i)
1679 {
1680     if (GAIL_IS_TEXT_VIEW (streamable) && GAIL_TEXT_VIEW (streamable)->textutil)
1681     {
1682         gint n_mime_types = 0;
1683         GdkAtom *atoms;
1684         atoms = gtk_text_buffer_get_serialize_formats (
1685             GAIL_TEXT_VIEW (streamable)->textutil->buffer, 
1686             &n_mime_types);
1687         if (i < n_mime_types)
1688         {
1689             return gdk_atom_name (atoms [i]);
1690         }
1691         else if (i == n_mime_types)
1692             return "text/plain";
1693     }
1694     return NULL;
1695 }
1696
1697 static GIOChannel*       gail_streamable_content_get_stream       (AtkStreamableContent *streamable,
1698                                                                    const gchar *mime_type)
1699 {
1700     gint i, n_mime_types = 0;
1701     GdkAtom *atoms;
1702     if (!GAIL_IS_TEXT_VIEW (streamable) || !GAIL_TEXT_VIEW (streamable)->textutil)
1703         return NULL;
1704     atoms = gtk_text_buffer_get_serialize_formats (
1705         GAIL_TEXT_VIEW (streamable)->textutil->buffer, 
1706         &n_mime_types);
1707     for (i = 0; i < n_mime_types; ++i) 
1708     {
1709         if (!strcmp ("text/plain", mime_type) ||
1710             !strcmp (gdk_atom_name (atoms[i]), mime_type)) {
1711             GtkTextBuffer *buffer;
1712             guint8 *cbuf;
1713             GError *err = NULL;
1714             gsize len, written;
1715             gchar tname[80];
1716             GtkTextIter start, end;
1717             GIOChannel *gio = NULL;
1718             int fd;
1719             buffer = GAIL_TEXT_VIEW (streamable)->textutil->buffer;
1720             gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
1721             gtk_text_buffer_get_iter_at_offset (buffer, &end, -1);
1722             if (!strcmp ("text/plain", mime_type)) 
1723             {
1724                 cbuf = (guint8*) gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
1725                 len = strlen ((const char *) cbuf); 
1726             }
1727             else
1728             {
1729                 cbuf = gtk_text_buffer_serialize (buffer, buffer, atoms[i], &start, &end, &len);
1730             }
1731             g_snprintf (tname, 20, "streamXXXXXX");
1732             fd = g_mkstemp (tname);
1733             gio = g_io_channel_unix_new (fd);
1734             g_io_channel_set_encoding (gio, NULL, &err);
1735             if (!err) g_io_channel_write_chars (gio, (const char *) cbuf, (gssize) len, &written, &err);
1736             else g_message ("%s", err->message);
1737             if (!err) g_io_channel_seek_position (gio, 0, G_SEEK_SET, &err);
1738             else g_message ("%s", err->message);
1739             if (!err) g_io_channel_flush (gio, &err);
1740             else g_message ("%s", err->message);
1741             if (err) {
1742                 g_message ("<error writing to stream [%s]>", tname);
1743                 g_error_free (err);
1744             }
1745             /* make sure the file is removed on unref of the giochannel */
1746             else {
1747                 g_unlink (tname);
1748                 return gio; 
1749             }
1750         }
1751     }
1752     return NULL;
1753 }
1754