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