]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtkentryaccessible.c
Initial conversion of GailWidget to GtkWidgetAccessible
[~andy/gtk] / gtk / a11y / gtkentryaccessible.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 "gtkpango.h"
25 #include "gtkentryaccessible.h"
26 #include "gtkcomboboxaccessible.h"
27
28 /* Callbacks */
29
30 static void     insert_text_cb             (GtkEditable        *editable,
31                                             gchar              *new_text,
32                                             gint                new_text_length,
33                                             gint               *position);
34 static void     delete_text_cb             (GtkEditable        *editable,
35                                             gint                start,
36                                             gint                end);
37 static void     changed_cb                 (GtkEditable        *editable);
38
39 static gboolean check_for_selection_change (GtkEntryAccessible *entry,
40                                             GtkEntry           *gtk_entry);
41
42
43 static void atk_editable_text_interface_init (AtkEditableTextIface *iface);
44 static void atk_text_interface_init          (AtkTextIface         *iface);
45 static void atk_action_interface_init        (AtkActionIface       *iface);
46
47
48 G_DEFINE_TYPE_WITH_CODE (GtkEntryAccessible, gtk_entry_accessible, GTK_TYPE_WIDGET_ACCESSIBLE,
49                          G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, atk_editable_text_interface_init)
50                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init)
51                          G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init))
52
53
54 static void
55 gtk_entry_accessible_finalize (GObject *object)
56 {
57   GtkEntryAccessible *entry = GTK_ENTRY_ACCESSIBLE (object);
58
59   if (entry->action_idle_handler)
60     {
61       g_source_remove (entry->action_idle_handler);
62       entry->action_idle_handler = 0;
63     }
64   G_OBJECT_CLASS (gtk_entry_accessible_parent_class)->finalize (object);
65 }
66
67 static AtkStateSet*
68 gtk_entry_accessible_ref_state_set (AtkObject *accessible)
69 {
70   AtkStateSet *state_set;
71   gboolean value;
72   GtkWidget *widget;
73
74   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
75   if (widget == NULL)
76     return NULL;
77
78   state_set = ATK_OBJECT_CLASS (gtk_entry_accessible_parent_class)->ref_state_set (accessible);
79
80   g_object_get (G_OBJECT (widget), "editable", &value, NULL);
81   if (value)
82     atk_state_set_add_state (state_set, ATK_STATE_EDITABLE);
83   atk_state_set_add_state (state_set, ATK_STATE_SINGLE_LINE);
84
85   return state_set;
86 }
87
88 static AtkAttributeSet *
89 gtk_entry_accessible_get_attributes (AtkObject *accessible)
90 {
91   GtkWidget *widget;
92   AtkAttributeSet *attributes;
93   AtkAttribute *placeholder_text;
94   const gchar *text;
95
96   attributes = ATK_OBJECT_CLASS (gtk_entry_accessible_parent_class)->get_attributes (accessible);
97
98   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
99   if (widget == NULL)
100     return attributes;
101
102   text = gtk_entry_get_placeholder_text (GTK_ENTRY (widget));
103   if (text == NULL)
104     return attributes;
105
106   placeholder_text = g_malloc (sizeof (AtkAttribute));
107   placeholder_text->name = g_strdup ("placeholder-text");
108   placeholder_text->value = g_strdup (text);
109
110   attributes = g_slist_append (attributes, placeholder_text);
111
112   return attributes;
113 }
114
115 static void
116 gtk_entry_accessible_initialize (AtkObject *obj,
117                                  gpointer   data)
118 {
119   GtkEntry *entry;
120   GtkEntryAccessible *gtk_entry_accessible;
121   gint start_pos, end_pos;
122
123   ATK_OBJECT_CLASS (gtk_entry_accessible_parent_class)->initialize (obj, data);
124
125   gtk_entry_accessible = GTK_ENTRY_ACCESSIBLE (obj);
126
127   entry = GTK_ENTRY (data);
128   gtk_editable_get_selection_bounds (GTK_EDITABLE (entry),
129                                      &start_pos, &end_pos);
130   gtk_entry_accessible->cursor_position = end_pos;
131   gtk_entry_accessible->selection_bound = start_pos;
132
133   /* Set up signal callbacks */
134   g_signal_connect (entry, "insert-text", G_CALLBACK (insert_text_cb), NULL);
135   g_signal_connect (entry, "delete-text", G_CALLBACK (delete_text_cb), NULL);
136   g_signal_connect (entry, "changed", G_CALLBACK (changed_cb), NULL);
137
138   if (gtk_entry_get_visibility (entry))
139     obj->role = ATK_ROLE_TEXT;
140   else
141     obj->role = ATK_ROLE_PASSWORD_TEXT;
142 }
143
144 static void
145 gtk_entry_accessible_notify_gtk (GObject    *obj,
146                                  GParamSpec *pspec)
147 {
148   GtkWidget *widget;
149   AtkObject* atk_obj;
150   GtkEntry* gtk_entry;
151   GtkEntryAccessible* entry;
152
153   widget = GTK_WIDGET (obj);
154   atk_obj = gtk_widget_get_accessible (widget);
155   gtk_entry = GTK_ENTRY (widget);
156   entry = GTK_ENTRY_ACCESSIBLE (atk_obj);
157
158   if (strcmp (pspec->name, "cursor-position") == 0)
159     {
160       if (check_for_selection_change (entry, gtk_entry))
161         g_signal_emit_by_name (atk_obj, "text_selection_changed");
162       /*
163        * The entry cursor position has moved so generate the signal.
164        */
165       g_signal_emit_by_name (atk_obj, "text_caret_moved",
166                              entry->cursor_position);
167     }
168   else if (strcmp (pspec->name, "selection-bound") == 0)
169     {
170       if (check_for_selection_change (entry, gtk_entry))
171         g_signal_emit_by_name (atk_obj, "text_selection_changed");
172     }
173   else if (strcmp (pspec->name, "editable") == 0)
174     {
175       gboolean value;
176
177       g_object_get (obj, "editable", &value, NULL);
178       atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, value);
179     }
180   else if (strcmp (pspec->name, "visibility") == 0)
181     {
182       gboolean visibility;
183       AtkRole new_role;
184
185       visibility = gtk_entry_get_visibility (gtk_entry);
186       new_role = visibility ? ATK_ROLE_TEXT : ATK_ROLE_PASSWORD_TEXT;
187       atk_object_set_role (atk_obj, new_role);
188     }
189   else
190     GTK_WIDGET_ACCESSIBLE_CLASS (gtk_entry_accessible_parent_class)->notify_gtk (obj, pspec);
191 }
192
193 static gint
194 gtk_entry_accessible_get_index_in_parent (AtkObject *accessible)
195 {
196   /*
197    * If the parent widget is a combo box then the index is 1
198    * otherwise do the normal thing.
199    */
200   if (accessible->accessible_parent)
201     if (GTK_IS_COMBO_BOX_ACCESSIBLE (accessible->accessible_parent))
202       return 1;
203
204   return ATK_OBJECT_CLASS (gtk_entry_accessible_parent_class)->get_index_in_parent (accessible);
205 }
206
207 static void
208 gtk_entry_accessible_class_init (GtkEntryAccessibleClass *klass)
209 {
210   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
211   AtkObjectClass  *class = ATK_OBJECT_CLASS (klass);
212   GtkWidgetAccessibleClass *widget_class = (GtkWidgetAccessibleClass*)klass;
213
214   gobject_class->finalize = gtk_entry_accessible_finalize;
215
216   class->ref_state_set = gtk_entry_accessible_ref_state_set;
217   class->get_index_in_parent = gtk_entry_accessible_get_index_in_parent;
218   class->initialize = gtk_entry_accessible_initialize;
219   class->get_attributes = gtk_entry_accessible_get_attributes;
220
221   widget_class->notify_gtk = gtk_entry_accessible_notify_gtk;
222 }
223
224 static void
225 gtk_entry_accessible_init (GtkEntryAccessible *entry)
226 {
227   entry->length_insert = 0;
228   entry->length_delete = 0;
229   entry->cursor_position = 0;
230   entry->selection_bound = 0;
231 }
232
233 static gchar *
234 gtk_entry_accessible_get_text (AtkText *atk_text,
235                                gint     start_pos,
236                                gint     end_pos)
237 {
238   GtkWidget *widget;
239   const gchar *text;
240
241   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
242   if (widget == NULL)
243     return NULL;
244
245   /* FIXME: is this acceptable ? */
246   if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
247     return g_strdup ("");
248
249   text = gtk_entry_get_text (GTK_ENTRY (widget));
250
251   if (text)
252     return g_utf8_substring (text, start_pos, end_pos > -1 ? end_pos : g_utf8_strlen (text, -1));
253
254   return NULL;
255 }
256
257 static gchar *
258 gtk_entry_accessible_get_text_before_offset (AtkText         *text,
259                                              gint             offset,
260                                              AtkTextBoundary  boundary_type,
261                                              gint            *start_offset,
262                                              gint            *end_offset)
263 {
264   GtkWidget *widget;
265
266   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
267   if (widget == NULL)
268     return NULL;
269
270   /* FIXME: is this acceptable ? */
271   if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
272     return g_strdup ("");
273
274   return _gtk_pango_get_text_before (gtk_entry_get_layout (GTK_ENTRY (widget)),
275                                      boundary_type, offset,
276                                      start_offset, end_offset);
277 }
278
279 static gchar *
280 gtk_entry_accessible_get_text_at_offset (AtkText         *text,
281                                          gint             offset,
282                                          AtkTextBoundary  boundary_type,
283                                          gint            *start_offset,
284                                          gint            *end_offset)
285 {
286   GtkWidget *widget;
287
288   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
289   if (widget == NULL)
290     return NULL;
291
292   /* FIXME: is this acceptable ? */
293   if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
294     return g_strdup ("");
295
296   return _gtk_pango_get_text_at (gtk_entry_get_layout (GTK_ENTRY (widget)),
297                                  boundary_type, offset,
298                                  start_offset, end_offset);
299 }
300
301 static gchar *
302 gtk_entry_accessible_get_text_after_offset (AtkText         *text,
303                                             gint             offset,
304                                             AtkTextBoundary  boundary_type,
305                                             gint            *start_offset,
306                                             gint            *end_offset)
307 {
308   GtkWidget *widget;
309
310   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
311   if (widget == NULL)
312     return NULL;
313
314   /* FIXME: is this acceptable ? */
315   if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
316     return g_strdup ("");
317
318   return _gtk_pango_get_text_after (gtk_entry_get_layout (GTK_ENTRY (widget)),
319                                     boundary_type, offset,
320                                     start_offset, end_offset);
321 }
322
323 static gint
324 gtk_entry_accessible_get_character_count (AtkText *atk_text)
325 {
326   GtkWidget *widget;
327   const gchar *text;
328
329   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
330   if (widget == NULL)
331     return 0;
332
333   text = gtk_entry_get_text (GTK_ENTRY (widget));
334
335   if (text)
336     return g_utf8_strlen (text, -1);
337
338   return 0;
339 }
340
341 static gint
342 gtk_entry_accessible_get_caret_offset (AtkText *text)
343 {
344   GtkWidget *widget;
345
346   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
347   if (widget == NULL)
348     return 0;
349
350   return gtk_editable_get_position (GTK_EDITABLE (widget));
351 }
352
353 static gboolean
354 gtk_entry_accessible_set_caret_offset (AtkText *text,
355                                        gint     offset)
356 {
357   GtkWidget *widget;
358
359   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
360   if (widget == NULL)
361     return FALSE;
362
363   gtk_editable_set_position (GTK_EDITABLE (widget), offset);
364
365   return TRUE;
366 }
367
368 static AtkAttributeSet *
369 add_text_attribute (AtkAttributeSet  *attributes,
370                     AtkTextAttribute  attr,
371                     gint              i)
372 {
373   AtkAttribute *at;
374
375   at = g_new (AtkAttribute, 1);
376   at->name = g_strdup (atk_text_attribute_get_name (attr));
377   at->value = g_strdup (atk_text_attribute_get_value (attr, i));
378
379   return g_slist_prepend (attributes, at);
380 }
381
382 static AtkAttributeSet *
383 gtk_entry_accessible_get_run_attributes (AtkText *text,
384                                          gint     offset,
385                                          gint    *start_offset,
386                                          gint    *end_offset)
387 {
388   GtkWidget *widget;
389   AtkAttributeSet *attributes;
390
391   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
392   if (widget == NULL)
393     return NULL;
394
395   attributes = NULL;
396   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
397                                    gtk_widget_get_direction (widget));
398   attributes = _gtk_pango_get_run_attributes (attributes,
399                                               gtk_entry_get_layout (GTK_ENTRY (widget)),
400                                               offset,
401                                               start_offset,
402                                               end_offset);
403
404   return attributes;
405 }
406
407 static AtkAttributeSet *
408 gtk_entry_accessible_get_default_attributes (AtkText *text)
409 {
410   GtkWidget *widget;
411   AtkAttributeSet *attributes;
412
413   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
414   if (widget == NULL)
415     return NULL;
416
417   attributes = NULL;
418   attributes = add_text_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
419                                    gtk_widget_get_direction (widget));
420   attributes = _gtk_pango_get_default_attributes (attributes,
421                                                   gtk_entry_get_layout (GTK_ENTRY (widget)));
422   attributes = _gtk_style_context_get_attributes (attributes,
423                                                   gtk_widget_get_style_context (widget),
424                                                   gtk_widget_get_state_flags (widget));
425
426   return attributes;
427 }
428
429 static void
430 gtk_entry_accessible_get_character_extents (AtkText      *text,
431                                             gint          offset,
432                                             gint         *x,
433                                             gint         *y,
434                                             gint         *width,
435                                             gint         *height,
436                                             AtkCoordType  coords)
437 {
438   GtkWidget *widget;
439   GtkEntry *entry;
440   PangoRectangle char_rect;
441   const gchar *entry_text;
442   gint index, x_layout, y_layout;
443   GdkWindow *window;
444   gint x_window, y_window;
445
446   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
447   if (widget == NULL)
448     return;
449
450   entry = GTK_ENTRY (widget);
451
452   gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout);
453   entry_text = gtk_entry_get_text (entry);
454   index = g_utf8_offset_to_pointer (entry_text, offset) - entry_text;
455   pango_layout_index_to_pos (gtk_entry_get_layout (entry), index, &char_rect);
456   pango_extents_to_pixels (&char_rect, NULL);
457
458   window = gtk_widget_get_window (widget);
459   gdk_window_get_origin (window, &x_window, &y_window);
460
461   *x = x_window + x_layout + char_rect.x;
462   *y = x_window + y_layout + char_rect.y;
463   *width = char_rect.width;
464   *height = char_rect.height;
465
466   if (coords == ATK_XY_WINDOW)
467     {
468       window = gdk_window_get_toplevel (window);
469       gdk_window_get_origin (window, &x_window, &y_window);
470
471       *x -= x_window;
472       *y -= y_window;
473     }
474 }
475
476 static gint
477 gtk_entry_accessible_get_offset_at_point (AtkText      *atk_text,
478                                           gint          x,
479                                           gint          y,
480                                           AtkCoordType  coords)
481 {
482   GtkWidget *widget;
483   GtkEntry *entry;
484   const gchar *text;
485   gint index, x_layout, y_layout;
486   gint x_window, y_window;
487   gint x_local, y_local;
488   GdkWindow *window;
489
490   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
491   if (widget == NULL)
492     return -1;
493
494   entry = GTK_ENTRY (widget);
495
496   gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout);
497
498   window = gtk_widget_get_window (widget);
499   gdk_window_get_origin (window, &x_window, &y_window);
500
501   x_local = x - x_layout - x_window;
502   y_local = y - y_layout - y_window;
503
504   if (coords == ATK_XY_WINDOW)
505     {
506       window = gdk_window_get_toplevel (window);
507       gdk_window_get_origin (window, &x_window, &y_window);
508
509       x_local += x_window;
510       y_local += y_window;
511     }
512   if (!pango_layout_xy_to_index (gtk_entry_get_layout (entry),
513                                  x_local * PANGO_SCALE,
514                                  y_local * PANGO_SCALE,
515                                  &index, NULL))
516     {
517       if (x_local < 0 || y_local < 0)
518         index = 0;
519       else
520         index = -1;
521     }
522
523   if (index != -1)
524     {
525       text = gtk_entry_get_text (entry);
526       return g_utf8_pointer_to_offset (text, text + index);
527     }
528
529   return -1;
530 }
531
532 static gint
533 gtk_entry_accessible_get_n_selections (AtkText *text)
534 {
535   GtkWidget *widget;
536   gint start, end;
537
538   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
539   if (widget == NULL)
540     return 0;
541
542   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
543     return 1;
544
545   return 0;
546 }
547
548 static gchar *
549 gtk_entry_accessible_get_selection (AtkText *text,
550                                     gint     selection_num,
551                                     gint    *start_pos,
552                                     gint    *end_pos)
553 {
554   GtkWidget *widget;
555
556   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
557   if (widget == NULL)
558     return NULL;
559
560   if (selection_num != 0)
561      return NULL;
562
563   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), start_pos, end_pos))
564     return gtk_editable_get_chars (GTK_EDITABLE (widget), *start_pos, *end_pos);
565
566   return NULL;
567 }
568
569 static gboolean
570 gtk_entry_accessible_add_selection (AtkText *text,
571                                     gint     start_pos,
572                                     gint     end_pos)
573 {
574   GtkEntry *entry;
575   GtkWidget *widget;
576   gint start, end;
577
578   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
579   if (widget == NULL)
580     return FALSE;
581
582   entry = GTK_ENTRY (widget);
583
584   if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
585     {
586       gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos);
587       return TRUE;
588     }
589   else
590     return FALSE;
591 }
592
593 static gboolean
594 gtk_entry_accessible_remove_selection (AtkText *text,
595                                        gint     selection_num)
596 {
597   GtkWidget *widget;
598   gint start, end;
599
600   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
601   if (widget == NULL)
602     return FALSE;
603
604   if (selection_num != 0)
605      return FALSE;
606
607   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
608     {
609       gtk_editable_select_region (GTK_EDITABLE (widget), end, end);
610       return TRUE;
611     }
612   else
613     return FALSE;
614 }
615
616 static gboolean
617 gtk_entry_accessible_set_selection (AtkText *text,
618                                     gint     selection_num,
619                                     gint     start_pos,
620                                     gint     end_pos)
621 {
622   GtkWidget *widget;
623   gint start, end;
624
625   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
626   if (widget == NULL)
627     return FALSE;
628
629   if (selection_num != 0)
630      return FALSE;
631
632   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
633     {
634       gtk_editable_select_region (GTK_EDITABLE (widget), start_pos, end_pos);
635       return TRUE;
636     }
637   else
638     return FALSE;
639 }
640
641 static gunichar
642 gtk_entry_accessible_get_character_at_offset (AtkText *atk_text,
643                                               gint     offset)
644 {
645   GtkWidget *widget;
646   const gchar *text;
647   gchar *index;
648
649   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
650   if (widget == NULL)
651     return '\0';
652
653   if (!gtk_entry_get_visibility (GTK_ENTRY (widget)))
654     return '\0';
655
656   text = gtk_entry_get_text (GTK_ENTRY (widget));
657   if (offset >= g_utf8_strlen (text, -1))
658     return '\0';
659
660   index = g_utf8_offset_to_pointer (text, offset);
661
662   return g_utf8_get_char (index);
663 }
664
665 static void
666 atk_text_interface_init (AtkTextIface *iface)
667 {
668   iface->get_text = gtk_entry_accessible_get_text;
669   iface->get_character_at_offset = gtk_entry_accessible_get_character_at_offset;
670   iface->get_text_before_offset = gtk_entry_accessible_get_text_before_offset;
671   iface->get_text_at_offset = gtk_entry_accessible_get_text_at_offset;
672   iface->get_text_after_offset = gtk_entry_accessible_get_text_after_offset;
673   iface->get_caret_offset = gtk_entry_accessible_get_caret_offset;
674   iface->set_caret_offset = gtk_entry_accessible_set_caret_offset;
675   iface->get_character_count = gtk_entry_accessible_get_character_count;
676   iface->get_n_selections = gtk_entry_accessible_get_n_selections;
677   iface->get_selection = gtk_entry_accessible_get_selection;
678   iface->add_selection = gtk_entry_accessible_add_selection;
679   iface->remove_selection = gtk_entry_accessible_remove_selection;
680   iface->set_selection = gtk_entry_accessible_set_selection;
681   iface->get_run_attributes = gtk_entry_accessible_get_run_attributes;
682   iface->get_default_attributes = gtk_entry_accessible_get_default_attributes;
683   iface->get_character_extents = gtk_entry_accessible_get_character_extents;
684   iface->get_offset_at_point = gtk_entry_accessible_get_offset_at_point;
685 }
686
687 static void
688 gtk_entry_accessible_set_text_contents (AtkEditableText *text,
689                                         const gchar     *string)
690 {
691   GtkWidget *widget;
692
693   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
694   if (widget == NULL)
695     return;
696
697   if (!gtk_editable_get_editable (GTK_EDITABLE (widget)))
698     return;
699
700   gtk_entry_set_text (GTK_ENTRY (widget), string);
701 }
702
703 static void
704 gtk_entry_accessible_insert_text (AtkEditableText *text,
705                                   const gchar     *string,
706                                   gint             length,
707                                   gint            *position)
708 {
709   GtkWidget *widget;
710   GtkEditable *editable;
711
712   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
713   if (widget == NULL)
714     return;
715
716   editable = GTK_EDITABLE (widget);
717   if (!gtk_editable_get_editable (editable))
718     return;
719
720   gtk_editable_insert_text (editable, string, length, position);
721   gtk_editable_set_position (editable, *position);
722 }
723
724 static void
725 gtk_entry_accessible_copy_text (AtkEditableText *text,
726                                 gint             start_pos,
727                                 gint             end_pos)
728 {
729   GtkWidget *widget;
730   GtkEditable *editable;
731   gchar *str;
732   GtkClipboard *clipboard;
733
734   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
735   if (widget == NULL)
736     return;
737
738   if (!gtk_widget_has_screen (widget))
739     return;
740
741   editable = GTK_EDITABLE (widget);
742   str = gtk_editable_get_chars (editable, start_pos, end_pos);
743   clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
744   gtk_clipboard_set_text (clipboard, str, -1);
745   g_free (str);
746 }
747
748 static void
749 gtk_entry_accessible_cut_text (AtkEditableText *text,
750                                gint             start_pos,
751                                gint             end_pos)
752 {
753   GtkWidget *widget;
754   GtkEditable *editable;
755   gchar *str;
756   GtkClipboard *clipboard;
757
758   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
759   if (widget == NULL)
760     return;
761
762   if (!gtk_widget_has_screen (widget))
763     return;
764
765   editable = GTK_EDITABLE (widget);
766   if (!gtk_editable_get_editable (editable))
767     return;
768
769   str = gtk_editable_get_chars (editable, start_pos, end_pos);
770   clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
771   gtk_clipboard_set_text (clipboard, str, -1);
772   gtk_editable_delete_text (editable, start_pos, end_pos);
773 }
774
775 static void
776 gtk_entry_accessible_delete_text (AtkEditableText *text,
777                                   gint             start_pos,
778                                   gint             end_pos)
779 {
780   GtkWidget *widget;
781   GtkEditable *editable;
782
783   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
784   if (widget == NULL)
785     return;
786
787   editable = GTK_EDITABLE (widget);
788   if (!gtk_editable_get_editable (editable))
789     return;
790
791   gtk_editable_delete_text (editable, start_pos, end_pos);
792 }
793
794 typedef struct
795 {
796   GtkEntry* entry;
797   gint position;
798 } PasteData;
799
800 static void
801 paste_received_cb (GtkClipboard *clipboard,
802                    const gchar  *text,
803                    gpointer      data)
804 {
805   PasteData *paste = data;
806
807   if (text)
808     gtk_editable_insert_text (GTK_EDITABLE (paste->entry), text, -1,
809                               &paste->position);
810
811   g_object_unref (paste->entry);
812   g_free (paste);
813 }
814
815 static void
816 gtk_entry_accessible_paste_text (AtkEditableText *text,
817                                  gint             position)
818 {
819   GtkWidget *widget;
820   GtkEditable *editable;
821   PasteData *paste;
822   GtkClipboard *clipboard;
823
824   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
825   if (widget == NULL)
826     return;
827
828   if (!gtk_widget_has_screen (widget))
829     return;
830
831   editable = GTK_EDITABLE (widget);
832   if (!gtk_editable_get_editable (editable))
833     return;
834
835   paste = g_new0 (PasteData, 1);
836   paste->entry = GTK_ENTRY (widget);
837   paste->position = position;
838
839   g_object_ref (paste->entry);
840   clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
841   gtk_clipboard_request_text (clipboard, paste_received_cb, paste);
842 }
843
844 static void
845 atk_editable_text_interface_init (AtkEditableTextIface *iface)
846 {
847   iface->set_text_contents = gtk_entry_accessible_set_text_contents;
848   iface->insert_text = gtk_entry_accessible_insert_text;
849   iface->copy_text = gtk_entry_accessible_copy_text;
850   iface->cut_text = gtk_entry_accessible_cut_text;
851   iface->delete_text = gtk_entry_accessible_delete_text;
852   iface->paste_text = gtk_entry_accessible_paste_text;
853   iface->set_run_attributes = NULL;
854 }
855
856 /* We connect to GtkEditable::insert-text, since it carries
857  * the information we need. But we delay emitting our own
858  * text_changed::insert signal until the entry has update
859  * all its internal state and emits GtkEntry::changed.
860  */
861 static void
862 insert_text_cb (GtkEditable *editable,
863                 gchar       *new_text,
864                 gint         new_text_length,
865                 gint        *position)
866 {
867   GtkEntryAccessible *accessible;
868
869   if (new_text_length == 0)
870     return;
871
872   accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
873   if (accessible->length_insert == 0)
874     {
875       accessible->position_insert = *position;
876       accessible->length_insert = g_utf8_strlen (new_text, new_text_length);
877     }
878 }
879
880 /* We connect to GtkEditable::delete-text, since it carries
881  * the information we need. But we delay emitting our own
882  * text_changed::delete signal until the entry has update
883  * all its internal state and emits GtkEntry::changed.
884  */
885 static void
886 delete_text_cb (GtkEditable *editable,
887                 gint         start,
888                 gint         end)
889 {
890   GtkEntryAccessible *accessible;
891
892   if (end < 0)
893     {
894       const gchar *text;
895
896       text = gtk_entry_get_text (GTK_ENTRY (editable));
897       end = g_utf8_strlen (text, -1);
898     }
899
900   if (end == start)
901     return;
902
903   accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
904   if (accessible->length_delete == 0)
905     {
906       accessible->position_delete = start;
907       accessible->length_delete = end - start;
908     }
909 }
910
911 /* Note the assumption here: A single ::changed emission
912  * will only collect a single deletion/insertion, and there
913  * won't be multiple insertions or deletions in a single
914  * change.
915  */
916 static void
917 changed_cb (GtkEditable *editable)
918 {
919   GtkEntryAccessible *accessible;
920
921   accessible = GTK_ENTRY_ACCESSIBLE (gtk_widget_get_accessible (GTK_WIDGET (editable)));
922
923   if (accessible->length_delete > 0)
924     {
925       g_signal_emit_by_name (accessible,
926                              "text_changed::delete",
927                              accessible->position_delete,
928                              accessible->length_delete);
929       accessible->length_delete = 0;
930     }
931   if (accessible->length_insert > 0)
932     {
933       g_signal_emit_by_name (accessible,
934                              "text_changed::insert",
935                              accessible->position_insert,
936                              accessible->length_insert);
937       accessible->length_insert = 0;
938     }
939 }
940
941 static gboolean
942 check_for_selection_change (GtkEntryAccessible *accessible,
943                             GtkEntry           *entry)
944 {
945   gboolean ret_val = FALSE;
946   gint start, end;
947
948   if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &start, &end))
949     {
950       if (end != accessible->cursor_position ||
951           start != accessible->selection_bound)
952         /*
953          * This check is here as this function can be called
954          * for notification of selection_bound and current_pos.
955          * The values of current_pos and selection_bound may be the same
956          * for both notifications and we only want to generate one
957          * text_selection_changed signal.
958          */
959         ret_val = TRUE;
960     }
961   else
962     {
963       /* We had a selection */
964       ret_val = (accessible->cursor_position != accessible->selection_bound);
965     }
966
967   accessible->cursor_position = end;
968   accessible->selection_bound = start;
969
970   return ret_val;
971 }
972
973 static gboolean
974 idle_do_action (gpointer data)
975 {
976   GtkEntryAccessible *entry;
977   GtkWidget *widget;
978
979   entry = GTK_ENTRY_ACCESSIBLE (data);
980   entry->action_idle_handler = 0;
981   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (entry));
982   if (widget == NULL /* State is defunct */ ||
983       !gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget))
984     return FALSE;
985
986   gtk_widget_activate (widget);
987
988   return FALSE;
989 }
990
991 static gboolean
992 gtk_entry_accessible_do_action (AtkAction *action,
993                                 gint       i)
994 {
995   GtkEntryAccessible *entry;
996   GtkWidget *widget;
997
998   entry = GTK_ENTRY_ACCESSIBLE (action);
999   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
1000   if (widget == NULL)
1001     return FALSE;
1002
1003   if (!gtk_widget_get_sensitive (widget) || !gtk_widget_get_visible (widget))
1004     return FALSE;
1005
1006   if (i != 0)
1007     return FALSE;
1008
1009   if (entry->action_idle_handler)
1010     return FALSE;
1011
1012   entry->action_idle_handler = gdk_threads_add_idle (idle_do_action, entry);
1013
1014   return TRUE;
1015 }
1016
1017 static gint
1018 gtk_entry_accessible_get_n_actions (AtkAction *action)
1019 {
1020   return 1;
1021 }
1022
1023 static const gchar *
1024 gtk_entry_accessible_get_keybinding (AtkAction *action,
1025                                      gint       i)
1026 {
1027   GtkWidget *widget;
1028   GtkWidget *label;
1029   AtkRelationSet *set;
1030   AtkRelation *relation;
1031   GPtrArray *target;
1032   gpointer target_object;
1033   guint key_val;
1034
1035   if (i != 0)
1036     return NULL;
1037
1038   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (action));
1039   if (widget == NULL)
1040     return NULL;
1041
1042   set = atk_object_ref_relation_set (ATK_OBJECT (action));
1043   if (!set)
1044     return NULL;
1045
1046   label = NULL;
1047   relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY);
1048   if (relation)
1049     {
1050       target = atk_relation_get_target (relation);
1051
1052       target_object = g_ptr_array_index (target, 0);
1053       label = gtk_accessible_get_widget (GTK_ACCESSIBLE (target_object));
1054     }
1055
1056   g_object_unref (set);
1057
1058   if (GTK_IS_LABEL (label))
1059     {
1060       key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label));
1061       if (key_val != GDK_KEY_VoidSymbol)
1062         return gtk_accelerator_name (key_val, GDK_MOD1_MASK);
1063     }
1064
1065   return NULL;
1066 }
1067
1068 static const gchar*
1069 gtk_entry_accessible_action_get_name (AtkAction *action,
1070                                       gint       i)
1071 {
1072   if (i != 0)
1073     return NULL;
1074
1075   return "activate";
1076 }
1077
1078 static void
1079 atk_action_interface_init (AtkActionIface *iface)
1080 {
1081   iface->do_action = gtk_entry_accessible_do_action;
1082   iface->get_n_actions = gtk_entry_accessible_get_n_actions;
1083   iface->get_keybinding = gtk_entry_accessible_get_keybinding;
1084   iface->get_name = gtk_entry_accessible_action_get_name;
1085 }