source: trunk/third/gail/gail/gailtextview.c @ 20902

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