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