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