]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gailentry.c
1a24de9282f2aeb49bf3326672566cf495e44874
[~andy/gtk] / modules / other / gail / gailentry.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 <string.h>
23 #include <gtk/gtk.h>
24 #include <gdk/gdkkeysyms.h>
25 #include "gailentry.h"
26 #include "gailcombobox.h"
27 #include <libgail-util/gailmisc.h>
28
29 static void       gail_entry_class_init            (GailEntryClass       *klass);
30 static void       gail_entry_init                  (GailEntry            *entry);
31 static void       gail_entry_real_initialize       (AtkObject            *obj,
32                                                     gpointer             data);
33 static void       text_setup                       (GailEntry            *entry,
34                                                     GtkEntry             *gtk_entry);
35 static void       gail_entry_real_notify_gtk       (GObject              *obj,
36                                                     GParamSpec           *pspec);
37 static void       gail_entry_finalize              (GObject              *object);
38
39 static gint       gail_entry_get_index_in_parent   (AtkObject            *accessible);
40
41 /* atkobject.h */
42
43 static AtkStateSet*     gail_entry_ref_state_set   (AtkObject            *accessible);
44 static AtkAttributeSet* gail_entry_get_attributes  (AtkObject            *accessible);
45
46 /* atktext.h */
47
48 static void       atk_text_interface_init          (AtkTextIface         *iface);
49
50 static gchar*     gail_entry_get_text              (AtkText              *text,
51                                                     gint                 start_pos,
52                                                     gint                 end_pos);
53 static gunichar   gail_entry_get_character_at_offset
54                                                    (AtkText              *text,
55                                                     gint                 offset);
56 static gchar*     gail_entry_get_text_before_offset(AtkText              *text,
57                                                     gint                 offset,
58                                                     AtkTextBoundary      boundary_type,
59                                                     gint                 *start_offset,
60                                                     gint                 *end_offset);
61 static gchar*     gail_entry_get_text_at_offset    (AtkText              *text,
62                                                     gint                 offset,
63                                                     AtkTextBoundary      boundary_type,
64                                                     gint                 *start_offset,
65                                                     gint                 *end_offset);
66 static gchar*     gail_entry_get_text_after_offset (AtkText              *text,
67                                                     gint                 offset,
68                                                     AtkTextBoundary      boundary_type,
69                                                     gint                 *start_offset,
70                                                     gint                 *end_offset);
71 static gint       gail_entry_get_caret_offset      (AtkText              *text);
72 static gboolean   gail_entry_set_caret_offset      (AtkText              *text,
73                                                     gint                 offset);
74 static gint       gail_entry_get_n_selections      (AtkText              *text);
75 static gchar*     gail_entry_get_selection         (AtkText              *text,
76                                                     gint                 selection_num,
77                                                     gint                 *start_offset,
78                                                     gint                 *end_offset);
79 static gboolean   gail_entry_add_selection         (AtkText              *text,
80                                                     gint                 start_offset,
81                                                     gint                 end_offset);
82 static gboolean   gail_entry_remove_selection      (AtkText              *text,
83                                                     gint                 selection_num);
84 static gboolean   gail_entry_set_selection         (AtkText              *text,
85                                                     gint                 selection_num,
86                                                     gint                 start_offset,
87                                                     gint                 end_offset);
88 static gint       gail_entry_get_character_count   (AtkText              *text);
89 static AtkAttributeSet *  gail_entry_get_run_attributes 
90                                                    (AtkText              *text,
91                                                     gint                 offset,
92                                                     gint                 *start_offset,
93                                                     gint                 *end_offset);
94 static AtkAttributeSet *  gail_entry_get_default_attributes 
95                                                    (AtkText              *text);
96 static void gail_entry_get_character_extents       (AtkText              *text,
97                                                     gint                 offset,
98                                                     gint                 *x,
99                                                     gint                 *y,
100                                                     gint                 *width,
101                                                     gint                 *height,
102                                                     AtkCoordType         coords);
103 static gint gail_entry_get_offset_at_point         (AtkText              *text,
104                                                     gint                 x,
105                                                     gint                 y,
106                                                     AtkCoordType         coords);
107 /* atkeditabletext.h */
108
109 static void       atk_editable_text_interface_init (AtkEditableTextIface *iface);
110 static void       gail_entry_set_text_contents     (AtkEditableText      *text,
111                                                     const gchar          *string);
112 static void       gail_entry_insert_text           (AtkEditableText      *text,
113                                                     const gchar          *string,
114                                                     gint                 length,
115                                                     gint                 *position);
116 static void       gail_entry_copy_text             (AtkEditableText      *text,
117                                                     gint                 start_pos,
118                                                     gint                 end_pos);
119 static void       gail_entry_cut_text              (AtkEditableText      *text,
120                                                     gint                 start_pos,
121                                                     gint                 end_pos);
122 static void       gail_entry_delete_text           (AtkEditableText      *text,
123                                                     gint                 start_pos,
124                                                     gint                 end_pos);
125 static void       gail_entry_paste_text            (AtkEditableText      *text,
126                                                     gint                 position);
127 static void       gail_entry_paste_received        (GtkClipboard *clipboard,
128                                                     const gchar  *text,
129                                                     gpointer     data);
130
131
132 /* Callbacks */
133
134 static gboolean   gail_entry_idle_notify_insert    (gpointer data);
135 static void       gail_entry_notify_insert         (GailEntry            *entry);
136 static void       gail_entry_notify_delete         (GailEntry            *entry);
137 static void       _gail_entry_insert_text_cb       (GtkEntry             *entry,
138                                                     gchar                *arg1,
139                                                     gint                 arg2,
140                                                     gpointer             arg3);
141 static void       _gail_entry_delete_text_cb       (GtkEntry             *entry,
142                                                     gint                 arg1,
143                                                     gint                 arg2);
144 static void       _gail_entry_changed_cb           (GtkEntry             *entry);
145 static gboolean   check_for_selection_change       (GailEntry            *entry,
146                                                     GtkEntry             *gtk_entry);
147
148 static void                  atk_action_interface_init          (AtkActionIface  *iface);
149
150 static gboolean              gail_entry_do_action               (AtkAction       *action,
151                                                                  gint            i);
152 static gboolean              idle_do_action                     (gpointer        data);
153 static gint                  gail_entry_get_n_actions           (AtkAction       *action);
154 static const gchar* gail_entry_action_get_description  (AtkAction       *action,
155                                                                  gint            i);
156 static const gchar* gail_entry_get_keybinding          (AtkAction       *action,
157                                                                  gint            i);
158 static const gchar* gail_entry_action_get_name         (AtkAction       *action,
159                                                                  gint            i);
160 static gboolean              gail_entry_action_set_description  (AtkAction       *action,
161                                                                  gint            i,
162                                                                  const gchar     *desc);
163
164 typedef struct _GailEntryPaste                  GailEntryPaste;
165
166 struct _GailEntryPaste
167 {
168   GtkEntry* entry;
169   gint position;
170 };
171
172 G_DEFINE_TYPE_WITH_CODE (GailEntry, gail_entry, GAIL_TYPE_WIDGET,
173                          G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init)
174                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init)
175                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init))
176
177 static void
178 gail_entry_class_init (GailEntryClass *klass)
179 {
180   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
181   AtkObjectClass  *class = ATK_OBJECT_CLASS (klass);
182   GailWidgetClass *widget_class;
183
184   widget_class = (GailWidgetClass*)klass;
185
186   gobject_class->finalize = gail_entry_finalize;
187
188   class->ref_state_set = gail_entry_ref_state_set;
189   class->get_index_in_parent = gail_entry_get_index_in_parent;
190   class->initialize = gail_entry_real_initialize;
191   class->get_attributes = gail_entry_get_attributes;
192
193   widget_class->notify_gtk = gail_entry_real_notify_gtk;
194 }
195
196 static void
197 gail_entry_init (GailEntry *entry)
198 {
199   entry->textutil = NULL;
200   entry->signal_name_insert = NULL;
201   entry->signal_name_delete = NULL;
202   entry->cursor_position = 0;
203   entry->selection_bound = 0;
204   entry->activate_description = NULL;
205   entry->activate_keybinding = NULL;
206 }
207
208 static void
209 gail_entry_real_initialize (AtkObject *obj, 
210                             gpointer  data)
211 {
212   GtkEntry *entry;
213   GailEntry *gail_entry;
214   gint start_pos, end_pos;
215
216   ATK_OBJECT_CLASS (gail_entry_parent_class)->initialize (obj, data);
217
218   gail_entry = GAIL_ENTRY (obj);
219   gail_entry->textutil = gail_text_util_new ();
220   
221   g_assert (GTK_IS_ENTRY (data));
222
223   entry = GTK_ENTRY (data);
224   text_setup (gail_entry, entry);
225   gtk_editable_get_selection_bounds (GTK_EDITABLE (entry),
226                                      &start_pos, &end_pos);
227   gail_entry->cursor_position = end_pos;
228   gail_entry->selection_bound = start_pos;
229
230   /* Set up signal callbacks */
231   g_signal_connect (data, "insert-text",
232         G_CALLBACK (_gail_entry_insert_text_cb), NULL);
233   g_signal_connect (data, "delete-text",
234         G_CALLBACK (_gail_entry_delete_text_cb), NULL);
235   g_signal_connect (data, "changed",
236         G_CALLBACK (_gail_entry_changed_cb), NULL);
237
238   if (gtk_entry_get_visibility (entry))
239     obj->role = ATK_ROLE_TEXT;
240   else
241     obj->role = ATK_ROLE_PASSWORD_TEXT;
242 }
243
244 static void
245 gail_entry_real_notify_gtk (GObject             *obj,
246                             GParamSpec          *pspec)
247 {
248   GtkWidget *widget;
249   AtkObject* atk_obj;
250   GtkEntry* gtk_entry;
251   GailEntry* entry;
252
253   widget = GTK_WIDGET (obj);
254   atk_obj = gtk_widget_get_accessible (widget);
255   gtk_entry = GTK_ENTRY (widget);
256   entry = GAIL_ENTRY (atk_obj);
257
258   if (strcmp (pspec->name, "cursor-position") == 0)
259     {
260       if (entry->insert_idle_handler == 0)
261         entry->insert_idle_handler = gdk_threads_add_idle (gail_entry_idle_notify_insert, entry);
262
263       if (check_for_selection_change (entry, gtk_entry))
264         g_signal_emit_by_name (atk_obj, "text_selection_changed");
265       /*
266        * The entry cursor position has moved so generate the signal.
267        */
268       g_signal_emit_by_name (atk_obj, "text_caret_moved", 
269                              entry->cursor_position);
270     }
271   else if (strcmp (pspec->name, "selection-bound") == 0)
272     {
273       if (entry->insert_idle_handler == 0)
274         entry->insert_idle_handler = gdk_threads_add_idle (gail_entry_idle_notify_insert, entry);
275
276       if (check_for_selection_change (entry, gtk_entry))
277         g_signal_emit_by_name (atk_obj, "text_selection_changed");
278     }
279   else if (strcmp (pspec->name, "editable") == 0)
280     {
281       gboolean value;
282
283       g_object_get (obj, "editable", &value, NULL);
284       atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE,
285                                                value);
286     }
287   else if (strcmp (pspec->name, "visibility") == 0)
288     {
289       gboolean visibility;
290       AtkRole new_role;
291
292       text_setup (entry, gtk_entry);
293       visibility = gtk_entry_get_visibility (gtk_entry);
294       new_role = visibility ? ATK_ROLE_TEXT : ATK_ROLE_PASSWORD_TEXT;
295       atk_object_set_role (atk_obj, new_role);
296     }
297   else if (strcmp (pspec->name, "invisible-char") == 0)
298     {
299       text_setup (entry, gtk_entry);
300     }
301   else if (strcmp (pspec->name, "editing-canceled") == 0)
302     {
303       if (entry->insert_idle_handler)
304         {
305           g_source_remove (entry->insert_idle_handler);
306           entry->insert_idle_handler = 0;
307         }
308     }
309   else
310     GAIL_WIDGET_CLASS (gail_entry_parent_class)->notify_gtk (obj, pspec);
311 }
312
313 static void
314 text_setup (GailEntry *entry,
315             GtkEntry  *gtk_entry)
316 {
317   if (gtk_entry_get_visibility (gtk_entry))
318     {
319       gail_text_util_text_setup (entry->textutil, gtk_entry_get_text (gtk_entry));
320     }
321   else
322     {
323       gunichar invisible_char;
324       GString *tmp_string = g_string_new (NULL);
325       gint ch_len; 
326       gchar buf[7];
327       guint length;
328       gint i;
329
330       invisible_char = gtk_entry_get_invisible_char (gtk_entry);
331       if (invisible_char == 0)
332         invisible_char = ' ';
333
334       ch_len = g_unichar_to_utf8 (invisible_char, buf);
335       length = gtk_entry_get_text_length (gtk_entry);
336       for (i = 0; i < length; i++)
337         {
338           g_string_append_len (tmp_string, buf, ch_len);
339         }
340
341       gail_text_util_text_setup (entry->textutil, tmp_string->str);
342       g_string_free (tmp_string, TRUE);
343
344     } 
345 }
346
347 static void
348 gail_entry_finalize (GObject            *object)
349 {
350   GailEntry *entry = GAIL_ENTRY (object);
351
352   g_object_unref (entry->textutil);
353   g_free (entry->activate_description);
354   g_free (entry->activate_keybinding);
355   if (entry->action_idle_handler)
356     {
357       g_source_remove (entry->action_idle_handler);
358       entry->action_idle_handler = 0;
359     }
360   if (entry->insert_idle_handler)
361     {
362       g_source_remove (entry->insert_idle_handler);
363       entry->insert_idle_handler = 0;
364     }
365   G_OBJECT_CLASS (gail_entry_parent_class)->finalize (object);
366 }
367
368 static gint
369 gail_entry_get_index_in_parent (AtkObject *accessible)
370 {
371   /*
372    * If the parent widget is a combo box then the index is 1
373    * otherwise do the normal thing.
374    */
375   if (accessible->accessible_parent)
376     if (GAIL_IS_COMBO_BOX (accessible->accessible_parent))
377       return 1;
378
379   return ATK_OBJECT_CLASS (gail_entry_parent_class)->get_index_in_parent (accessible);
380 }
381
382 /* atkobject.h */
383
384 static AtkStateSet*
385 gail_entry_ref_state_set (AtkObject *accessible)
386 {
387   AtkStateSet *state_set;
388   GtkEntry *entry;
389   gboolean value;
390   GtkWidget *widget;
391
392   state_set = ATK_OBJECT_CLASS (gail_entry_parent_class)->ref_state_set (accessible);
393   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
394
395   if (widget == NULL)
396     return state_set;
397
398   entry = GTK_ENTRY (widget);
399
400   g_object_get (G_OBJECT (entry), "editable", &value, NULL);
401   if (value)
402     atk_state_set_add_state (state_set, ATK_STATE_EDITABLE);
403   atk_state_set_add_state (state_set, ATK_STATE_SINGLE_LINE);
404
405   return state_set;
406 }
407
408 static AtkAttributeSet *
409 gail_entry_get_attributes (AtkObject *accessible)
410 {
411   GtkWidget *widget;
412   AtkAttributeSet *attributes;
413   AtkAttribute *placeholder_text;
414   const gchar *text;
415
416   attributes = ATK_OBJECT_CLASS (gail_entry_parent_class)->get_attributes (accessible);
417
418   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
419   if (widget == NULL)
420     return attributes;
421
422   text = gtk_entry_get_placeholder_text (GTK_ENTRY (widget));
423   if (text == NULL)
424     return attributes;
425
426   placeholder_text = g_malloc (sizeof (AtkAttribute));
427   placeholder_text->name = g_strdup ("placeholder-text");
428   placeholder_text->value = g_strdup (text);
429
430   attributes = g_slist_append (attributes, placeholder_text);
431
432   return attributes;
433 }
434
435 /* atktext.h */
436
437 static void
438 atk_text_interface_init (AtkTextIface *iface)
439 {
440   iface->get_text = gail_entry_get_text;
441   iface->get_character_at_offset = gail_entry_get_character_at_offset;
442   iface->get_text_before_offset = gail_entry_get_text_before_offset;
443   iface->get_text_at_offset = gail_entry_get_text_at_offset;
444   iface->get_text_after_offset = gail_entry_get_text_after_offset;
445   iface->get_caret_offset = gail_entry_get_caret_offset;
446   iface->set_caret_offset = gail_entry_set_caret_offset;
447   iface->get_character_count = gail_entry_get_character_count;
448   iface->get_n_selections = gail_entry_get_n_selections;
449   iface->get_selection = gail_entry_get_selection;
450   iface->add_selection = gail_entry_add_selection;
451   iface->remove_selection = gail_entry_remove_selection;
452   iface->set_selection = gail_entry_set_selection;
453   iface->get_run_attributes = gail_entry_get_run_attributes;
454   iface->get_default_attributes = gail_entry_get_default_attributes;
455   iface->get_character_extents = gail_entry_get_character_extents;
456   iface->get_offset_at_point = gail_entry_get_offset_at_point;
457 }
458
459 static gchar*
460 gail_entry_get_text (AtkText *text,
461                      gint    start_pos,
462                      gint    end_pos)
463 {
464   GtkWidget *widget;
465
466   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
467   if (widget == NULL)
468     /* State is defunct */
469     return NULL;
470
471   return gail_text_util_get_substring (GAIL_ENTRY (text)->textutil, start_pos, end_pos);
472 }
473
474 static gchar*
475 gail_entry_get_text_before_offset (AtkText          *text,
476                                    gint             offset,
477                                    AtkTextBoundary  boundary_type,
478                                    gint             *start_offset,
479                                    gint             *end_offset)
480 {
481   GtkWidget *widget;
482   GtkEntry *entry;
483
484   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
485   if (widget == NULL)
486     /* State is defunct */
487     return NULL;
488
489   /* Get Entry */
490   entry = GTK_ENTRY (widget);
491
492   return gail_text_util_get_text (GAIL_ENTRY (text)->textutil,
493                           gtk_entry_get_layout (entry), GAIL_BEFORE_OFFSET, 
494                           boundary_type, offset, start_offset, end_offset);
495 }
496
497 static gchar*
498 gail_entry_get_text_at_offset (AtkText          *text,
499                                gint             offset,
500                                AtkTextBoundary  boundary_type,
501                                gint             *start_offset,
502                                gint             *end_offset)
503 {
504   GtkWidget *widget;
505   GtkEntry *entry;
506
507   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
508   if (widget == NULL)
509     /* State is defunct */
510     return NULL;
511
512   /* Get Entry */
513   entry = GTK_ENTRY (widget);
514
515   return gail_text_util_get_text (GAIL_ENTRY (text)->textutil,
516                             gtk_entry_get_layout (entry), GAIL_AT_OFFSET, 
517                             boundary_type, offset, start_offset, end_offset);
518 }
519
520 static gchar*
521 gail_entry_get_text_after_offset  (AtkText          *text,
522                                    gint             offset,
523                                    AtkTextBoundary  boundary_type,
524                                    gint             *start_offset,
525                                    gint             *end_offset)
526 {
527   GtkWidget *widget;
528   GtkEntry *entry;
529
530   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
531   if (widget == NULL)
532     /* State is defunct */
533     return NULL;
534
535   /* Get Entry */
536   entry = GTK_ENTRY (widget);
537
538   return gail_text_util_get_text (GAIL_ENTRY (text)->textutil,
539                            gtk_entry_get_layout (entry), GAIL_AFTER_OFFSET, 
540                            boundary_type, offset, start_offset, end_offset);
541 }
542
543 static gint
544 gail_entry_get_character_count (AtkText *text)
545 {
546   GtkEntry *entry;
547   GtkWidget *widget;
548
549   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
550   if (widget == NULL)
551     /* State is defunct */
552     return 0;
553
554   entry = GTK_ENTRY (widget);
555   return g_utf8_strlen (gtk_entry_get_text (entry), -1);
556 }
557
558 static gint
559 gail_entry_get_caret_offset (AtkText *text)
560 {
561   GtkEntry *entry;
562   GtkWidget *widget;
563
564   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
565   if (widget == NULL)
566     /* State is defunct */
567     return 0;
568
569   entry = GTK_ENTRY (widget);
570
571   return gtk_editable_get_position (GTK_EDITABLE (entry));
572 }
573
574 static gboolean
575 gail_entry_set_caret_offset (AtkText *text, gint offset)
576 {
577   GtkEntry *entry;
578   GtkWidget *widget;
579
580   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
581   if (widget == NULL)
582     /* State is defunct */
583     return FALSE;
584
585   entry = GTK_ENTRY (widget);
586
587   gtk_editable_set_position (GTK_EDITABLE (entry), offset);
588   return TRUE;
589 }
590
591 static AtkAttributeSet*
592 gail_entry_get_run_attributes (AtkText *text,
593                                gint    offset,
594                                gint    *start_offset,
595                                gint    *end_offset)
596 {
597   GtkWidget *widget;
598   GtkEntry *entry;
599   AtkAttributeSet *at_set = NULL;
600   GtkTextDirection dir;
601
602   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
603   if (widget == NULL)
604     /* State is defunct */
605     return NULL;
606
607   entry = GTK_ENTRY (widget);
608  
609   dir = gtk_widget_get_direction (widget);
610   if (dir == GTK_TEXT_DIR_RTL)
611     {
612       at_set = gail_misc_add_attribute (at_set,
613                                         ATK_TEXT_ATTR_DIRECTION,
614        g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
615     }
616
617   at_set = gail_misc_layout_get_run_attributes (at_set,
618                                                 gtk_entry_get_layout (entry),
619                                                 (gchar*)gtk_entry_get_text (entry),
620                                                 offset,
621                                                 start_offset,
622                                                 end_offset);
623   return at_set;
624 }
625
626 static AtkAttributeSet*
627 gail_entry_get_default_attributes (AtkText *text)
628 {
629   GtkWidget *widget;
630   GtkEntry *entry;
631   AtkAttributeSet *at_set = NULL;
632
633   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
634   if (widget == NULL)
635     /* State is defunct */
636     return NULL;
637
638   entry = GTK_ENTRY (widget);
639
640   at_set = gail_misc_get_default_attributes (at_set,
641                                              gtk_entry_get_layout (entry),
642                                              widget);
643   return at_set;
644 }
645   
646 static void
647 gail_entry_get_character_extents (AtkText *text,
648                                   gint    offset,
649                                   gint    *x,
650                                   gint    *y,
651                                   gint    *width,
652                                   gint    *height,
653                                   AtkCoordType coords)
654 {
655   GtkWidget *widget;
656   GtkEntry *entry;
657   PangoRectangle char_rect;
658   gint index, x_layout, y_layout;
659   const gchar *entry_text;
660   gint start_pos, end_pos;
661
662   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
663   if (widget == NULL)
664     /* State is defunct */
665     return;
666
667   entry = GTK_ENTRY (widget);
668
669   gtk_editable_get_selection_bounds (GTK_EDITABLE (entry),
670                                      &start_pos, &end_pos);
671   gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout);
672   entry_text = gtk_entry_get_text (entry);
673
674   index = g_utf8_offset_to_pointer (entry_text, offset) - entry_text;
675
676   /* FIXME: entry->preedit cannot be accessed directly
677   cursor_index = g_utf8_offset_to_pointer (entry_text, end_pos) - entry_text;
678   if (index > cursor_index)
679     index += entry->preedit_length;
680   */
681   pango_layout_index_to_pos (gtk_entry_get_layout(entry), index, &char_rect);
682  
683   gail_misc_get_extents_from_pango_rectangle (widget, &char_rect, 
684                         x_layout, y_layout, x, y, width, height, coords);
685
686
687 static gint 
688 gail_entry_get_offset_at_point (AtkText *text,
689                                 gint x,
690                                 gint y,
691                                 AtkCoordType coords)
692
693   GtkWidget *widget;
694   GtkEntry *entry;
695   gint index, x_layout, y_layout;
696   const gchar *entry_text;
697
698   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
699   if (widget == NULL)
700     /* State is defunct */
701     return -1;
702
703   entry = GTK_ENTRY (widget);
704   
705   gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout);
706   entry_text = gtk_entry_get_text (entry);
707   
708   index = gail_misc_get_index_at_point_in_layout (widget, 
709                gtk_entry_get_layout(entry), x_layout, y_layout, x, y, coords);
710   if (index == -1)
711     {
712       if (coords == ATK_XY_SCREEN || coords == ATK_XY_WINDOW)
713         return g_utf8_strlen (entry_text, -1);
714
715       return index;  
716     }
717   else
718     {
719       gint start_pos, end_pos;
720
721       gtk_editable_get_selection_bounds (GTK_EDITABLE (entry),
722                                          &start_pos, &end_pos);
723       /* FIXME: entry->preedit_length cannot be accessed directly
724       cursor_index = g_utf8_offset_to_pointer (entry_text, end_pos) - entry_text;
725       if (index >= cursor_index && entry->preedit_length)
726         {
727           if (index >= cursor_index + entry->preedit_length)
728             index -= entry->preedit_length;
729           else
730             index = cursor_index;
731         }
732       */
733       return g_utf8_pointer_to_offset (entry_text, entry_text + index);
734     }
735 }
736
737 static gint
738 gail_entry_get_n_selections (AtkText              *text)
739 {
740   GtkEntry *entry;
741   GtkWidget *widget;
742   gint select_start, select_end;
743
744   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
745   if (widget == NULL)
746     /* State is defunct */
747     return -1;
748
749   entry = GTK_ENTRY (widget);
750
751   gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &select_start, 
752                                      &select_end);
753
754   if (select_start != select_end)
755     return 1;
756   else
757     return 0;
758 }
759
760 static gchar*
761 gail_entry_get_selection (AtkText *text,
762                           gint    selection_num,
763                           gint    *start_pos,
764                           gint    *end_pos)
765 {
766   GtkEntry *entry;
767   GtkWidget *widget;
768
769   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
770   if (widget == NULL)
771     /* State is defunct */
772     return NULL;
773
774  /* Only let the user get the selection if one is set, and if the
775   * selection_num is 0.
776   */
777   if (selection_num != 0)
778      return NULL;
779
780   entry = GTK_ENTRY (widget);
781   gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), start_pos, end_pos);
782
783   if (*start_pos != *end_pos)
784      return gtk_editable_get_chars (GTK_EDITABLE (entry), *start_pos, *end_pos);
785   else
786      return NULL;
787 }
788
789 static gboolean
790 gail_entry_add_selection (AtkText *text,
791                           gint    start_pos,
792                           gint    end_pos)
793 {
794   GtkEntry *entry;
795   GtkWidget *widget;
796   gint select_start, select_end;
797
798   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
799   if (widget == NULL)
800     /* State is defunct */
801     return FALSE;
802
803   entry = GTK_ENTRY (widget);
804
805   gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &select_start, 
806                                      &select_end);
807
808  /* If there is already a selection, then don't allow another to be added,
809   * since GtkEntry only supports one selected region.
810   */
811   if (select_start == select_end)
812     {
813        gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
814        return TRUE;
815     }
816   else
817    return FALSE;
818 }
819
820 static gboolean
821 gail_entry_remove_selection (AtkText *text,
822                              gint    selection_num)
823 {
824   GtkEntry *entry;
825   GtkWidget *widget;
826   gint select_start, select_end, caret_pos;
827
828   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
829   if (widget == NULL)
830     /* State is defunct */
831     return FALSE;
832
833   if (selection_num != 0)
834      return FALSE;
835
836   entry = GTK_ENTRY (widget);
837   gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &select_start, 
838                                      &select_end);
839
840   if (select_start != select_end)
841     {
842      /* Setting the start & end of the selected region to the caret position
843       * turns off the selection.
844       */
845       caret_pos = gtk_editable_get_position (GTK_EDITABLE (entry));
846       gtk_editable_select_region (GTK_EDITABLE (entry), caret_pos, caret_pos);
847       return TRUE;
848     }
849   else
850     return FALSE;
851 }
852
853 static gboolean
854 gail_entry_set_selection (AtkText *text,
855                           gint    selection_num,
856                           gint    start_pos,
857                           gint    end_pos)
858 {
859   GtkEntry *entry;
860   GtkWidget *widget;
861   gint select_start, select_end;
862
863   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
864   if (widget == NULL)
865     /* State is defunct */
866     return FALSE;
867
868  /* Only let the user move the selection if one is set, and if the
869   * selection_num is 0
870   */
871   if (selection_num != 0)
872      return FALSE;
873
874   entry = GTK_ENTRY (widget);
875
876   gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &select_start, 
877                                      &select_end);
878
879   if (select_start != select_end)
880     {
881       gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
882       return TRUE;
883     }
884   else
885     return FALSE;
886 }
887
888 static void
889 atk_editable_text_interface_init (AtkEditableTextIface *iface)
890 {
891   iface->set_text_contents = gail_entry_set_text_contents;
892   iface->insert_text = gail_entry_insert_text;
893   iface->copy_text = gail_entry_copy_text;
894   iface->cut_text = gail_entry_cut_text;
895   iface->delete_text = gail_entry_delete_text;
896   iface->paste_text = gail_entry_paste_text;
897   iface->set_run_attributes = NULL;
898 }
899
900 static void
901 gail_entry_set_text_contents (AtkEditableText *text,
902                               const gchar     *string)
903 {
904   GtkEntry *entry;
905   GtkWidget *widget;
906   GtkEditable *editable;
907
908   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
909   if (widget == NULL)
910     /* State is defunct */
911     return;
912
913   entry = GTK_ENTRY (widget);
914   editable = GTK_EDITABLE (entry);
915   if (!gtk_editable_get_editable (editable))
916     return;
917
918   gtk_entry_set_text (entry, string);
919 }
920
921 static void
922 gail_entry_insert_text (AtkEditableText *text,
923                         const gchar     *string,
924                         gint            length,
925                         gint            *position)
926 {
927   GtkEntry *entry;
928   GtkWidget *widget;
929   GtkEditable *editable;
930
931   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
932   if (widget == NULL)
933     /* State is defunct */
934     return;
935
936   entry = GTK_ENTRY (widget);
937   editable = GTK_EDITABLE (entry);
938   if (!gtk_editable_get_editable (editable))
939     return;
940
941   gtk_editable_insert_text (editable, string, length, position);
942   gtk_editable_set_position (editable, *position);
943 }
944
945 static void
946 gail_entry_copy_text   (AtkEditableText *text,
947                         gint            start_pos,
948                         gint            end_pos)
949 {
950   GtkEntry *entry;
951   GtkWidget *widget;
952   GtkEditable *editable;
953   gchar *str;
954   GtkClipboard *clipboard;
955
956   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
957   if (widget == NULL)
958     /* State is defunct */
959     return;
960
961   entry = GTK_ENTRY (widget);
962   editable = GTK_EDITABLE (entry);
963   str = gtk_editable_get_chars (editable, start_pos, end_pos);
964   clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (widget),
965                                              GDK_SELECTION_CLIPBOARD);
966   gtk_clipboard_set_text (clipboard, str, -1);
967 }
968
969 static void
970 gail_entry_cut_text (AtkEditableText *text,
971                      gint            start_pos,
972                      gint            end_pos)
973 {
974   GtkEntry *entry;
975   GtkWidget *widget;
976   GtkEditable *editable;
977   gchar *str;
978   GtkClipboard *clipboard;
979
980   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
981   if (widget == NULL)
982     /* State is defunct */
983     return;
984
985   entry = GTK_ENTRY (widget);
986   editable = GTK_EDITABLE (entry);
987   if (!gtk_editable_get_editable (editable))
988     return;
989   str = gtk_editable_get_chars (editable, start_pos, end_pos);
990   clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (widget),
991                                              GDK_SELECTION_CLIPBOARD);
992   gtk_clipboard_set_text (clipboard, str, -1);
993   gtk_editable_delete_text (editable, start_pos, end_pos);
994 }
995
996 static void
997 gail_entry_delete_text (AtkEditableText *text,
998                         gint            start_pos,
999                         gint            end_pos)
1000 {
1001   GtkEntry *entry;
1002   GtkWidget *widget;
1003   GtkEditable *editable;
1004
1005   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1006   if (widget == NULL)
1007     /* State is defunct */
1008     return;
1009
1010   entry = GTK_ENTRY (widget);
1011   editable = GTK_EDITABLE (entry);
1012   if (!gtk_editable_get_editable (editable))
1013     return;
1014
1015   gtk_editable_delete_text (editable, start_pos, end_pos);
1016 }
1017
1018 static void
1019 gail_entry_paste_text (AtkEditableText *text,
1020                        gint            position)
1021 {
1022   GtkWidget *widget;
1023   GtkEditable *editable;
1024   GailEntryPaste paste_struct;
1025   GtkClipboard *clipboard;
1026
1027   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1028   if (widget == NULL)
1029     /* State is defunct */
1030     return;
1031
1032   editable = GTK_EDITABLE (widget);
1033   if (!gtk_editable_get_editable (editable))
1034     return;
1035   paste_struct.entry = GTK_ENTRY (widget);
1036   paste_struct.position = position;
1037
1038   g_object_ref (paste_struct.entry);
1039   clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (widget),
1040                                              GDK_SELECTION_CLIPBOARD);
1041   gtk_clipboard_request_text (clipboard,
1042     gail_entry_paste_received, &paste_struct);
1043 }
1044
1045 static void
1046 gail_entry_paste_received (GtkClipboard *clipboard,
1047                 const gchar  *text,
1048                 gpointer     data)
1049 {
1050   GailEntryPaste* paste_struct = (GailEntryPaste *)data;
1051
1052   if (text)
1053     gtk_editable_insert_text (GTK_EDITABLE (paste_struct->entry), text, -1,
1054        &(paste_struct->position));
1055
1056   g_object_unref (paste_struct->entry);
1057 }
1058
1059 /* Callbacks */
1060
1061 static gboolean
1062 gail_entry_idle_notify_insert (gpointer data)
1063 {
1064   GailEntry *entry;
1065
1066   entry = GAIL_ENTRY (data);
1067   entry->insert_idle_handler = 0;
1068   gail_entry_notify_insert (entry);
1069
1070   return FALSE;
1071 }
1072
1073 static void
1074 gail_entry_notify_insert (GailEntry *entry)
1075 {
1076   GtkWidget *widget;
1077
1078   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (entry));
1079   if (gtk_entry_get_text_length (GTK_ENTRY (widget)) == 0)
1080     return;
1081
1082   if (entry->signal_name_insert)
1083     {
1084       g_signal_emit_by_name (entry, 
1085                              entry->signal_name_insert,
1086                              entry->position_insert,
1087                              entry->length_insert);
1088       entry->signal_name_insert = NULL;
1089     }
1090 }
1091
1092 /* Note arg1 returns the character at the start of the insert.
1093  * arg2 returns the number of characters inserted.
1094  */
1095 static void 
1096 _gail_entry_insert_text_cb (GtkEntry *entry, 
1097                             gchar    *arg1, 
1098                             gint     arg2,
1099                             gpointer arg3)
1100 {
1101   AtkObject *accessible;
1102   GailEntry *gail_entry;
1103   gint *position = (gint *) arg3;
1104
1105   if (arg2 == 0)
1106     return;
1107
1108   accessible = gtk_widget_get_accessible (GTK_WIDGET (entry));
1109   gail_entry = GAIL_ENTRY (accessible);
1110   if (!gail_entry->signal_name_insert)
1111     {
1112       gail_entry->signal_name_insert = "text_changed::insert";
1113       gail_entry->position_insert = *position;
1114       gail_entry->length_insert = g_utf8_strlen(arg1, arg2);
1115     }
1116   /*
1117    * The signal will be emitted when the cursor position is updated.
1118    * or in an idle handler if it not updated.
1119    */
1120    if (gail_entry->insert_idle_handler == 0)
1121      gail_entry->insert_idle_handler = gdk_threads_add_idle (gail_entry_idle_notify_insert, gail_entry);
1122 }
1123
1124 static gunichar 
1125 gail_entry_get_character_at_offset (AtkText *text,
1126                                     gint     offset)
1127 {
1128   GtkWidget *widget;
1129   GailEntry *entry;
1130   gchar *string;
1131   gchar *index;
1132   gunichar unichar;
1133
1134   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
1135   if (widget == NULL)
1136     /* State is defunct */
1137     return '\0';
1138
1139   entry = GAIL_ENTRY (text);
1140   string = gail_text_util_get_substring (entry->textutil, 0, -1);
1141   if (offset >= g_utf8_strlen (string, -1))
1142     {
1143       unichar = '\0';
1144     }
1145   else
1146     {
1147       index = g_utf8_offset_to_pointer (string, offset);
1148
1149       unichar = g_utf8_get_char(index);
1150     }
1151
1152   g_free(string);
1153   return unichar;
1154 }
1155
1156 static void
1157 gail_entry_notify_delete (GailEntry *entry)
1158 {
1159   if (entry->signal_name_delete)
1160     {
1161       g_signal_emit_by_name (entry, 
1162                              entry->signal_name_delete,
1163                              entry->position_delete,
1164                              entry->length_delete);
1165       entry->signal_name_delete = NULL;
1166     }
1167 }
1168
1169 /* Note arg1 returns the start of the delete range, arg2 returns the
1170  * end of the delete range if multiple characters are deleted.  
1171  */
1172 static void 
1173 _gail_entry_delete_text_cb (GtkEntry *entry, 
1174                             gint      arg1, 
1175                             gint      arg2)
1176 {
1177   AtkObject *accessible;
1178   GailEntry *gail_entry;
1179
1180   /*
1181    * Zero length text deleted so ignore
1182    */
1183   if (arg2 - arg1 == 0)
1184     return;
1185
1186   accessible = gtk_widget_get_accessible (GTK_WIDGET (entry));
1187   gail_entry = GAIL_ENTRY (accessible);
1188   if (!gail_entry->signal_name_delete)
1189     {
1190       gail_entry->signal_name_delete = "text_changed::delete";
1191       gail_entry->position_delete = arg1;
1192       gail_entry->length_delete = arg2 - arg1;
1193     }
1194   gail_entry_notify_delete (gail_entry);
1195 }
1196
1197 static void
1198 _gail_entry_changed_cb (GtkEntry *entry)
1199 {
1200   AtkObject *accessible;
1201   GailEntry *gail_entry;
1202
1203   accessible = gtk_widget_get_accessible (GTK_WIDGET (entry));
1204
1205   gail_entry = GAIL_ENTRY (accessible);
1206
1207   text_setup (gail_entry, entry);
1208 }
1209
1210 static gboolean 
1211 check_for_selection_change (GailEntry   *entry,
1212                             GtkEntry    *gtk_entry)
1213 {
1214   gboolean selected, ret_val = FALSE;
1215   gint start_pos, end_pos;
1216
1217   selected = gtk_editable_get_selection_bounds (GTK_EDITABLE (gtk_entry),
1218                                                 &start_pos, &end_pos);
1219   if (selected)
1220     {
1221       if (end_pos != entry->cursor_position ||
1222           start_pos != entry->selection_bound)
1223         /*
1224          * This check is here as this function can be called
1225          * for notification of selection_bound and current_pos.
1226          * The values of current_pos and selection_bound may be the same 
1227          * for both notifications and we only want to generate one
1228          * text_selection_changed signal.
1229          */
1230         ret_val = TRUE;
1231     }
1232   else 
1233     {
1234       /* We had a selection */
1235       ret_val = (entry->cursor_position != entry->selection_bound);
1236     }
1237   entry->cursor_position = end_pos;
1238   entry->selection_bound = start_pos;
1239
1240   return ret_val;
1241 }
1242
1243 static void
1244 atk_action_interface_init (AtkActionIface *iface)
1245 {
1246   iface->do_action = gail_entry_do_action;
1247   iface->get_n_actions = gail_entry_get_n_actions;
1248   iface->get_description = gail_entry_action_get_description;
1249   iface->get_keybinding = gail_entry_get_keybinding;
1250   iface->get_name = gail_entry_action_get_name;
1251   iface->set_description = gail_entry_action_set_description;
1252 }
1253
1254 static gboolean
1255 gail_entry_do_action (AtkAction *action,
1256                       gint      i)
1257 {
1258   GailEntry *entry;
1259   GtkWidget *widget;
1260   gboolean return_value = TRUE;
1261
1262   entry = GAIL_ENTRY (action);
1263   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
1264   if (widget == NULL)
1265     /*
1266      * State is defunct
1267      */
1268     return FALSE;
1269
1270   if (!gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget))
1271     return FALSE;
1272
1273   switch (i)
1274     {
1275     case 0:
1276       if (entry->action_idle_handler)
1277         return_value = FALSE;
1278       else
1279         entry->action_idle_handler = gdk_threads_add_idle (idle_do_action, entry);
1280       break;
1281     default:
1282       return_value = FALSE;
1283       break;
1284     }
1285   return return_value; 
1286 }
1287
1288 static gboolean
1289 idle_do_action (gpointer data)
1290 {
1291   GailEntry *entry;
1292   GtkWidget *widget;
1293
1294   entry = GAIL_ENTRY (data);
1295   entry->action_idle_handler = 0;
1296   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (entry));
1297   if (widget == NULL /* State is defunct */ ||
1298       !gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget))
1299     return FALSE;
1300
1301   gtk_widget_activate (widget);
1302
1303   return FALSE;
1304 }
1305
1306 static gint
1307 gail_entry_get_n_actions (AtkAction *action)
1308 {
1309   return 1;
1310 }
1311
1312 static const gchar*
1313 gail_entry_action_get_description (AtkAction *action,
1314                                    gint      i)
1315 {
1316   GailEntry *entry;
1317   const gchar *return_value;
1318
1319   entry = GAIL_ENTRY (action);
1320   switch (i)
1321     {
1322     case 0:
1323       return_value = entry->activate_description;
1324       break;
1325     default:
1326       return_value = NULL;
1327       break;
1328     }
1329   return return_value; 
1330 }
1331
1332 static const gchar*
1333 gail_entry_get_keybinding (AtkAction *action,
1334                            gint      i)
1335 {
1336   GailEntry *entry;
1337   gchar *return_value = NULL;
1338
1339   entry = GAIL_ENTRY (action);
1340   switch (i)
1341     {
1342     case 0:
1343       {
1344         /*
1345          * We look for a mnemonic on the label
1346          */
1347         GtkWidget *widget;
1348         GtkWidget *label;
1349         AtkRelationSet *set;
1350         AtkRelation *relation;
1351         GPtrArray *target;
1352         gpointer target_object;
1353         guint key_val; 
1354
1355         entry = GAIL_ENTRY (action);
1356         widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (entry));
1357         if (widget == NULL)
1358           /*
1359            * State is defunct
1360            */
1361           return NULL;
1362
1363         /* Find labelled-by relation */
1364
1365         set = atk_object_ref_relation_set (ATK_OBJECT (action));
1366         if (!set)
1367           return NULL;
1368         label = NULL;
1369         relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY);
1370         if (relation)
1371           {              
1372             target = atk_relation_get_target (relation);
1373           
1374             target_object = g_ptr_array_index (target, 0);
1375             label = gtk_accessible_get_widget (GTK_ACCESSIBLE (target_object));
1376           }
1377
1378         g_object_unref (set);
1379
1380         if (GTK_IS_LABEL (label))
1381           {
1382             key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); 
1383             if (key_val != GDK_KEY_VoidSymbol)
1384               return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK);
1385           }
1386         g_free (entry->activate_keybinding);
1387         entry->activate_keybinding = return_value;
1388         break;
1389       }
1390     default:
1391       break;
1392     }
1393   return return_value; 
1394 }
1395
1396 static const gchar*
1397 gail_entry_action_get_name (AtkAction *action,
1398                             gint      i)
1399 {
1400   const gchar *return_value;
1401
1402   switch (i)
1403     {
1404     case 0:
1405       return_value = "activate";
1406       break;
1407     default:
1408       return_value = NULL;
1409       break;
1410   }
1411   return return_value; 
1412 }
1413
1414 static gboolean
1415 gail_entry_action_set_description (AtkAction      *action,
1416                                    gint           i,
1417                                    const gchar    *desc)
1418 {
1419   GailEntry *entry;
1420   gchar **value;
1421
1422   entry = GAIL_ENTRY (action);
1423   switch (i)
1424     {
1425     case 0:
1426       value = &entry->activate_description;
1427       break;
1428     default:
1429       value = NULL;
1430       break;
1431     }
1432
1433   if (value)
1434     {
1435       g_free (*value);
1436       *value = g_strdup (desc);
1437       return TRUE;
1438     }
1439   else
1440     return FALSE;
1441 }