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