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