1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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 Free
16  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 /*
20  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
21  * file for a list of people on the GTK+ Team.  See the ChangeLog
22  * files for a list of changes.  These files are distributed with
23  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24  */
25 
26 #include <config.h>
27 #include <math.h>
28 #include <string.h>
29 
30 #include "eel-editable-label.h"
31 #include "eel-marshal.h"
32 #include "eel-accessibility.h"
33 #include "eel-gtk-extensions.h"
34 
35 #include <libgail-util/gailmisc.h>
36 
37 #include <glib/gi18n-lib.h>
38 #include <pango/pango.h>
39 #include <gtk/gtk.h>
40 #include <gtk/gtk-a11y.h>
41 #include <gdk/gdkx.h>
42 #include <gdk/gdkkeysyms.h>
43 
44 #ifndef PANGO_CHECK_VERSION
45 #define PANGO_CHECK_VERSION(major, minor, micro)                          \
46      (PANGO_VERSION_MAJOR > (major) ||                                    \
47      (PANGO_VERSION_MAJOR == (major) && PANGO_VERSION_MINOR > (minor)) || \
48      (PANGO_VERSION_MAJOR == (major) && PANGO_VERSION_MINOR == (minor) && \
49       PANGO_VERSION_MICRO >= (micro)))
50 #endif
51 
52 
53 enum
54 {
55     MOVE_CURSOR,
56     POPULATE_POPUP,
57     DELETE_FROM_CURSOR,
58     CUT_CLIPBOARD,
59     COPY_CLIPBOARD,
60     PASTE_CLIPBOARD,
61     TOGGLE_OVERWRITE,
62     LAST_SIGNAL
63 };
64 
65 enum
66 {
67     PROP_0,
68     PROP_TEXT,
69     PROP_JUSTIFY,
70     PROP_WRAP,
71     PROP_CURSOR_POSITION,
72     PROP_SELECTION_BOUND
73 };
74 
75 static guint signals[LAST_SIGNAL] = { 0 };
76 
77 static void     eel_editable_label_editable_init           (GtkEditableInterface  *iface);
78 static void     eel_editable_label_set_property            (GObject               *object,
79         guint                  prop_id,
80         const GValue          *value,
81         GParamSpec            *pspec);
82 static void     eel_editable_label_get_property            (GObject               *object,
83         guint                  prop_id,
84         GValue                *value,
85         GParamSpec            *pspec);
86 static void     eel_editable_label_finalize                (GObject               *object);
87 
88 static void     eel_editable_label_get_preferred_width     (GtkWidget             *widget,
89         						    gint                  *minimum,
90         						    gint                  *natural);
91 static void     eel_editable_label_get_preferred_height    (GtkWidget             *widget,
92         						    gint                  *minimum,
93         						    gint                  *natural);
94 
95 static void     eel_editable_label_size_allocate           (GtkWidget             *widget,
96         GtkAllocation         *allocation);
97 static void     eel_editable_label_state_flags_changed     (GtkWidget             *widget,
98         GtkStateFlags          state);
99 
100 static void     eel_editable_label_style_updated           (GtkWidget             *widget);
101 
102 static void     eel_editable_label_direction_changed       (GtkWidget             *widget,
103         GtkTextDirection       previous_dir);
104 
105 static gint     eel_editable_label_draw                    (GtkWidget             *widget,
106     							    cairo_t               *cr);
107 
108 static void     eel_editable_label_realize                 (GtkWidget             *widget);
109 static void     eel_editable_label_unrealize               (GtkWidget             *widget);
110 static void     eel_editable_label_map                     (GtkWidget             *widget);
111 static void     eel_editable_label_unmap                   (GtkWidget             *widget);
112 static gint     eel_editable_label_button_press            (GtkWidget             *widget,
113         GdkEventButton        *event);
114 static gint     eel_editable_label_button_release          (GtkWidget             *widget,
115         GdkEventButton        *event);
116 static gint     eel_editable_label_motion                  (GtkWidget             *widget,
117         GdkEventMotion        *event);
118 static gint     eel_editable_label_key_press               (GtkWidget             *widget,
119         GdkEventKey           *event);
120 static gint     eel_editable_label_key_release             (GtkWidget             *widget,
121         GdkEventKey           *event);
122 static gint     eel_editable_label_focus_in                (GtkWidget             *widget,
123         GdkEventFocus         *event);
124 static gint     eel_editable_label_focus_out               (GtkWidget             *widget,
125         GdkEventFocus         *event);
126 static GType      eel_editable_label_accessible_get_type   (void);
127 
128 static void     eel_editable_label_commit_cb               (GtkIMContext          *context,
129         const gchar           *str,
130         EelEditableLabel      *label);
131 static void     eel_editable_label_preedit_changed_cb      (GtkIMContext          *context,
132         EelEditableLabel      *label);
133 static gboolean eel_editable_label_retrieve_surrounding_cb (GtkIMContext          *context,
134         EelEditableLabel      *label);
135 static gboolean eel_editable_label_delete_surrounding_cb   (GtkIMContext          *slave,
136         gint                   offset,
137         gint                   n_chars,
138         EelEditableLabel      *label);
139 static void     eel_editable_label_clear_layout            (EelEditableLabel      *label);
140 static void     eel_editable_label_recompute               (EelEditableLabel      *label);
141 static void     eel_editable_label_ensure_layout           (EelEditableLabel      *label,
142         gboolean               include_preedit);
143 static void     eel_editable_label_select_region_index     (EelEditableLabel      *label,
144         gint                   anchor_index,
145         gint                   end_index);
146 static gboolean eel_editable_label_focus                   (GtkWidget             *widget,
147         GtkDirectionType       direction);
148 static void     eel_editable_label_move_cursor             (EelEditableLabel      *label,
149         GtkMovementStep        step,
150         gint                   count,
151         gboolean               extend_selection);
152 static void     eel_editable_label_delete_from_cursor      (EelEditableLabel      *label,
153         GtkDeleteType          type,
154         gint                   count);
155 static void     eel_editable_label_copy_clipboard          (EelEditableLabel      *label);
156 static void     eel_editable_label_cut_clipboard           (EelEditableLabel      *label);
157 static void     eel_editable_label_paste                   (EelEditableLabel      *label,
158         GdkAtom                selection);
159 static void     eel_editable_label_paste_clipboard         (EelEditableLabel      *label);
160 static void     eel_editable_label_select_all              (EelEditableLabel      *label);
161 static void     eel_editable_label_do_popup                (EelEditableLabel      *label,
162         GdkEventButton        *event);
163 static void     eel_editable_label_toggle_overwrite        (EelEditableLabel      *label);
164 static gint     eel_editable_label_move_forward_word       (EelEditableLabel      *label,
165         gint                   start);
166 static gint     eel_editable_label_move_backward_word      (EelEditableLabel      *label,
167         gint                   start);
168 static void     eel_editable_label_reset_im_context        (EelEditableLabel      *label);
169 static void     eel_editable_label_check_cursor_blink      (EelEditableLabel      *label);
170 static void     eel_editable_label_pend_cursor_blink       (EelEditableLabel      *label);
171 
172 /* Editable implementation: */
173 static void     editable_insert_text_emit     (GtkEditable *editable,
174         const gchar *new_text,
175         gint         new_text_length,
176         gint        *position);
177 static void     editable_delete_text_emit     (GtkEditable *editable,
178         gint         start_pos,
179         gint         end_pos);
180 static void     editable_insert_text          (GtkEditable *editable,
181         const gchar *new_text,
182         gint         new_text_length,
183         gint        *position);
184 static void     editable_delete_text          (GtkEditable *editable,
185         gint         start_pos,
186         gint         end_pos);
187 static gchar *  editable_get_chars            (GtkEditable *editable,
188         gint         start_pos,
189         gint         end_pos);
190 static void     editable_set_selection_bounds (GtkEditable *editable,
191         gint         start,
192         gint         end);
193 static gboolean editable_get_selection_bounds (GtkEditable *editable,
194         gint        *start,
195         gint        *end);
196 static void     editable_real_set_position    (GtkEditable *editable,
197         gint         position);
198 static gint     editable_get_position         (GtkEditable *editable);
199 
200 G_DEFINE_TYPE_WITH_CODE (EelEditableLabel, eel_editable_label, GTK_TYPE_WIDGET,
201 
202                          G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, eel_editable_label_editable_init));
203 
204 static void
add_move_binding(GtkBindingSet * binding_set,guint keyval,guint modmask,GtkMovementStep step,gint count)205 add_move_binding (GtkBindingSet  *binding_set,
206                   guint           keyval,
207                   guint           modmask,
208                   GtkMovementStep step,
209                   gint            count)
210 {
211     g_assert ((modmask & GDK_SHIFT_MASK) == 0);
212 
213     gtk_binding_entry_add_signal (binding_set, keyval, modmask,
214                                   "move_cursor", 3,
215                                   G_TYPE_ENUM, step,
216                                   G_TYPE_INT, count,
217                                   G_TYPE_BOOLEAN, FALSE);
218 
219     /* Selection-extending version */
220     gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
221                                   "move_cursor", 3,
222                                   G_TYPE_ENUM, step,
223                                   G_TYPE_INT, count,
224                                   G_TYPE_BOOLEAN, TRUE);
225 }
226 
227 static void
eel_editable_label_class_init(EelEditableLabelClass * class)228 eel_editable_label_class_init (EelEditableLabelClass *class)
229 {
230     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
231     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
232     GtkBindingSet *binding_set;
233 
234     gobject_class->set_property = eel_editable_label_set_property;
235     gobject_class->get_property = eel_editable_label_get_property;
236     gobject_class->finalize = eel_editable_label_finalize;
237 
238 
239     widget_class->get_preferred_width = eel_editable_label_get_preferred_width;
240     widget_class->get_preferred_height = eel_editable_label_get_preferred_height;
241     widget_class->size_allocate = eel_editable_label_size_allocate;
242     widget_class->state_flags_changed = eel_editable_label_state_flags_changed;
243     widget_class->style_updated = eel_editable_label_style_updated;
244     widget_class->direction_changed = eel_editable_label_direction_changed;
245     widget_class->draw = eel_editable_label_draw;
246     widget_class->realize = eel_editable_label_realize;
247     widget_class->unrealize = eel_editable_label_unrealize;
248     widget_class->map = eel_editable_label_map;
249     widget_class->unmap = eel_editable_label_unmap;
250     widget_class->button_press_event = eel_editable_label_button_press;
251     widget_class->button_release_event = eel_editable_label_button_release;
252     widget_class->motion_notify_event = eel_editable_label_motion;
253     widget_class->focus = eel_editable_label_focus;
254     widget_class->key_press_event = eel_editable_label_key_press;
255     widget_class->key_release_event = eel_editable_label_key_release;
256     widget_class->focus_in_event = eel_editable_label_focus_in;
257     widget_class->focus_out_event = eel_editable_label_focus_out;
258     gtk_widget_class_set_accessible_type (widget_class, eel_editable_label_accessible_get_type ());
259 
260 
261     class->move_cursor = eel_editable_label_move_cursor;
262     class->delete_from_cursor = eel_editable_label_delete_from_cursor;
263     class->copy_clipboard = eel_editable_label_copy_clipboard;
264     class->cut_clipboard = eel_editable_label_cut_clipboard;
265     class->paste_clipboard = eel_editable_label_paste_clipboard;
266     class->toggle_overwrite = eel_editable_label_toggle_overwrite;
267 
268     signals[MOVE_CURSOR] =
269         g_signal_new ("move_cursor",
270                       G_TYPE_FROM_CLASS (class),
271                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
272                       G_STRUCT_OFFSET (EelEditableLabelClass, move_cursor),
273                       NULL, NULL,
274                       eel_marshal_VOID__ENUM_INT_BOOLEAN,
275                       G_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT, G_TYPE_BOOLEAN);
276 
277     signals[COPY_CLIPBOARD] =
278         g_signal_new ("copy_clipboard",
279                       G_TYPE_FROM_CLASS (class),
280                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
281                       G_STRUCT_OFFSET  (EelEditableLabelClass, copy_clipboard),
282                       NULL, NULL,
283                       g_cclosure_marshal_VOID__VOID,
284                       G_TYPE_NONE, 0);
285 
286     signals[POPULATE_POPUP] =
287         g_signal_new ("populate_popup",
288                       G_TYPE_FROM_CLASS (class),
289                       G_SIGNAL_RUN_LAST,
290                       G_STRUCT_OFFSET (EelEditableLabelClass, populate_popup),
291                       NULL, NULL,
292                       g_cclosure_marshal_VOID__OBJECT,
293                       G_TYPE_NONE, 1, GTK_TYPE_MENU);
294 
295     signals[DELETE_FROM_CURSOR] =
296         g_signal_new ("delete_from_cursor",
297                       G_TYPE_FROM_CLASS (class),
298                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
299                       G_STRUCT_OFFSET (EelEditableLabelClass, delete_from_cursor),
300                       NULL, NULL,
301                       eel_marshal_VOID__ENUM_INT,
302                       G_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, G_TYPE_INT);
303 
304     signals[CUT_CLIPBOARD] =
305         g_signal_new ("cut_clipboard",
306                       G_TYPE_FROM_CLASS (class),
307                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
308                       G_STRUCT_OFFSET (EelEditableLabelClass, cut_clipboard),
309                       NULL, NULL,
310                       g_cclosure_marshal_VOID__VOID,
311                       G_TYPE_NONE, 0);
312 
313     signals[PASTE_CLIPBOARD] =
314         g_signal_new ("paste_clipboard",
315                       G_TYPE_FROM_CLASS (class),
316                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
317                       G_STRUCT_OFFSET (EelEditableLabelClass, paste_clipboard),
318                       NULL, NULL,
319                       g_cclosure_marshal_VOID__VOID,
320                       G_TYPE_NONE, 0);
321 
322     signals[TOGGLE_OVERWRITE] =
323         g_signal_new ("toggle_overwrite",
324                       G_TYPE_FROM_CLASS (class),
325                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
326                       G_STRUCT_OFFSET (EelEditableLabelClass, toggle_overwrite),
327                       NULL, NULL,
328                       g_cclosure_marshal_VOID__VOID,
329                       G_TYPE_NONE, 0);
330 
331 
332     g_object_class_install_property (gobject_class,
333                                      PROP_TEXT,
334                                      g_param_spec_string ("text",
335                                              _("Text"),
336                                              _("The text of the label."),
337                                              NULL,
338                                              G_PARAM_READWRITE));
339     g_object_class_install_property (gobject_class,
340                                      PROP_JUSTIFY,
341                                      g_param_spec_enum ("justify",
342                                              _("Justification"),
343                                              _("The alignment of the lines in the text of the label relative to each other. This does NOT affect the alignment of the label within its allocation. See GtkMisc::xalign for that."),
344                                              GTK_TYPE_JUSTIFICATION,
345                                              GTK_JUSTIFY_LEFT,
346                                              G_PARAM_READWRITE));
347 
348     g_object_class_install_property (gobject_class,
349                                      PROP_WRAP,
350                                      g_param_spec_boolean ("wrap",
351                                              _("Line wrap"),
352                                              _("If set, wrap lines if the text becomes too wide."),
353                                              FALSE,
354                                              G_PARAM_READWRITE));
355 
356     g_object_class_install_property (gobject_class,
357                                      PROP_CURSOR_POSITION,
358                                      g_param_spec_int ("cursor_position",
359                                              _("Cursor Position"),
360                                              _("The current position of the insertion cursor in chars."),
361                                              0,
362                                              G_MAXINT,
363                                              0,
364                                              G_PARAM_READABLE));
365 
366     g_object_class_install_property (gobject_class,
367                                      PROP_SELECTION_BOUND,
368                                      g_param_spec_int ("selection_bound",
369                                              _("Selection Bound"),
370                                              _("The position of the opposite end of the selection from the cursor in chars."),
371                                              0,
372                                              G_MAXINT,
373                                              0,
374                                              G_PARAM_READABLE));
375 
376     /*
377      * Key bindings
378      */
379 
380     binding_set = gtk_binding_set_by_class (class);
381 
382     /* Moving the insertion point */
383     add_move_binding (binding_set, GDK_KEY_Right, 0,
384                       GTK_MOVEMENT_VISUAL_POSITIONS, 1);
385 
386     add_move_binding (binding_set, GDK_KEY_Left, 0,
387                       GTK_MOVEMENT_VISUAL_POSITIONS, -1);
388 
389     add_move_binding (binding_set, GDK_KEY_KP_Right, 0,
390                       GTK_MOVEMENT_VISUAL_POSITIONS, 1);
391 
392     add_move_binding (binding_set, GDK_KEY_KP_Left, 0,
393                       GTK_MOVEMENT_VISUAL_POSITIONS, -1);
394 
395     add_move_binding (binding_set, GDK_KEY_f, GDK_CONTROL_MASK,
396                       GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
397 
398     add_move_binding (binding_set, GDK_KEY_b, GDK_CONTROL_MASK,
399                       GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
400 
401     add_move_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
402                       GTK_MOVEMENT_WORDS, 1);
403 
404     add_move_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
405                       GTK_MOVEMENT_WORDS, -1);
406 
407     add_move_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
408                       GTK_MOVEMENT_WORDS, 1);
409 
410     add_move_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
411                       GTK_MOVEMENT_WORDS, -1);
412 
413     add_move_binding (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
414                       GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
415 
416     add_move_binding (binding_set, GDK_KEY_e, GDK_CONTROL_MASK,
417                       GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
418 
419     add_move_binding (binding_set, GDK_KEY_f, GDK_MOD1_MASK,
420                       GTK_MOVEMENT_WORDS, 1);
421 
422     add_move_binding (binding_set, GDK_KEY_b, GDK_MOD1_MASK,
423                       GTK_MOVEMENT_WORDS, -1);
424 
425     add_move_binding (binding_set, GDK_KEY_Home, 0,
426                       GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
427 
428     add_move_binding (binding_set, GDK_KEY_End, 0,
429                       GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
430 
431     add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
432                       GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
433 
434     add_move_binding (binding_set, GDK_KEY_KP_End, 0,
435                       GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
436 
437     add_move_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
438                       GTK_MOVEMENT_BUFFER_ENDS, -1);
439 
440     add_move_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
441                       GTK_MOVEMENT_BUFFER_ENDS, 1);
442 
443     add_move_binding (binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
444                       GTK_MOVEMENT_BUFFER_ENDS, -1);
445 
446     add_move_binding (binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
447                       GTK_MOVEMENT_BUFFER_ENDS, 1);
448 
449     add_move_binding (binding_set, GDK_KEY_Up, 0,
450                       GTK_MOVEMENT_DISPLAY_LINES, -1);
451 
452     add_move_binding (binding_set, GDK_KEY_KP_Up, 0,
453                       GTK_MOVEMENT_DISPLAY_LINES, -1);
454 
455     add_move_binding (binding_set, GDK_KEY_Down, 0,
456                       GTK_MOVEMENT_DISPLAY_LINES, 1);
457 
458     add_move_binding (binding_set, GDK_KEY_KP_Down, 0,
459                       GTK_MOVEMENT_DISPLAY_LINES, 1);
460 
461     /* Select all
462      */
463     gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
464                                   "move_cursor", 3,
465                                   GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS,
466                                   G_TYPE_INT, -1,
467                                   G_TYPE_BOOLEAN, FALSE);
468     gtk_binding_entry_add_signal (binding_set, GDK_KEY_a, GDK_CONTROL_MASK,
469                                   "move_cursor", 3,
470                                   GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS,
471                                   G_TYPE_INT, 1,
472                                   G_TYPE_BOOLEAN, TRUE);
473 
474     /* Deleting text */
475     gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, 0,
476                                   "delete_from_cursor", 2,
477                                   G_TYPE_ENUM, GTK_DELETE_CHARS,
478                                   G_TYPE_INT, 1);
479 
480     gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, 0,
481                                   "delete_from_cursor", 2,
482                                   G_TYPE_ENUM, GTK_DELETE_CHARS,
483                                   G_TYPE_INT, 1);
484 
485     gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, 0,
486                                   "delete_from_cursor", 2,
487                                   G_TYPE_ENUM, GTK_DELETE_CHARS,
488                                   G_TYPE_INT, -1);
489 
490     /* Make this do the same as Backspace, to help with mis-typing */
491     gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_SHIFT_MASK,
492                                   "delete_from_cursor", 2,
493                                   G_TYPE_ENUM, GTK_DELETE_CHARS,
494                                   G_TYPE_INT, -1);
495 
496     gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_CONTROL_MASK,
497                                   "delete_from_cursor", 2,
498                                   G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
499                                   G_TYPE_INT, 1);
500 
501     gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_CONTROL_MASK,
502                                   "delete_from_cursor", 2,
503                                   G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
504                                   G_TYPE_INT, 1);
505 
506     gtk_binding_entry_add_signal (binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK,
507                                   "delete_from_cursor", 2,
508                                   G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
509                                   G_TYPE_INT, -1);
510 
511     /* Cut/copy/paste */
512 
513     gtk_binding_entry_add_signal (binding_set, GDK_KEY_x, GDK_CONTROL_MASK,
514                                   "cut_clipboard", 0);
515     gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK,
516                                   "copy_clipboard", 0);
517     gtk_binding_entry_add_signal (binding_set, GDK_KEY_v, GDK_CONTROL_MASK,
518                                   "paste_clipboard", 0);
519 
520     gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_SHIFT_MASK,
521                                   "cut_clipboard", 0);
522     gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_CONTROL_MASK,
523                                   "copy_clipboard", 0);
524     gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_SHIFT_MASK,
525                                   "paste_clipboard", 0);
526 
527     /* Overwrite */
528     gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, 0,
529                                   "toggle_overwrite", 0);
530     gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Insert, 0,
531                                   "toggle_overwrite", 0);
532 }
533 
534 static void
eel_editable_label_editable_init(GtkEditableInterface * iface)535 eel_editable_label_editable_init (GtkEditableInterface *iface)
536 {
537     iface->do_insert_text = editable_insert_text_emit;
538     iface->do_delete_text = editable_delete_text_emit;
539     iface->insert_text = editable_insert_text;
540     iface->delete_text = editable_delete_text;
541     iface->get_chars = editable_get_chars;
542     iface->set_selection_bounds = editable_set_selection_bounds;
543     iface->get_selection_bounds = editable_get_selection_bounds;
544     iface->set_position = editable_real_set_position;
545     iface->get_position = editable_get_position;
546 }
547 
548 
549 static void
eel_editable_label_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)550 eel_editable_label_set_property (GObject      *object,
551                                  guint         prop_id,
552                                  const GValue *value,
553                                  GParamSpec   *pspec)
554 {
555     EelEditableLabel *label;
556 
557     label = EEL_EDITABLE_LABEL (object);
558 
559     switch (prop_id)
560     {
561     case PROP_TEXT:
562         eel_editable_label_set_text (label, g_value_get_string (value));
563         break;
564     case PROP_JUSTIFY:
565         eel_editable_label_set_justify (label, g_value_get_enum (value));
566         break;
567     case PROP_WRAP:
568         eel_editable_label_set_line_wrap (label, g_value_get_boolean (value));
569         break;
570     default:
571         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
572         break;
573     }
574 }
575 
576 static void
eel_editable_label_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)577 eel_editable_label_get_property (GObject     *object,
578                                  guint        prop_id,
579                                  GValue      *value,
580                                  GParamSpec  *pspec)
581 {
582     EelEditableLabel *label;
583     gint offset;
584 
585     label = EEL_EDITABLE_LABEL (object);
586 
587     switch (prop_id)
588     {
589     case PROP_TEXT:
590         g_value_set_string (value, label->text);
591         break;
592     case PROP_JUSTIFY:
593         g_value_set_enum (value, label->jtype);
594         break;
595     case PROP_WRAP:
596         g_value_set_boolean (value, label->wrap);
597         break;
598     case PROP_CURSOR_POSITION:
599         offset = g_utf8_pointer_to_offset (label->text,
600                                            label->text + label->selection_end);
601         g_value_set_int (value, offset);
602         break;
603     case PROP_SELECTION_BOUND:
604         offset = g_utf8_pointer_to_offset (label->text,
605                                            label->text + label->selection_anchor);
606         g_value_set_int (value, offset);
607         break;
608 
609     default:
610         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
611         break;
612     }
613 }
614 
615 static void
eel_editable_label_init(EelEditableLabel * label)616 eel_editable_label_init (EelEditableLabel *label)
617 {
618     label->jtype = GTK_JUSTIFY_LEFT;
619     label->wrap = FALSE;
620     label->wrap_mode = PANGO_WRAP_WORD;
621 
622     label->layout = NULL;
623     label->text_size = 1;
624     label->text = g_malloc (label->text_size);
625     label->text[0] = '\0';
626     label->n_bytes = 0;
627 
628     gtk_widget_set_can_focus (GTK_WIDGET (label), TRUE);
629 
630     gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (label)),
631                                  GTK_STYLE_CLASS_ENTRY);
632 
633     /* This object is completely private. No external entity can gain a reference
634     * to it; so we create it here and destroy it in finalize().
635     */
636     label->im_context = gtk_im_multicontext_new ();
637 
638     g_signal_connect (G_OBJECT (label->im_context), "commit",
639                       G_CALLBACK (eel_editable_label_commit_cb), label);
640     g_signal_connect (G_OBJECT (label->im_context), "preedit_changed",
641                       G_CALLBACK (eel_editable_label_preedit_changed_cb), label);
642     g_signal_connect (G_OBJECT (label->im_context), "retrieve_surrounding",
643                       G_CALLBACK (eel_editable_label_retrieve_surrounding_cb), label);
644     g_signal_connect (G_OBJECT (label->im_context), "delete_surrounding",
645                       G_CALLBACK (eel_editable_label_delete_surrounding_cb), label);
646 }
647 
648 /**
649  * eel_editable_label_new:
650  * @str: The text of the label
651  *
652  * Creates a new label with the given text inside it. You can
653  * pass %NULL to get an empty label widget.
654  *
655  * Return value: the new #EelEditableLabel
656  **/
657 GtkWidget*
eel_editable_label_new(const gchar * str)658 eel_editable_label_new (const gchar *str)
659 {
660     EelEditableLabel *label;
661 
662     label = g_object_new (EEL_TYPE_EDITABLE_LABEL, NULL);
663 
664     if (str && *str)
665         eel_editable_label_set_text (label, str);
666 
667     return GTK_WIDGET (label);
668 }
669 
670 /**
671  * eel_editable_label_set_text:
672  * @label: a #EelEditableLabel
673  * @str: The text you want to set.
674  *
675  * Sets the text within the #EelEditableLabel widget.  It overwrites any text that
676  * was there before.
677  *
678  * This will also clear any previously set mnemonic accelerators.
679  **/
680 void
eel_editable_label_set_text(EelEditableLabel * label,const gchar * str)681 eel_editable_label_set_text (EelEditableLabel *label,
682                              const gchar *str)
683 {
684     GtkEditable *editable;
685     int tmp_pos;
686 
687     g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
688     g_return_if_fail (str != NULL);
689 
690     if (strcmp (label->text, str) == 0)
691         return;
692 
693     editable = GTK_EDITABLE (label);
694     gtk_editable_delete_text (editable, 0, -1);
695     tmp_pos = 0;
696     gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
697 }
698 
699 /**
700  * eel_editable_label_get_text:
701  * @label: a #EelEditableLabel
702  *
703  * Fetches the text from a label widget, as displayed on the
704  * screen. This does not include any embedded underlines
705  * indicating mnemonics or Pango markup. (See eel_editable_label_get_label())
706  *
707  * Return value: the text in the label widget. This is the internal
708  *   string used by the label, and must not be modified.
709  **/
eel_editable_label_get_text(EelEditableLabel * label)710 const gchar* eel_editable_label_get_text(EelEditableLabel* label)
711 {
712     g_return_val_if_fail(EEL_IS_EDITABLE_LABEL(label), NULL);
713 
714     return label->text;
715 }
716 
717 /**
718  * eel_editable_label_set_justify:
719  * @label: a #EelEditableLabel
720  * @jtype: a #GtkJustification
721  *
722  * Sets the alignment of the lines in the text of the label relative to
723  * each other.  %GTK_JUSTIFY_LEFT is the default value when the
724  * widget is first created with eel_editable_label_new(). If you instead want
725  * to set the alignment of the label as a whole, use
726  * gtk_misc_set_alignment() instead. eel_editable_label_set_justify() has no
727  * effect on labels containing only a single line.
728  **/
729 void
eel_editable_label_set_justify(EelEditableLabel * label,GtkJustification jtype)730 eel_editable_label_set_justify (EelEditableLabel        *label,
731                                 GtkJustification jtype)
732 {
733     g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
734     g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
735 
736     if ((GtkJustification) label->jtype != jtype)
737     {
738         label->jtype = jtype;
739 
740         /* No real need to be this drastic, but easier than duplicating the code */
741         eel_editable_label_recompute (label);
742 
743         g_object_notify (G_OBJECT (label), "justify");
744         gtk_widget_queue_resize (GTK_WIDGET (label));
745     }
746 }
747 
748 /**
749  * eel_editable_label_get_justify:
750  * @label: a #EelEditableLabel
751  *
752  * Returns the justification of the label. See eel_editable_label_set_justify ().
753  *
754  * Return value: #GtkJustification
755  **/
756 GtkJustification
eel_editable_label_get_justify(EelEditableLabel * label)757 eel_editable_label_get_justify (EelEditableLabel *label)
758 {
759     g_return_val_if_fail (EEL_IS_EDITABLE_LABEL (label), 0);
760 
761     return label->jtype;
762 }
763 
764 void
eel_editable_label_set_draw_outline(EelEditableLabel * label,gboolean draw_outline)765 eel_editable_label_set_draw_outline (EelEditableLabel *label,
766                                      gboolean          draw_outline)
767 {
768     draw_outline = draw_outline != FALSE;
769 
770     if (label->draw_outline != draw_outline)
771     {
772         label->draw_outline = draw_outline;
773 
774         gtk_widget_queue_draw (GTK_WIDGET (label));
775     }
776 
777 }
778 
779 
780 /**
781  * eel_editable_label_set_line_wrap:
782  * @label: a #EelEditableLabel
783  * @wrap: the setting
784  *
785  * Toggles line wrapping within the #EelEditableLabel widget.  %TRUE makes it break
786  * lines if text exceeds the widget's size.  %FALSE lets the text get cut off
787  * by the edge of the widget if it exceeds the widget size.
788  **/
789 void
eel_editable_label_set_line_wrap(EelEditableLabel * label,gboolean wrap)790 eel_editable_label_set_line_wrap (EelEditableLabel *label,
791                                   gboolean  wrap)
792 {
793     g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
794 
795     wrap = wrap != FALSE;
796 
797     if (label->wrap != wrap)
798     {
799         label->wrap = wrap;
800         g_object_notify (G_OBJECT (label), "wrap");
801 
802         gtk_widget_queue_resize (GTK_WIDGET (label));
803     }
804 }
805 
806 
807 void
eel_editable_label_set_line_wrap_mode(EelEditableLabel * label,PangoWrapMode mode)808 eel_editable_label_set_line_wrap_mode (EelEditableLabel *label,
809                                        PangoWrapMode     mode)
810 {
811     g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
812 
813     if (label->wrap_mode != mode)
814     {
815         label->wrap_mode = mode;
816 
817         gtk_widget_queue_resize (GTK_WIDGET (label));
818     }
819 
820 }
821 
822 
823 /**
824  * eel_editable_label_get_line_wrap:
825  * @label: a #EelEditableLabel
826  *
827  * Returns whether lines in the label are automatically wrapped. See eel_editable_label_set_line_wrap ().
828  *
829  * Return value: %TRUE if the lines of the label are automatically wrapped.
830  */
831 gboolean
eel_editable_label_get_line_wrap(EelEditableLabel * label)832 eel_editable_label_get_line_wrap (EelEditableLabel *label)
833 {
834     g_return_val_if_fail (EEL_IS_EDITABLE_LABEL (label), FALSE);
835 
836     return label->wrap;
837 }
838 
839 PangoFontDescription *
eel_editable_label_get_font_description(EelEditableLabel * label)840 eel_editable_label_get_font_description (EelEditableLabel *label)
841 {
842     if (label->font_desc)
843         return pango_font_description_copy (label->font_desc);
844 
845     return NULL;
846 }
847 
848 void
eel_editable_label_set_font_description(EelEditableLabel * label,const PangoFontDescription * desc)849 eel_editable_label_set_font_description (EelEditableLabel *label,
850         const PangoFontDescription *desc)
851 {
852     if (label->font_desc)
853         pango_font_description_free (label->font_desc);
854 
855     if (desc)
856         label->font_desc = pango_font_description_copy (desc);
857     else
858         label->font_desc = NULL;
859 
860     eel_editable_label_clear_layout (label);
861 }
862 
863 static void
eel_editable_label_finalize(GObject * object)864 eel_editable_label_finalize (GObject *object)
865 {
866     EelEditableLabel *label;
867 
868     g_assert (EEL_IS_EDITABLE_LABEL (object));
869 
870     label = EEL_EDITABLE_LABEL (object);
871 
872     if (label->font_desc)
873     {
874         pango_font_description_free (label->font_desc);
875         label->font_desc = NULL;
876     }
877 
878     g_object_unref (G_OBJECT (label->im_context));
879     label->im_context = NULL;
880 
881     g_free (label->text);
882     label->text = NULL;
883 
884     if (label->layout)
885     {
886         g_object_unref (G_OBJECT (label->layout));
887         label->layout = NULL;
888     }
889 
890     G_OBJECT_CLASS (eel_editable_label_parent_class)->finalize (object);
891 }
892 
893 static void
eel_editable_label_clear_layout(EelEditableLabel * label)894 eel_editable_label_clear_layout (EelEditableLabel *label)
895 {
896     if (label->layout)
897     {
898         g_object_unref (G_OBJECT (label->layout));
899         label->layout = NULL;
900     }
901 }
902 
903 static void
eel_editable_label_recompute(EelEditableLabel * label)904 eel_editable_label_recompute (EelEditableLabel *label)
905 {
906     eel_editable_label_clear_layout (label);
907     eel_editable_label_check_cursor_blink (label);
908 }
909 
910 typedef struct _LabelWrapWidth LabelWrapWidth;
911 struct _LabelWrapWidth
912 {
913     gint width;
914     PangoFontDescription *font_desc;
915 };
916 
917 static void
label_wrap_width_free(gpointer data)918 label_wrap_width_free (gpointer data)
919 {
920     LabelWrapWidth *wrap_width = data;
921     pango_font_description_free (wrap_width->font_desc);
922     g_free (wrap_width);
923 }
924 
925 static gint
get_label_wrap_width(EelEditableLabel * label)926 get_label_wrap_width (EelEditableLabel *label)
927 {
928     PangoLayout *layout;
929     GtkStyleContext *style = gtk_widget_get_style_context (GTK_WIDGET (label));
930     PangoFontDescription *desc;
931 
932     LabelWrapWidth *wrap_width = g_object_get_data (G_OBJECT (style), "gtk-label-wrap-width");
933     if (!wrap_width)
934     {
935         wrap_width = g_new0 (LabelWrapWidth, 1);
936         g_object_set_data_full (G_OBJECT (style), "gtk-label-wrap-width",
937                                 wrap_width, label_wrap_width_free);
938     }
939 
940     gtk_style_context_get (style, gtk_widget_get_state_flags (GTK_WIDGET (label)),
941                            GTK_STYLE_PROPERTY_FONT, &desc,
942                            NULL);
943 
944     if (wrap_width->font_desc && pango_font_description_equal (wrap_width->font_desc, desc))
945         goto out;
946 
947     if (wrap_width->font_desc)
948         pango_font_description_free (wrap_width->font_desc);
949 
950     wrap_width->font_desc = pango_font_description_copy (desc);
951 
952     layout = gtk_widget_create_pango_layout (GTK_WIDGET (label),
953              "This long string gives a good enough length for any line to have.");
954     pango_layout_get_size (layout, &wrap_width->width, NULL);
955     g_object_unref (layout);
956 
957     out:
958     pango_font_description_free (desc);
959     return wrap_width->width;
960 }
961 
962 static void
eel_editable_label_ensure_layout(EelEditableLabel * label,gboolean include_preedit)963 eel_editable_label_ensure_layout (EelEditableLabel *label,
964                                   gboolean        include_preedit)
965 {
966     GtkWidget *widget;
967     PangoRectangle logical_rect;
968 
969     /* Normalize for comparisons */
970     include_preedit = include_preedit != 0;
971 
972     if (label->preedit_length > 0 &&
973             include_preedit != label->layout_includes_preedit)
974         eel_editable_label_clear_layout (label);
975 
976     widget = GTK_WIDGET (label);
977 
978     if (label->layout == NULL)
979     {
980         gchar *preedit_string = NULL;
981         gint preedit_length = 0;
982         PangoAttrList *preedit_attrs = NULL;
983         PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
984         PangoAttrList *tmp_attrs = pango_attr_list_new ();
985 
986         if (include_preedit)
987         {
988             gtk_im_context_get_preedit_string (label->im_context,
989                                                &preedit_string, &preedit_attrs, NULL);
990             preedit_length = label->preedit_length;
991         }
992 
993         if (preedit_length)
994         {
995             GString *tmp_string = g_string_new (NULL);
996 
997             g_string_prepend_len (tmp_string, label->text, label->n_bytes);
998             g_string_insert (tmp_string, label->selection_anchor, preedit_string);
999 
1000             label->layout = gtk_widget_create_pango_layout (widget, tmp_string->str);
1001 
1002             pango_attr_list_splice (tmp_attrs, preedit_attrs,
1003                                     label->selection_anchor, preedit_length);
1004 
1005             g_string_free (tmp_string, TRUE);
1006         }
1007         else
1008         {
1009             label->layout = gtk_widget_create_pango_layout (widget, label->text);
1010         }
1011         label->layout_includes_preedit = include_preedit;
1012 
1013         if (label->font_desc != NULL)
1014             pango_layout_set_font_description (label->layout, label->font_desc);
1015 
1016         #if PANGO_CHECK_VERSION (1, 44, 0)
1017         pango_attr_list_insert (tmp_attrs, pango_attr_insert_hyphens_new (FALSE));
1018         #endif
1019         pango_layout_set_attributes (label->layout, tmp_attrs);
1020 
1021         if (preedit_string)
1022             g_free (preedit_string);
1023         if (preedit_attrs)
1024             pango_attr_list_unref (preedit_attrs);
1025         pango_attr_list_unref (tmp_attrs);
1026 
1027         switch (label->jtype)
1028         {
1029         case GTK_JUSTIFY_LEFT:
1030             align = PANGO_ALIGN_LEFT;
1031             break;
1032         case GTK_JUSTIFY_RIGHT:
1033             align = PANGO_ALIGN_RIGHT;
1034             break;
1035         case GTK_JUSTIFY_CENTER:
1036             align = PANGO_ALIGN_CENTER;
1037             break;
1038         case GTK_JUSTIFY_FILL:
1039             /* FIXME: This just doesn't work to do this */
1040             align = PANGO_ALIGN_LEFT;
1041             pango_layout_set_justify (label->layout, TRUE);
1042             break;
1043         default:
1044             g_assert_not_reached();
1045         }
1046 
1047         pango_layout_set_alignment (label->layout, align);
1048 
1049         if (label->wrap)
1050         {
1051             gint longest_paragraph;
1052             gint width, height;
1053             gint set_width;
1054 
1055             gtk_widget_get_size_request (widget, &set_width, NULL);
1056             if (set_width > 0)
1057                 pango_layout_set_width (label->layout, set_width * PANGO_SCALE);
1058             else
1059             {
1060                 gint wrap_width;
1061                 gint scale;
1062 
1063                 pango_layout_set_width (label->layout, -1);
1064                 pango_layout_get_extents (label->layout, NULL, &logical_rect);
1065 
1066                 width = logical_rect.width;
1067 
1068                 /* Try to guess a reasonable maximum width */
1069                 longest_paragraph = width;
1070 
1071                 wrap_width = get_label_wrap_width (label);
1072                 scale = gtk_widget_get_scale_factor (widget);
1073                 width = MIN (width, wrap_width);
1074                 width = MIN (width,
1075                              PANGO_SCALE * (WidthOfScreen (gdk_x11_screen_get_xscreen (gdk_screen_get_default ())) / scale + 1) / 2);
1076 
1077                 pango_layout_set_width (label->layout, width);
1078                 pango_layout_get_extents (label->layout, NULL, &logical_rect);
1079                 width = logical_rect.width;
1080                 height = logical_rect.height;
1081 
1082                 /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
1083                  * so we try short search for a narrower width that leaves us with the same height
1084                  */
1085                 if (longest_paragraph > 0)
1086                 {
1087                     gint nlines, perfect_width;
1088 
1089                     nlines = pango_layout_get_line_count (label->layout);
1090                     perfect_width = (longest_paragraph + nlines - 1) / nlines;
1091 
1092                     if (perfect_width < width)
1093                     {
1094                         pango_layout_set_width (label->layout, perfect_width);
1095                         pango_layout_get_extents (label->layout, NULL, &logical_rect);
1096 
1097                         if (logical_rect.height <= height)
1098                             width = logical_rect.width;
1099                         else
1100                         {
1101                             gint mid_width = (perfect_width + width) / 2;
1102 
1103                             if (mid_width > perfect_width)
1104                             {
1105                                 pango_layout_set_width (label->layout, mid_width);
1106                                 pango_layout_get_extents (label->layout, NULL, &logical_rect);
1107 
1108                                 if (logical_rect.height <= height)
1109                                     width = logical_rect.width;
1110                             }
1111                         }
1112                     }
1113                 }
1114                 pango_layout_set_width (label->layout, width);
1115             }
1116             pango_layout_set_wrap (label->layout, label->wrap_mode);
1117         }
1118         else		/* !label->wrap */
1119             pango_layout_set_width (label->layout, -1);
1120     }
1121 }
1122 
1123 static void
eel_editable_label_size_request(GtkWidget * widget,GtkRequisition * requisition)1124 eel_editable_label_size_request (GtkWidget      *widget,
1125                                  GtkRequisition *requisition)
1126 {
1127     EelEditableLabel *label;
1128     gint width, height;
1129     PangoRectangle logical_rect;
1130     gint set_width;
1131     gint xpad, ypad;
1132     gint margin_start, margin_end, margin_top, margin_bottom;
1133 
1134 
1135     g_assert (EEL_IS_EDITABLE_LABEL (widget));
1136     g_assert (requisition != NULL);
1137 
1138     label = EEL_EDITABLE_LABEL (widget);
1139 
1140     /*
1141      * If word wrapping is on, then the height requisition can depend
1142      * on:
1143      *
1144      *   - Any width set on the widget via gtk_widget_set_size_request().
1145      *   - The padding of the widget (xpad, set by gtk_misc_set_padding)
1146      *
1147      * Instead of trying to detect changes to these quantities, if we
1148      * are wrapping, we just rewrap for each size request. Since
1149      * size requisitions are cached by the GTK+ core, this is not
1150      * expensive.
1151      */
1152 
1153     if (label->wrap)
1154         eel_editable_label_recompute (label);
1155 
1156     eel_editable_label_ensure_layout (label, TRUE);
1157 
1158     margin_start = gtk_widget_get_margin_start (widget);
1159     margin_end = gtk_widget_get_margin_end (widget);
1160     margin_top = gtk_widget_get_margin_top (widget);
1161     margin_bottom = gtk_widget_get_margin_bottom (widget);
1162 
1163     xpad = margin_start + margin_end;
1164     ypad = margin_top + margin_bottom;
1165     width = xpad * 2;
1166     height = ypad * 2;
1167 
1168     pango_layout_get_extents (label->layout, NULL, &logical_rect);
1169 
1170     gtk_widget_get_size_request (widget, &set_width, NULL);
1171     if (label->wrap && set_width > 0)
1172         width += set_width;
1173     else
1174         width += PANGO_PIXELS (logical_rect.width);
1175 
1176     height += PANGO_PIXELS (logical_rect.height);
1177 
1178     requisition->width = width;
1179     requisition->height = height;
1180 }
1181 
1182 static void
eel_editable_label_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)1183 eel_editable_label_get_preferred_width (GtkWidget *widget,
1184                                         gint      *minimum,
1185                                         gint      *natural)
1186 {
1187   GtkRequisition requisition;
1188 
1189   eel_editable_label_size_request (widget, &requisition);
1190 
1191   *minimum = *natural = requisition.width;
1192 }
1193 
1194 static void
eel_editable_label_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)1195 eel_editable_label_get_preferred_height (GtkWidget *widget,
1196                                          gint      *minimum,
1197                                          gint      *natural)
1198 {
1199   GtkRequisition requisition;
1200 
1201   eel_editable_label_size_request (widget, &requisition);
1202 
1203   *minimum = *natural = requisition.height;
1204 }
1205 
1206 static void
eel_editable_label_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1207 eel_editable_label_size_allocate (GtkWidget     *widget,
1208                                   GtkAllocation *allocation)
1209 {
1210     (* GTK_WIDGET_CLASS (eel_editable_label_parent_class)->size_allocate) (widget, allocation);
1211 }
1212 
1213 static void
eel_editable_label_state_flags_changed(GtkWidget * widget,GtkStateFlags prev_state)1214 eel_editable_label_state_flags_changed (GtkWidget    *widget,
1215                                         GtkStateFlags prev_state)
1216 {
1217     EelEditableLabel *label;
1218 
1219     label = EEL_EDITABLE_LABEL (widget);
1220 
1221     /* clear any selection if we're insensitive */
1222     if (!gtk_widget_is_sensitive (widget))
1223         eel_editable_label_select_region (label, 0, 0);
1224 
1225     if (GTK_WIDGET_CLASS (eel_editable_label_parent_class)->state_flags_changed)
1226         GTK_WIDGET_CLASS (eel_editable_label_parent_class)->state_flags_changed (widget, prev_state);
1227 }
1228 
1229 static void
eel_editable_label_style_updated(GtkWidget * widget)1230 eel_editable_label_style_updated (GtkWidget *widget)
1231 {
1232     EelEditableLabel *label;
1233 
1234     g_assert (EEL_IS_EDITABLE_LABEL (widget));
1235 
1236     label = EEL_EDITABLE_LABEL (widget);
1237 
1238     GTK_WIDGET_CLASS (eel_editable_label_parent_class)->style_updated (widget);
1239 
1240     /* We have to clear the layout, fonts etc. may have changed */
1241     eel_editable_label_recompute (label);
1242 }
1243 
1244 static void
eel_editable_label_direction_changed(GtkWidget * widget,GtkTextDirection previous_dir)1245 eel_editable_label_direction_changed (GtkWidget        *widget,
1246                                       GtkTextDirection previous_dir)
1247 {
1248     EelEditableLabel *label = EEL_EDITABLE_LABEL (widget);
1249 
1250     if (label->layout)
1251         pango_layout_context_changed (label->layout);
1252 
1253     GTK_WIDGET_CLASS (eel_editable_label_parent_class)->direction_changed (widget, previous_dir);
1254 }
1255 
1256 static gfloat
gtk_align_to_gfloat(GtkAlign align)1257 gtk_align_to_gfloat (GtkAlign align)
1258 {
1259 	switch (align) {
1260 		case GTK_ALIGN_START:
1261 			return 0.0;
1262 		case GTK_ALIGN_END:
1263 			return 1.0;
1264 		case GTK_ALIGN_CENTER:
1265 		case GTK_ALIGN_FILL:
1266 			return 0.5;
1267 		default:
1268 			return 0.0;
1269 	}
1270 }
1271 
1272 static void
get_layout_location(EelEditableLabel * label,gint * xp,gint * yp)1273 get_layout_location (EelEditableLabel  *label,
1274                      gint      *xp,
1275                      gint      *yp)
1276 {
1277     GtkWidget *widget;
1278     gfloat xalign, yalign;
1279     GtkRequisition req;
1280     gint x, y, xpad, ypad;
1281     gint margin_start, margin_end, margin_top, margin_bottom;
1282     GtkAllocation allocation;
1283 
1284     widget = GTK_WIDGET (label);
1285     xalign = gtk_align_to_gfloat (gtk_widget_get_halign (widget));
1286     yalign = gtk_align_to_gfloat (gtk_widget_get_valign (widget));
1287 
1288     if (gtk_widget_get_direction (widget) != GTK_TEXT_DIR_LTR)
1289         xalign = 1.0 - xalign;
1290 
1291     gtk_widget_get_preferred_size (widget, &req, NULL);
1292     margin_start = gtk_widget_get_margin_start (widget);
1293     margin_end = gtk_widget_get_margin_end (widget);
1294     margin_top = gtk_widget_get_margin_top (widget);
1295     margin_bottom = gtk_widget_get_margin_bottom (widget);
1296 
1297     xpad = margin_start + margin_end;
1298     ypad = margin_top + margin_bottom;
1299 
1300     gtk_widget_get_allocation (widget, &allocation);
1301     x = floor (0.5 + xpad
1302                + ((allocation.width - req.width) * xalign)
1303                + 0.5);
1304 
1305     y = floor (0.5 + ypad
1306                + ((allocation.height - req.height) * yalign)
1307                + 0.5);
1308 
1309     if (xp)
1310         *xp = x;
1311 
1312     if (yp)
1313         *yp = y;
1314 }
1315 
1316 static gint
eel_editable_label_get_cursor_pos(EelEditableLabel * label,PangoRectangle * strong_pos,PangoRectangle * weak_pos)1317 eel_editable_label_get_cursor_pos (EelEditableLabel  *label,
1318                                    PangoRectangle *strong_pos,
1319                                    PangoRectangle *weak_pos)
1320 {
1321     const gchar *text;
1322     const gchar *preedit_text;
1323     gint index;
1324 
1325     eel_editable_label_ensure_layout (label, TRUE);
1326 
1327     text = pango_layout_get_text (label->layout);
1328     preedit_text = text + label->selection_anchor;
1329     index = label->selection_anchor +
1330             g_utf8_offset_to_pointer (preedit_text, label->preedit_cursor) - preedit_text;
1331 
1332     pango_layout_get_cursor_pos (label->layout, index, strong_pos, weak_pos);
1333 
1334     return index;
1335 }
1336 
1337 /* Copied from gtkutil private function */
1338 static gboolean
eel_editable_label_get_block_cursor_location(EelEditableLabel * label,gint * index,PangoRectangle * pos,gboolean * at_line_end)1339 eel_editable_label_get_block_cursor_location (EelEditableLabel  *label,
1340         gint *index,
1341         PangoRectangle *pos,
1342         gboolean *at_line_end)
1343 {
1344     const gchar *text;
1345     const gchar *preedit_text;
1346     PangoLayoutLine *layout_line;
1347     PangoRectangle strong_pos, weak_pos;
1348     gint line_no;
1349     gboolean rtl;
1350     PangoContext *context;
1351     PangoFontMetrics *metrics;
1352     const PangoFontDescription *font_desc;
1353 
1354     eel_editable_label_ensure_layout (label, TRUE);
1355 
1356     text = pango_layout_get_text (label->layout);
1357     preedit_text = text + label->selection_anchor;
1358     text = g_utf8_offset_to_pointer (preedit_text, label->preedit_cursor);
1359     index[0] = label->selection_anchor + text - preedit_text;
1360 
1361     pango_layout_index_to_pos (label->layout, index[0], pos);
1362 
1363     index[1] = label->selection_anchor + g_utf8_next_char (text) - preedit_text;
1364 
1365     if (pos->width != 0)
1366     {
1367         if (at_line_end)
1368             *at_line_end = FALSE;
1369         if (pos->width < 0) /* RTL char, shift x value back to top left of rect */
1370         {
1371             pos->x += pos->width;
1372             pos->width = -pos->width;
1373         }
1374         return TRUE;
1375     }
1376 
1377     pango_layout_index_to_line_x (label->layout, index[0], FALSE, &line_no, NULL);
1378     layout_line = pango_layout_get_line_readonly (label->layout, line_no);
1379     if (layout_line == NULL)
1380         return FALSE;
1381 
1382     text = pango_layout_get_text (label->layout);
1383     if (index[0] < layout_line->start_index + layout_line->length)
1384     {
1385         /* this may be a zero-width character in the middle of the line,
1386          * or it could be a character where line is wrapped, we do want
1387          * block cursor in latter case */
1388         if (g_utf8_next_char (text + index[0]) - text !=
1389                 layout_line->start_index + layout_line->length)
1390         {
1391             /* zero-width character in the middle of the line, do not
1392              * bother with block cursor */
1393             return FALSE;
1394         }
1395     }
1396 
1397     /* Cursor is at the line end. It may be an empty line, or it could
1398      * be on the left or on the right depending on text direction, or it
1399      * even could be in the middle of visual layout in bidi text. */
1400 
1401     pango_layout_get_cursor_pos (label->layout, index[0], &strong_pos, &weak_pos);
1402 
1403     if (strong_pos.x != weak_pos.x)
1404     {
1405         /* do not show block cursor in this case, since the character typed
1406          * in may or may not appear at the cursor position */
1407         return FALSE;
1408     }
1409 
1410     context = pango_layout_get_context (label->layout);
1411 
1412     /* In case when index points to the end of line, pos->x is always most right
1413      * pixel of the layout line, so we need to correct it for RTL text. */
1414     if (layout_line->length)
1415     {
1416         if (layout_line->resolved_dir == PANGO_DIRECTION_RTL)
1417         {
1418             PangoLayoutIter *iter;
1419             PangoRectangle line_rect;
1420             gint i;
1421             gint left, right;
1422             const gchar *p;
1423 
1424             p = g_utf8_prev_char (text + index[0]);
1425 
1426             pango_layout_line_index_to_x (layout_line, p - text, FALSE, &left);
1427             pango_layout_line_index_to_x (layout_line, p - text, TRUE, &right);
1428             pos->x = MIN (left, right);
1429 
1430             iter = pango_layout_get_iter (label->layout);
1431             for (i = 0; i < line_no; i++)
1432                 pango_layout_iter_next_line (iter);
1433             pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
1434             pango_layout_iter_free (iter);
1435 
1436             rtl = TRUE;
1437             pos->x += line_rect.x;
1438         }
1439         else
1440             rtl = FALSE;
1441     }
1442     else
1443     {
1444         rtl = pango_context_get_base_dir (context) == PANGO_DIRECTION_RTL;
1445     }
1446 
1447     font_desc = pango_layout_get_font_description (label->layout);
1448     if (!font_desc)
1449         font_desc = pango_context_get_font_description (context);
1450 
1451     metrics = pango_context_get_metrics (context, font_desc, NULL);
1452     pos->width = pango_font_metrics_get_approximate_char_width (metrics);
1453     pango_font_metrics_unref (metrics);
1454 
1455     if (rtl)
1456         pos->x -= pos->width - 1;
1457 
1458     if (at_line_end)
1459         *at_line_end = TRUE;
1460 
1461     return pos->width != 0;
1462 }
1463 
1464 
1465 /* These functions are copies from gtk+, as they are not exported from gtk+ */
1466 
1467 static void
eel_editable_label_draw_cursor(EelEditableLabel * label,cairo_t * cr,gint xoffset,gint yoffset)1468 eel_editable_label_draw_cursor (EelEditableLabel  *label, cairo_t *cr, gint xoffset, gint yoffset)
1469 {
1470     if (gtk_widget_is_drawable (GTK_WIDGET (label)))
1471     {
1472         GtkWidget *widget = GTK_WIDGET (label);
1473 
1474         gboolean block;
1475         gboolean block_at_line_end;
1476         gint range[2];
1477         gint index;
1478         GtkStyleContext *context;
1479         PangoRectangle strong_pos;
1480 
1481         context = gtk_widget_get_style_context (widget);
1482         index = eel_editable_label_get_cursor_pos (label, NULL, NULL);
1483 
1484         if (label->overwrite_mode &&
1485                 eel_editable_label_get_block_cursor_location (label, range,
1486                         &strong_pos,
1487                         &block_at_line_end))
1488             block = TRUE;
1489         else
1490             block = FALSE;
1491 
1492         if (!block)
1493         {
1494             gtk_render_insertion_cursor (context, cr,
1495                                          xoffset, yoffset,
1496                                          label->layout, index,
1497                                          gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (widget))));
1498         }
1499         else /* Block cursor */
1500         {
1501             GdkRGBA fg_color;
1502 
1503             gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &fg_color);
1504 
1505             cairo_save (cr);
1506             gdk_cairo_set_source_rgba (cr, &fg_color);
1507             cairo_rectangle (cr,
1508                              xoffset + PANGO_PIXELS (strong_pos.x),
1509                              yoffset + PANGO_PIXELS (strong_pos.y),
1510                              PANGO_PIXELS (strong_pos.width),
1511                              PANGO_PIXELS (strong_pos.height));
1512             cairo_fill (cr);
1513 
1514             if (!block_at_line_end)
1515             {
1516                 GdkRGBA color;
1517                 GdkRGBA *c;
1518                 cairo_region_t *clip;
1519 
1520                 clip = gdk_pango_layout_get_clip_region (label->layout,
1521 							 xoffset, yoffset,
1522 							 range, 1);
1523 
1524                 gdk_cairo_region (cr, clip);
1525                 cairo_clip (cr);
1526 
1527                 gtk_style_context_get (context, GTK_STATE_FLAG_FOCUSED,
1528                                        GTK_STYLE_PROPERTY_BACKGROUND_COLOR,
1529                                        &c, NULL);
1530                 color = *c;
1531                 gdk_rgba_free (c);
1532 
1533                 gdk_cairo_set_source_rgba (cr,
1534                                            &color);
1535                 cairo_move_to (cr, xoffset, yoffset);
1536                 pango_cairo_show_layout (cr, label->layout);
1537 
1538                 cairo_region_destroy (clip);
1539             }
1540 
1541             cairo_restore (cr);
1542         }
1543     }
1544 }
1545 
1546 
1547 static gint
eel_editable_label_draw(GtkWidget * widget,cairo_t * cr)1548 eel_editable_label_draw (GtkWidget *widget,
1549                          cairo_t   *cr)
1550 {
1551     EelEditableLabel *label;
1552     GtkStyleContext *style;
1553     gint x, y;
1554 
1555     g_assert (EEL_IS_EDITABLE_LABEL (widget));
1556 
1557     label = EEL_EDITABLE_LABEL (widget);
1558     style = gtk_widget_get_style_context (widget);
1559 
1560     gtk_render_background (style, cr,
1561                            0, 0,
1562                            gtk_widget_get_allocated_width (widget),
1563                            gtk_widget_get_allocated_height (widget));
1564 
1565     eel_editable_label_ensure_layout (label, TRUE);
1566 
1567     if (gtk_widget_get_visible (widget) && gtk_widget_get_mapped (widget) &&
1568             label->text)
1569     {
1570         get_layout_location (label, &x, &y);
1571 
1572         gtk_render_layout (style,
1573                            cr,
1574                            x, y,
1575                            label->layout);
1576 
1577         if (label->selection_anchor != label->selection_end)
1578         {
1579             gint range[2];
1580             cairo_region_t *clip;
1581             GtkStateFlags state;
1582 
1583             GdkRGBA background_color;
1584             GdkRGBA *c;
1585 
1586             range[0] = label->selection_anchor;
1587             range[1] = label->selection_end;
1588 
1589             /* Handle possible preedit string */
1590             if (label->preedit_length > 0 &&
1591                 range[1] > label->selection_anchor)
1592             {
1593                 const char *text;
1594 
1595                 text = pango_layout_get_text (label->layout) + label->selection_anchor;
1596                 range[1] += g_utf8_offset_to_pointer (text, label->preedit_length) - text;
1597             }
1598 
1599             if (range[0] > range[1])
1600             {
1601                 gint tmp = range[0];
1602                 range[0] = range[1];
1603                 range[1] = tmp;
1604             }
1605 
1606             clip = gdk_pango_layout_get_clip_region (label->layout,
1607                                                      x, y,
1608                                                      range,
1609                                                      1);
1610 
1611             cairo_save (cr);
1612 
1613             gdk_cairo_region (cr, clip);
1614             cairo_clip (cr);
1615 
1616             state = gtk_widget_get_state_flags (widget);
1617             state |= GTK_STATE_FLAG_SELECTED;
1618 
1619             gtk_style_context_get (style, state,
1620                                    GTK_STYLE_PROPERTY_BACKGROUND_COLOR,
1621                                    &c, NULL);
1622             background_color = *c;
1623             gdk_rgba_free (c);
1624 
1625             gdk_cairo_set_source_rgba (cr, &background_color);
1626             cairo_paint (cr);
1627 
1628             gtk_style_context_save (style);
1629             gtk_style_context_set_state (style, state);
1630 
1631             gtk_render_layout (style, cr,
1632                                x, y, label->layout);
1633 
1634             gtk_style_context_restore (style);
1635 
1636             cairo_restore (cr);
1637 
1638             cairo_region_destroy (clip);
1639         }
1640         else if (gtk_widget_has_focus (widget))
1641             eel_editable_label_draw_cursor (label, cr, x, y);
1642 
1643         if (label->draw_outline)
1644         {
1645             gtk_style_context_save (style);
1646             gtk_style_context_set_state (style, gtk_widget_get_state_flags (widget));
1647 
1648             gtk_render_frame (style, cr,
1649                               0, 0,
1650                               gtk_widget_get_allocated_width (widget),
1651                               gtk_widget_get_allocated_height (widget));
1652 
1653             gtk_style_context_restore (style);
1654         }
1655     }
1656 
1657     return FALSE;
1658 }
1659 
1660 static void
eel_editable_label_realize(GtkWidget * widget)1661 eel_editable_label_realize (GtkWidget *widget)
1662 {
1663     EelEditableLabel *label;
1664     GdkDisplay *display;
1665     GdkWindowAttr attributes;
1666     gint attributes_mask;
1667     GtkAllocation allocation;
1668     GdkWindow *window;
1669 
1670     gtk_widget_set_realized (widget, TRUE);
1671     label = EEL_EDITABLE_LABEL (widget);
1672     gtk_widget_get_allocation (widget, &allocation);
1673 
1674     attributes.wclass = GDK_INPUT_OUTPUT;
1675     attributes.window_type = GDK_WINDOW_CHILD;
1676     attributes.x = allocation.x;
1677     attributes.y = allocation.y;
1678     attributes.width = allocation.width;
1679     attributes.height = allocation.height;
1680     attributes.visual = gtk_widget_get_visual (widget);
1681     display = gtk_widget_get_display (GTK_WIDGET (label));
1682     attributes.cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
1683     attributes.event_mask = gtk_widget_get_events (widget) |
1684                             (GDK_EXPOSURE_MASK |
1685                              GDK_BUTTON_PRESS_MASK |
1686                              GDK_BUTTON_RELEASE_MASK |
1687                              GDK_BUTTON1_MOTION_MASK |
1688                              GDK_BUTTON3_MOTION_MASK |
1689                              GDK_POINTER_MOTION_HINT_MASK |
1690                              GDK_POINTER_MOTION_MASK |
1691                              GDK_ENTER_NOTIFY_MASK |
1692                              GDK_LEAVE_NOTIFY_MASK);
1693 
1694     attributes_mask = GDK_WA_X | GDK_WA_Y  | GDK_WA_VISUAL | GDK_WA_CURSOR;
1695 
1696     window = gdk_window_new (gtk_widget_get_parent_window (widget),
1697                              &attributes, attributes_mask);
1698     gtk_widget_set_window (widget, window);
1699     gdk_window_set_user_data (window, widget);
1700 
1701     g_object_unref (attributes.cursor);
1702 
1703     gtk_im_context_set_client_window (label->im_context, gtk_widget_get_window (widget));
1704 }
1705 
1706 static void
eel_editable_label_unrealize(GtkWidget * widget)1707 eel_editable_label_unrealize (GtkWidget *widget)
1708 {
1709     EelEditableLabel *label;
1710 
1711     label = EEL_EDITABLE_LABEL (widget);
1712 
1713     /* Strange. Copied from GtkEntry, should be NULL? */
1714     gtk_im_context_set_client_window (label->im_context, NULL);
1715 
1716     (* GTK_WIDGET_CLASS (eel_editable_label_parent_class)->unrealize) (widget);
1717 }
1718 
1719 static void
eel_editable_label_map(GtkWidget * widget)1720 eel_editable_label_map (GtkWidget *widget)
1721 {
1722     (* GTK_WIDGET_CLASS (eel_editable_label_parent_class)->map) (widget);
1723 }
1724 
1725 static void
eel_editable_label_unmap(GtkWidget * widget)1726 eel_editable_label_unmap (GtkWidget *widget)
1727 {
1728     (* GTK_WIDGET_CLASS (eel_editable_label_parent_class)->unmap) (widget);
1729 }
1730 
1731 static void
window_to_layout_coords(EelEditableLabel * label,gint * x,gint * y)1732 window_to_layout_coords (EelEditableLabel *label,
1733                          gint     *x,
1734                          gint     *y)
1735 {
1736     gint lx, ly;
1737 
1738     /* get layout location in gtk_widget_get_window (widget) coords */
1739     get_layout_location (label, &lx, &ly);
1740 
1741     if (x)
1742         *x -= lx;                   /* go to layout */
1743 
1744     if (y)
1745         *y -= ly;                   /* go to layout */
1746 }
1747 
1748 static void
get_layout_index(EelEditableLabel * label,gint x,gint y,gint * index)1749 get_layout_index (EelEditableLabel *label,
1750                   gint      x,
1751                   gint      y,
1752                   gint     *index)
1753 {
1754     gint trailing = 0;
1755     const gchar *cluster;
1756     const gchar *cluster_end;
1757 
1758     *index = 0;
1759 
1760     eel_editable_label_ensure_layout (label, TRUE);
1761 
1762     window_to_layout_coords (label, &x, &y);
1763 
1764     x *= PANGO_SCALE;
1765     y *= PANGO_SCALE;
1766 
1767     pango_layout_xy_to_index (label->layout,
1768                               x, y,
1769                               index, &trailing);
1770 
1771     if (*index >= label->selection_anchor && label->preedit_length)
1772     {
1773         if (*index >= label->selection_anchor + label->preedit_length)
1774             *index -= label->preedit_length;
1775         else
1776         {
1777             *index = label->selection_anchor;
1778             trailing = 0;
1779         }
1780     }
1781 
1782     cluster = label->text + *index;
1783     cluster_end = cluster;
1784     while (trailing)
1785     {
1786         cluster_end = g_utf8_next_char (cluster_end);
1787         --trailing;
1788     }
1789 
1790     *index += (cluster_end - cluster);
1791 }
1792 
1793 static void
eel_editable_label_select_word(EelEditableLabel * label)1794 eel_editable_label_select_word (EelEditableLabel *label)
1795 {
1796     gint min, max;
1797 
1798     gint start_index = eel_editable_label_move_backward_word (label, label->selection_end);
1799     gint end_index = eel_editable_label_move_forward_word (label, label->selection_end);
1800 
1801     min = MIN (label->selection_anchor,
1802                label->selection_end);
1803     max = MAX (label->selection_anchor,
1804                label->selection_end);
1805 
1806     min = MIN (min, start_index);
1807     max = MAX (max, end_index);
1808 
1809     eel_editable_label_select_region_index (label, min, max);
1810 }
1811 
1812 static gint
eel_editable_label_button_press(GtkWidget * widget,GdkEventButton * event)1813 eel_editable_label_button_press (GtkWidget      *widget,
1814                                  GdkEventButton *event)
1815 {
1816     EelEditableLabel *label;
1817     gint index = 0;
1818 
1819     label = EEL_EDITABLE_LABEL (widget);
1820 
1821     if (event->button == 1)
1822     {
1823         if (!gtk_widget_has_focus (widget))
1824             gtk_widget_grab_focus (widget);
1825 
1826         if (event->type == GDK_3BUTTON_PRESS)
1827         {
1828             eel_editable_label_select_region_index (label, 0, strlen (label->text));
1829             return TRUE;
1830         }
1831 
1832         if (event->type == GDK_2BUTTON_PRESS)
1833         {
1834             eel_editable_label_select_word (label);
1835             return TRUE;
1836         }
1837 
1838         get_layout_index (label, event->x, event->y, &index);
1839 
1840         if ((label->selection_anchor !=
1841                 label->selection_end) &&
1842                 (event->state & GDK_SHIFT_MASK))
1843         {
1844             gint min, max;
1845 
1846             /* extend (same as motion) */
1847             min = MIN (label->selection_anchor,
1848                        label->selection_end);
1849             max = MAX (label->selection_anchor,
1850                        label->selection_end);
1851 
1852             min = MIN (min, index);
1853             max = MAX (max, index);
1854 
1855             /* ensure the anchor is opposite index */
1856             if (index == min)
1857             {
1858                 gint tmp = min;
1859                 min = max;
1860                 max = tmp;
1861             }
1862 
1863             eel_editable_label_select_region_index (label, min, max);
1864         }
1865         else
1866         {
1867             if (event->type == GDK_3BUTTON_PRESS)
1868                 eel_editable_label_select_region_index (label, 0, strlen (label->text));
1869             else if (event->type == GDK_2BUTTON_PRESS)
1870                 eel_editable_label_select_word (label);
1871             else
1872                 /* start a replacement */
1873                 eel_editable_label_select_region_index (label, index, index);
1874         }
1875 
1876         return TRUE;
1877     }
1878     else if (event->button == 2 && event->type == GDK_BUTTON_PRESS)
1879     {
1880         get_layout_index (label, event->x, event->y, &index);
1881 
1882         eel_editable_label_select_region_index (label, index, index);
1883         eel_editable_label_paste (label, GDK_SELECTION_PRIMARY);
1884 
1885         return TRUE;
1886     }
1887     else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
1888     {
1889         eel_editable_label_do_popup (label, event);
1890 
1891         return TRUE;
1892 
1893     }
1894     return FALSE;
1895 }
1896 
1897 static gint
eel_editable_label_button_release(GtkWidget * widget,GdkEventButton * event)1898 eel_editable_label_button_release (GtkWidget      *widget,
1899                                    GdkEventButton *event)
1900 
1901 {
1902     if (event->button != 1)
1903         return FALSE;
1904 
1905     /* The goal here is to return TRUE iff we ate the
1906      * button press to start selecting.
1907      */
1908 
1909     return TRUE;
1910 }
1911 
1912 static gint
eel_editable_label_motion(GtkWidget * widget,GdkEventMotion * event)1913 eel_editable_label_motion (GtkWidget      *widget,
1914                            GdkEventMotion *event)
1915 {
1916     EelEditableLabel *label;
1917     gint index;
1918     gint x, y;
1919 
1920     label = EEL_EDITABLE_LABEL (widget);
1921 
1922     if ((event->state & GDK_BUTTON1_MASK) == 0)
1923         return FALSE;
1924 
1925     gdk_window_get_device_position (gtk_widget_get_window (widget),
1926                                     event->device,
1927                                     &x, &y, NULL);
1928 
1929     get_layout_index (label, x, y, &index);
1930 
1931     eel_editable_label_select_region_index (label,
1932                                             label->selection_anchor,
1933                                             index);
1934 
1935     return TRUE;
1936 }
1937 
1938 static void
get_text_callback(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer user_data_or_owner)1939 get_text_callback (GtkClipboard     *clipboard,
1940                    GtkSelectionData *selection_data,
1941                    guint             info,
1942                    gpointer          user_data_or_owner)
1943 {
1944     EelEditableLabel *label;
1945 
1946     label = EEL_EDITABLE_LABEL (user_data_or_owner);
1947 
1948     if ((label->selection_anchor != label->selection_end) &&
1949             label->text)
1950     {
1951         gint start, end;
1952         gint len;
1953 
1954         start = MIN (label->selection_anchor,
1955                      label->selection_end);
1956         end = MAX (label->selection_anchor,
1957                    label->selection_end);
1958 
1959         len = strlen (label->text);
1960 
1961         if (end > len)
1962             end = len;
1963 
1964         if (start > len)
1965             start = len;
1966 
1967         gtk_selection_data_set_text (selection_data,
1968                                      label->text + start,
1969                                      end - start);
1970     }
1971 }
1972 
1973 static void
clear_text_callback(GtkClipboard * clipboard,gpointer user_data_or_owner)1974 clear_text_callback (GtkClipboard     *clipboard,
1975                      gpointer          user_data_or_owner)
1976 {
1977     EelEditableLabel *label;
1978 
1979     label = EEL_EDITABLE_LABEL (user_data_or_owner);
1980 
1981     label->selection_anchor = label->selection_end;
1982 
1983     gtk_widget_queue_draw (GTK_WIDGET (label));
1984 }
1985 
1986 static void
eel_editable_label_select_region_index(EelEditableLabel * label,gint anchor_index,gint end_index)1987 eel_editable_label_select_region_index (EelEditableLabel *label,
1988                                         gint      anchor_index,
1989                                         gint      end_index)
1990 {
1991     GtkClipboard *clipboard;
1992 
1993     g_assert (EEL_IS_EDITABLE_LABEL (label));
1994 
1995 
1996     if (label->selection_anchor == anchor_index &&
1997             label->selection_end == end_index)
1998         return;
1999 
2000     eel_editable_label_reset_im_context (label);
2001 
2002     label->selection_anchor = anchor_index;
2003     label->selection_end = end_index;
2004 
2005     clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2006 
2007     if (anchor_index != end_index)
2008     {
2009         GtkTargetList *list;
2010         GtkTargetEntry *targets;
2011         gint n_targets;
2012 
2013         list = gtk_target_list_new (NULL, 0);
2014         gtk_target_list_add_text_targets (list, 0);
2015         targets = gtk_target_table_new_from_list (list, &n_targets);
2016 
2017         gtk_clipboard_set_with_owner (clipboard,
2018                                       targets, n_targets,
2019                                       get_text_callback,
2020                                       clear_text_callback,
2021                                       G_OBJECT (label));
2022 
2023         gtk_clipboard_set_can_store (clipboard, NULL, 0);
2024         gtk_target_table_free (targets, n_targets);
2025         gtk_target_list_unref (list);
2026     }
2027     else
2028     {
2029         if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
2030             gtk_clipboard_clear (clipboard);
2031     }
2032 
2033     gtk_widget_queue_draw (GTK_WIDGET (label));
2034 
2035     g_object_freeze_notify (G_OBJECT (label));
2036     g_object_notify (G_OBJECT (label), "cursor_position");
2037     g_object_notify (G_OBJECT (label), "selection_bound");
2038     g_object_thaw_notify (G_OBJECT (label));
2039 }
2040 
2041 /**
2042  * eel_editable_label_select_region:
2043  * @label: a #EelEditableLabel
2044  * @start_offset: start offset (in characters not bytes)
2045  * @end_offset: end offset (in characters not bytes)
2046  *
2047  * Selects a range of characters in the label, if the label is selectable.
2048  * See eel_editable_label_set_selectable(). If the label is not selectable,
2049  * this function has no effect. If @start_offset or
2050  * @end_offset are -1, then the end of the label will be substituted.
2051  *
2052  **/
2053 void
eel_editable_label_select_region(EelEditableLabel * label,gint start_offset,gint end_offset)2054 eel_editable_label_select_region  (EelEditableLabel *label,
2055                                    gint      start_offset,
2056                                    gint      end_offset)
2057 {
2058     g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
2059 
2060     if (label->text)
2061     {
2062         if (start_offset < 0)
2063             start_offset = g_utf8_strlen (label->text, -1);
2064 
2065         if (end_offset < 0)
2066             end_offset = g_utf8_strlen (label->text, -1);
2067 
2068         eel_editable_label_select_region_index (label,
2069                                                 g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
2070                                                 g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
2071     }
2072 }
2073 
2074 /**
2075  * eel_editable_label_get_selection_bounds:
2076  * @label: a #EelEditableLabel
2077  * @start: return location for start of selection, as a character offset
2078  * @end: return location for end of selection, as a character offset
2079  *
2080  * Gets the selected range of characters in the label, returning %TRUE
2081  * if there's a selection.
2082  *
2083  * Return value: %TRUE if selection is non-empty
2084  **/
2085 gboolean
eel_editable_label_get_selection_bounds(EelEditableLabel * label,gint * start,gint * end)2086 eel_editable_label_get_selection_bounds (EelEditableLabel  *label,
2087         gint      *start,
2088         gint      *end)
2089 {
2090     gint start_index, end_index;
2091     gint start_offset, end_offset;
2092     gint len;
2093 
2094     g_return_val_if_fail (EEL_IS_EDITABLE_LABEL (label), FALSE);
2095 
2096 
2097     start_index = MIN (label->selection_anchor,
2098                        label->selection_end);
2099     end_index = MAX (label->selection_anchor,
2100                      label->selection_end);
2101 
2102     len = strlen (label->text);
2103 
2104     if (end_index > len)
2105         end_index = len;
2106 
2107     if (start_index > len)
2108         start_index = len;
2109 
2110     start_offset = g_utf8_strlen (label->text, start_index);
2111     end_offset = g_utf8_strlen (label->text, end_index);
2112 
2113     if (start_offset > end_offset)
2114     {
2115         gint tmp = start_offset;
2116         start_offset = end_offset;
2117         end_offset = tmp;
2118     }
2119 
2120     if (start)
2121         *start = start_offset;
2122 
2123     if (end)
2124         *end = end_offset;
2125 
2126     return start_offset != end_offset;
2127 }
2128 
2129 
2130 /**
2131  * eel_editable_label_get_layout:
2132  * @label: a #EelEditableLabel
2133  *
2134  * Gets the #PangoLayout used to display the label.
2135  * The layout is useful to e.g. convert text positions to
2136  * pixel positions, in combination with eel_editable_label_get_layout_offsets().
2137  * The returned layout is owned by the label so need not be
2138  * freed by the caller.
2139  *
2140  * Return value: the #PangoLayout for this label
2141  **/
2142 PangoLayout*
eel_editable_label_get_layout(EelEditableLabel * label)2143 eel_editable_label_get_layout (EelEditableLabel *label)
2144 {
2145     g_return_val_if_fail (EEL_IS_EDITABLE_LABEL (label), NULL);
2146 
2147     eel_editable_label_ensure_layout (label, TRUE);
2148 
2149     return label->layout;
2150 }
2151 
2152 /**
2153  * eel_editable_label_get_layout_offsets:
2154  * @label: a #EelEditableLabel
2155  * @x: location to store X offset of layout, or %NULL
2156  * @y: location to store Y offset of layout, or %NULL
2157  *
2158  * Obtains the coordinates where the label will draw the #PangoLayout
2159  * representing the text in the label; useful to convert mouse events
2160  * into coordinates inside the #PangoLayout, e.g. to take some action
2161  * if some part of the label is clicked. Of course you will need to
2162  * create a #GtkEventBox to receive the events, and pack the label
2163  * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
2164  * when using the #PangoLayout functions you need to convert to
2165  * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
2166  *
2167  **/
2168 void
eel_editable_label_get_layout_offsets(EelEditableLabel * label,gint * x,gint * y)2169 eel_editable_label_get_layout_offsets (EelEditableLabel *label,
2170                                        gint     *x,
2171                                        gint     *y)
2172 {
2173     g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
2174 
2175     get_layout_location (label, x, y);
2176 }
2177 
2178 static void
eel_editable_label_pend_cursor_blink(EelEditableLabel * label)2179 eel_editable_label_pend_cursor_blink (EelEditableLabel *label)
2180 {
2181     /* TODO */
2182 }
2183 
2184 static void
eel_editable_label_check_cursor_blink(EelEditableLabel * label)2185 eel_editable_label_check_cursor_blink (EelEditableLabel *label)
2186 {
2187     /* TODO */
2188 }
2189 
2190 static gint
eel_editable_label_key_press(GtkWidget * widget,GdkEventKey * event)2191 eel_editable_label_key_press (GtkWidget   *widget,
2192                               GdkEventKey *event)
2193 {
2194     EelEditableLabel *label = EEL_EDITABLE_LABEL (widget);
2195 
2196     eel_editable_label_pend_cursor_blink (label);
2197 
2198     if (gtk_im_context_filter_keypress (label->im_context, event))
2199     {
2200         /*TODO eel_editable_label_obscure_mouse_cursor (label);*/
2201         label->need_im_reset = TRUE;
2202         return TRUE;
2203     }
2204 
2205     if (GTK_WIDGET_CLASS (eel_editable_label_parent_class)->key_press_event (widget, event))
2206         /* Activate key bindings
2207          */
2208         return TRUE;
2209 
2210     return FALSE;
2211 }
2212 
2213 static gint
eel_editable_label_key_release(GtkWidget * widget,GdkEventKey * event)2214 eel_editable_label_key_release (GtkWidget   *widget,
2215                                 GdkEventKey *event)
2216 {
2217     EelEditableLabel *label = EEL_EDITABLE_LABEL (widget);
2218 
2219     if (gtk_im_context_filter_keypress (label->im_context, event))
2220     {
2221         label->need_im_reset = TRUE;
2222         return TRUE;
2223     }
2224 
2225     return GTK_WIDGET_CLASS (eel_editable_label_parent_class)->key_release_event (widget, event);
2226 }
2227 
2228 static void
eel_editable_label_keymap_direction_changed(GdkKeymap * keymap,EelEditableLabel * label)2229 eel_editable_label_keymap_direction_changed (GdkKeymap *keymap,
2230         EelEditableLabel  *label)
2231 {
2232     gtk_widget_queue_draw (GTK_WIDGET (label));
2233 }
2234 
2235 static gint
eel_editable_label_focus_in(GtkWidget * widget,GdkEventFocus * event)2236 eel_editable_label_focus_in (GtkWidget     *widget,
2237                              GdkEventFocus *event)
2238 {
2239     EelEditableLabel *label = EEL_EDITABLE_LABEL (widget);
2240 
2241     gtk_widget_queue_draw (widget);
2242 
2243     label->need_im_reset = TRUE;
2244     gtk_im_context_focus_in (label->im_context);
2245 
2246     g_signal_connect (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
2247                       "direction_changed",
2248                       G_CALLBACK (eel_editable_label_keymap_direction_changed), label);
2249 
2250     eel_editable_label_check_cursor_blink (label);
2251 
2252     return FALSE;
2253 }
2254 
2255 static gint
eel_editable_label_focus_out(GtkWidget * widget,GdkEventFocus * event)2256 eel_editable_label_focus_out (GtkWidget     *widget,
2257                               GdkEventFocus *event)
2258 {
2259     EelEditableLabel *label = EEL_EDITABLE_LABEL (widget);
2260 
2261     gtk_widget_queue_draw (widget);
2262 
2263     label->need_im_reset = TRUE;
2264     gtk_im_context_focus_out (label->im_context);
2265 
2266     eel_editable_label_check_cursor_blink (label);
2267 
2268     g_signal_handlers_disconnect_by_func (gdk_keymap_get_for_display (gtk_widget_get_display (widget)),
2269                                           (gpointer) eel_editable_label_keymap_direction_changed,
2270                                           label);
2271 
2272     return FALSE;
2273 }
2274 
2275 static void
eel_editable_label_delete_text(EelEditableLabel * label,int start_pos,int end_pos)2276 eel_editable_label_delete_text (EelEditableLabel *label,
2277                                 int start_pos,
2278                                 int end_pos)
2279 {
2280     if (start_pos < 0)
2281         start_pos = 0;
2282     if (end_pos < 0 || end_pos > label->n_bytes)
2283         end_pos = label->n_bytes;
2284 
2285     if (start_pos < end_pos)
2286     {
2287         int anchor, end;
2288 
2289         memmove (label->text + start_pos, label->text + end_pos, label->n_bytes + 1 - end_pos);
2290         label->n_bytes -= (end_pos - start_pos);
2291 
2292         anchor = label->selection_anchor;
2293         if (anchor > start_pos)
2294             anchor -= MIN (anchor, end_pos) - start_pos;
2295 
2296         end = label->selection_end;
2297         if (end > start_pos)
2298             end -= MIN (end, end_pos) - start_pos;
2299 
2300         /* We might have changed the selection */
2301         eel_editable_label_select_region_index (label, anchor, end);
2302 
2303         eel_editable_label_recompute (label);
2304         gtk_widget_queue_resize (GTK_WIDGET (label));
2305 
2306         g_object_notify (G_OBJECT (label), "text");
2307         g_signal_emit_by_name (GTK_EDITABLE (label), "changed");
2308     }
2309 }
2310 
2311 static void
eel_editable_label_insert_text(EelEditableLabel * label,const gchar * new_text,gint new_text_length,gint * index)2312 eel_editable_label_insert_text (EelEditableLabel *label,
2313                                 const gchar *new_text,
2314                                 gint         new_text_length,
2315                                 gint        *index)
2316 {
2317     if (new_text_length + label->n_bytes + 1 > label->text_size)
2318     {
2319         while (new_text_length + label->n_bytes + 1 > label->text_size)
2320         {
2321             if (label->text_size == 0)
2322                 label->text_size = 16;
2323             else
2324                 label->text_size *= 2;
2325         }
2326 
2327         label->text = g_realloc (label->text, label->text_size);
2328     }
2329 
2330     g_object_freeze_notify (G_OBJECT (label));
2331 
2332     memmove (label->text + *index + new_text_length, label->text + *index, label->n_bytes - *index);
2333     memmove (label->text + *index, new_text, new_text_length);
2334 
2335     label->n_bytes += new_text_length;
2336 
2337     /* NUL terminate for safety and convenience */
2338     label->text[label->n_bytes] = '\0';
2339 
2340     g_object_notify (G_OBJECT (label), "text");
2341 
2342     if (label->selection_anchor > *index)
2343     {
2344         g_object_notify (G_OBJECT (label), "cursor_position");
2345         g_object_notify (G_OBJECT (label), "selection_bound");
2346         label->selection_anchor += new_text_length;
2347     }
2348 
2349     if (label->selection_end > *index)
2350     {
2351         label->selection_end += new_text_length;
2352         g_object_notify (G_OBJECT (label), "selection_bound");
2353     }
2354 
2355     *index += new_text_length;
2356 
2357     eel_editable_label_recompute (label);
2358     gtk_widget_queue_resize (GTK_WIDGET (label));
2359 
2360     g_object_thaw_notify (G_OBJECT (label));
2361     g_signal_emit_by_name (GTK_EDITABLE (label), "changed");
2362 }
2363 
2364 /* Used for im_commit_cb and inserting Unicode chars */
2365 static void
eel_editable_label_enter_text(EelEditableLabel * label,const gchar * str)2366 eel_editable_label_enter_text (EelEditableLabel *label,
2367                                const gchar    *str)
2368 {
2369     GtkEditable *editable = GTK_EDITABLE (label);
2370     gint tmp_pos;
2371     gboolean old_need_im_reset;
2372 
2373     /* Never reset the im while commiting, as that resets possible im state */
2374     old_need_im_reset = label->need_im_reset;
2375     label->need_im_reset = FALSE;
2376 
2377     if (label->selection_end != label->selection_anchor)
2378         gtk_editable_delete_selection (editable);
2379     else
2380     {
2381         if (label->overwrite_mode)
2382             eel_editable_label_delete_from_cursor (label, GTK_DELETE_CHARS, 1);
2383     }
2384 
2385     tmp_pos = g_utf8_pointer_to_offset (label->text,
2386                                         label->text + label->selection_anchor);
2387     gtk_editable_insert_text (GTK_EDITABLE (label), str, strlen (str), &tmp_pos);
2388     tmp_pos = g_utf8_offset_to_pointer (label->text, tmp_pos) - label->text;
2389     eel_editable_label_select_region_index (label, tmp_pos, tmp_pos);
2390 
2391     label->need_im_reset = old_need_im_reset;
2392 }
2393 
2394 /* IM Context Callbacks
2395  */
2396 
2397 static void
eel_editable_label_commit_cb(GtkIMContext * context,const gchar * str,EelEditableLabel * label)2398 eel_editable_label_commit_cb (GtkIMContext *context,
2399                               const gchar  *str,
2400                               EelEditableLabel  *label)
2401 {
2402     eel_editable_label_enter_text (label, str);
2403 }
2404 
2405 static void
eel_editable_label_preedit_changed_cb(GtkIMContext * context,EelEditableLabel * label)2406 eel_editable_label_preedit_changed_cb (GtkIMContext *context,
2407                                        EelEditableLabel  *label)
2408 {
2409     gchar *preedit_string;
2410     gint cursor_pos;
2411 
2412     gtk_im_context_get_preedit_string (label->im_context,
2413                                        &preedit_string, NULL,
2414                                        &cursor_pos);
2415     label->preedit_length = strlen (preedit_string);
2416     cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
2417     label->preedit_cursor = cursor_pos;
2418     g_free (preedit_string);
2419 
2420     eel_editable_label_recompute (label);
2421     gtk_widget_queue_resize (GTK_WIDGET (label));
2422 }
2423 
2424 static gboolean
eel_editable_label_retrieve_surrounding_cb(GtkIMContext * context,EelEditableLabel * label)2425 eel_editable_label_retrieve_surrounding_cb (GtkIMContext *context,
2426         EelEditableLabel  *label)
2427 {
2428     gtk_im_context_set_surrounding (context,
2429                                     label->text,
2430                                     strlen (label->text) + 1,
2431                                     label->selection_end);
2432 
2433     return TRUE;
2434 }
2435 
2436 static gboolean
eel_editable_label_delete_surrounding_cb(GtkIMContext * slave,gint offset,gint n_chars,EelEditableLabel * label)2437 eel_editable_label_delete_surrounding_cb (GtkIMContext *slave,
2438         gint          offset,
2439         gint          n_chars,
2440         EelEditableLabel  *label)
2441 {
2442     gint current_pos;
2443 
2444     current_pos = g_utf8_pointer_to_offset (label->text, label->text + label->selection_anchor);
2445     gtk_editable_delete_text (GTK_EDITABLE (label),
2446                               current_pos + offset,
2447                               current_pos + offset + n_chars);
2448 
2449     return TRUE;
2450 }
2451 
2452 static gboolean
eel_editable_label_focus(GtkWidget * widget,GtkDirectionType direction)2453 eel_editable_label_focus (GtkWidget         *widget,
2454                           GtkDirectionType   direction)
2455 {
2456     /* We never want to be in the tab chain */
2457     return FALSE;
2458 }
2459 
2460 /* Compute the X position for an offset that corresponds to the "more important
2461  * cursor position for that offset. We use this when trying to guess to which
2462  * end of the selection we should go to when the user hits the left or
2463  * right arrow key.
2464  */
2465 static void
get_better_cursor(EelEditableLabel * label,gint index,gint * x,gint * y)2466 get_better_cursor (EelEditableLabel *label,
2467                    gint      index,
2468                    gint      *x,
2469                    gint      *y)
2470 {
2471     GtkTextDirection keymap_direction =
2472         (gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)))) == PANGO_DIRECTION_LTR) ?
2473         GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
2474     GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (label));
2475     gboolean split_cursor;
2476     PangoRectangle strong_pos, weak_pos;
2477 
2478     g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2479                   "gtk-split-cursor", &split_cursor,
2480                   NULL);
2481 
2482     eel_editable_label_get_cursor_pos (label, &strong_pos, &weak_pos);
2483 
2484     if (split_cursor)
2485     {
2486         *x = strong_pos.x / PANGO_SCALE;
2487         *y = strong_pos.y / PANGO_SCALE;
2488     }
2489     else
2490     {
2491         if (keymap_direction == widget_direction)
2492         {
2493             *x = strong_pos.x / PANGO_SCALE;
2494             *y = strong_pos.y / PANGO_SCALE;
2495         }
2496         else
2497         {
2498             *x = weak_pos.x / PANGO_SCALE;
2499             *y = weak_pos.y / PANGO_SCALE;
2500         }
2501     }
2502 }
2503 
2504 
2505 static gint
eel_editable_label_move_logically(EelEditableLabel * label,gint start,gint count)2506 eel_editable_label_move_logically (EelEditableLabel *label,
2507                                    gint      start,
2508                                    gint      count)
2509 {
2510     gint offset = g_utf8_pointer_to_offset (label->text,
2511                                             label->text + start);
2512 
2513     if (label->text)
2514     {
2515         PangoLogAttr *log_attrs;
2516         gint n_attrs;
2517         gint length;
2518 
2519         eel_editable_label_ensure_layout (label, FALSE);
2520 
2521         length = g_utf8_strlen (label->text, -1);
2522 
2523         pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
2524 
2525         while (count > 0 && offset < length)
2526         {
2527             do
2528                 offset++;
2529             while (offset < length && !log_attrs[offset].is_cursor_position);
2530 
2531             count--;
2532         }
2533         while (count < 0 && offset > 0)
2534         {
2535             do
2536                 offset--;
2537             while (offset > 0 && !log_attrs[offset].is_cursor_position);
2538 
2539             count++;
2540         }
2541 
2542         g_free (log_attrs);
2543     }
2544 
2545     return g_utf8_offset_to_pointer (label->text, offset) - label->text;
2546 }
2547 
2548 static gint
eel_editable_label_move_visually(EelEditableLabel * label,gint start,gint count)2549 eel_editable_label_move_visually (EelEditableLabel *label,
2550                                   gint      start,
2551                                   gint      count)
2552 {
2553     gint index;
2554 
2555     index = start;
2556 
2557     while (count != 0)
2558     {
2559         int new_index, new_trailing;
2560         gboolean split_cursor;
2561         gboolean strong;
2562 
2563         eel_editable_label_ensure_layout (label, FALSE);
2564 
2565         g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
2566                       "gtk-split-cursor", &split_cursor,
2567                       NULL);
2568 
2569         if (split_cursor)
2570             strong = TRUE;
2571         else
2572         {
2573             GtkTextDirection keymap_direction =
2574                 (gdk_keymap_get_direction (gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (label)))) == PANGO_DIRECTION_LTR) ?
2575                 GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
2576 
2577             strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (label));
2578         }
2579 
2580         if (count > 0)
2581         {
2582             pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing);
2583             count--;
2584         }
2585         else
2586         {
2587             pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing);
2588             count++;
2589         }
2590 
2591         if (new_index < 0 || new_index == G_MAXINT)
2592             break;
2593 
2594         index = new_index;
2595 
2596         while (new_trailing--)
2597             index = g_utf8_next_char (label->text + new_index) - label->text;
2598     }
2599 
2600     return index;
2601 }
2602 
2603 static gint
eel_editable_label_move_line(EelEditableLabel * label,gint start,gint count)2604 eel_editable_label_move_line (EelEditableLabel *label,
2605                               gint      start,
2606                               gint      count)
2607 {
2608     PangoLayoutLine *line;
2609     int index;
2610     int n_lines, i;
2611     int x = 0;
2612 
2613     eel_editable_label_ensure_layout (label, FALSE);
2614 
2615     n_lines = pango_layout_get_line_count (label->layout);
2616 
2617     for (i = 0; i < n_lines; i++)
2618     {
2619         line = pango_layout_get_line (label->layout, i);
2620         if (start >= line->start_index &&
2621                 start <= line->start_index + line->length)
2622         {
2623             pango_layout_line_index_to_x (line, start, FALSE, &x);
2624             break;
2625         }
2626     }
2627     if (i == n_lines)
2628         i = n_lines - 1;
2629 
2630     i += count;
2631     i = CLAMP (i, 0, n_lines - 1);
2632 
2633     line = pango_layout_get_line (label->layout, i);
2634     if (pango_layout_line_x_to_index (line,
2635                                       x,
2636                                       &index, NULL))
2637         return index;
2638     else
2639     {
2640         if (i == n_lines - 1)
2641             return line->start_index + line->length;
2642         else
2643             return line->start_index + line->length - 1;
2644     }
2645 }
2646 
2647 static gint
eel_editable_label_move_forward_word(EelEditableLabel * label,gint start)2648 eel_editable_label_move_forward_word (EelEditableLabel *label,
2649                                       gint      start)
2650 {
2651     gint new_pos = g_utf8_pointer_to_offset (label->text,
2652                    label->text + start);
2653     gint length;
2654 
2655     length = g_utf8_strlen (label->text, -1);
2656     if (new_pos < length)
2657     {
2658         PangoLogAttr *log_attrs;
2659         gint n_attrs;
2660 
2661         eel_editable_label_ensure_layout (label, FALSE);
2662 
2663         pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
2664 
2665         /* Find the next word end,
2666         (remember, n_attrs is one more than the number of of chars) */
2667         new_pos++;
2668         while (new_pos < (n_attrs - 1) && !log_attrs[new_pos].is_word_end)
2669             new_pos++;
2670 
2671         g_free (log_attrs);
2672     }
2673 
2674     return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
2675 }
2676 
2677 
2678 static gint
eel_editable_label_move_backward_word(EelEditableLabel * label,gint start)2679 eel_editable_label_move_backward_word (EelEditableLabel *label,
2680                                        gint      start)
2681 {
2682     gint new_pos = g_utf8_pointer_to_offset (label->text,
2683                    label->text + start);
2684 
2685     if (new_pos > 0)
2686     {
2687         PangoLogAttr *log_attrs;
2688         gint n_attrs;
2689 
2690         eel_editable_label_ensure_layout (label, FALSE);
2691 
2692         pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
2693 
2694         new_pos -= 1;
2695 
2696         /* Find the previous word beginning */
2697         while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
2698             new_pos--;
2699 
2700         g_free (log_attrs);
2701     }
2702 
2703     return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
2704 }
2705 
2706 static void
eel_editable_label_move_cursor(EelEditableLabel * label,GtkMovementStep step,gint count,gboolean extend_selection)2707 eel_editable_label_move_cursor (EelEditableLabel    *label,
2708                                 GtkMovementStep      step,
2709                                 gint                 count,
2710                                 gboolean             extend_selection)
2711 {
2712     gint new_pos;
2713 
2714     new_pos = label->selection_end;
2715 
2716     if (label->selection_end != label->selection_anchor &&
2717             !extend_selection)
2718     {
2719         /* If we have a current selection and aren't extending it, move to the
2720          * start/or end of the selection as appropriate
2721          */
2722         switch (step)
2723         {
2724         case GTK_MOVEMENT_DISPLAY_LINES:
2725         case GTK_MOVEMENT_VISUAL_POSITIONS:
2726         {
2727             gint end_x, end_y;
2728             gint anchor_x, anchor_y;
2729             gboolean end_is_left;
2730 
2731             get_better_cursor (label, label->selection_end, &end_x, &end_y);
2732             get_better_cursor (label, label->selection_anchor, &anchor_x, &anchor_y);
2733 
2734             end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
2735 
2736             if (count < 0)
2737                 new_pos = end_is_left ? label->selection_end : label->selection_anchor;
2738             else
2739                 new_pos = !end_is_left ? label->selection_end : label->selection_anchor;
2740 
2741             break;
2742         }
2743         case GTK_MOVEMENT_LOGICAL_POSITIONS:
2744         case GTK_MOVEMENT_WORDS:
2745             if (count < 0)
2746                 new_pos = MIN (label->selection_end, label->selection_anchor);
2747             else
2748                 new_pos = MAX (label->selection_end, label->selection_anchor);
2749             break;
2750         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
2751         case GTK_MOVEMENT_PARAGRAPH_ENDS:
2752         case GTK_MOVEMENT_BUFFER_ENDS:
2753             /* FIXME: Can do better here */
2754             new_pos = count < 0 ? 0 : strlen (label->text);
2755             break;
2756         case GTK_MOVEMENT_PARAGRAPHS:
2757         case GTK_MOVEMENT_PAGES:
2758             break;
2759         default:
2760             g_assert_not_reached ();
2761             break;
2762         }
2763     }
2764     else
2765     {
2766         switch (step)
2767         {
2768         case GTK_MOVEMENT_LOGICAL_POSITIONS:
2769             new_pos = eel_editable_label_move_logically (label, new_pos, count);
2770             break;
2771         case GTK_MOVEMENT_VISUAL_POSITIONS:
2772             new_pos = eel_editable_label_move_visually (label, new_pos, count);
2773             break;
2774         case GTK_MOVEMENT_WORDS:
2775             while (count > 0)
2776             {
2777                 new_pos = eel_editable_label_move_forward_word (label, new_pos);
2778                 count--;
2779             }
2780             while (count < 0)
2781             {
2782                 new_pos = eel_editable_label_move_backward_word (label, new_pos);
2783                 count++;
2784             }
2785             break;
2786         case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
2787         case GTK_MOVEMENT_PARAGRAPH_ENDS:
2788         case GTK_MOVEMENT_BUFFER_ENDS:
2789             /* FIXME: Can do better here */
2790             new_pos = count < 0 ? 0 : strlen (label->text);
2791             break;
2792         case GTK_MOVEMENT_DISPLAY_LINES:
2793             new_pos = eel_editable_label_move_line (label, new_pos, count);
2794             break;
2795         case GTK_MOVEMENT_PARAGRAPHS:
2796         case GTK_MOVEMENT_PAGES:
2797             break;
2798         default:
2799             g_assert_not_reached ();
2800             break;
2801         }
2802     }
2803 
2804     if (extend_selection)
2805         eel_editable_label_select_region_index (label,
2806                                                 label->selection_anchor,
2807                                                 new_pos);
2808     else
2809         eel_editable_label_select_region_index (label, new_pos, new_pos);
2810 }
2811 
2812 static void
eel_editable_label_reset_im_context(EelEditableLabel * label)2813 eel_editable_label_reset_im_context (EelEditableLabel  *label)
2814 {
2815     if (label->need_im_reset)
2816     {
2817         label->need_im_reset = 0;
2818         gtk_im_context_reset (label->im_context);
2819     }
2820 }
2821 
2822 
2823 static void
eel_editable_label_delete_from_cursor(EelEditableLabel * label,GtkDeleteType type,gint count)2824 eel_editable_label_delete_from_cursor (EelEditableLabel *label,
2825                                        GtkDeleteType     type,
2826                                        gint              count)
2827 {
2828     GtkEditable *editable = GTK_EDITABLE (label);
2829     gint start_pos = label->selection_anchor;
2830     gint end_pos = label->selection_anchor;
2831 
2832     eel_editable_label_reset_im_context (label);
2833 
2834     if (label->selection_anchor != label->selection_end)
2835     {
2836         gtk_editable_delete_selection (editable);
2837         return;
2838     }
2839 
2840     switch (type)
2841     {
2842     case GTK_DELETE_CHARS:
2843         end_pos = eel_editable_label_move_logically (label, start_pos, count);
2844         start_pos = g_utf8_pointer_to_offset (label->text, label->text + start_pos);
2845         end_pos = g_utf8_pointer_to_offset (label->text, label->text + end_pos);
2846         gtk_editable_delete_text (GTK_EDITABLE (label), MIN (start_pos, end_pos), MAX (start_pos, end_pos));
2847         break;
2848     case GTK_DELETE_WORDS:
2849         if (count < 0)
2850         {
2851             /* Move to end of current word, or if not on a word, end of previous word */
2852             end_pos = eel_editable_label_move_backward_word (label, end_pos);
2853             end_pos = eel_editable_label_move_forward_word (label, end_pos);
2854         }
2855         else if (count > 0)
2856         {
2857             /* Move to beginning of current word, or if not on a word, begining of next word */
2858             start_pos = eel_editable_label_move_forward_word (label, start_pos);
2859             start_pos = eel_editable_label_move_backward_word (label, start_pos);
2860         }
2861 
2862         /* Fall through */
2863     case GTK_DELETE_WORD_ENDS:
2864         while (count < 0)
2865         {
2866             start_pos = eel_editable_label_move_backward_word (label, start_pos);
2867             count++;
2868         }
2869         while (count > 0)
2870         {
2871             end_pos = eel_editable_label_move_forward_word (label, end_pos);
2872             count--;
2873         }
2874         start_pos = g_utf8_pointer_to_offset (label->text, label->text + start_pos);
2875         end_pos = g_utf8_pointer_to_offset (label->text, label->text + end_pos);
2876 
2877         gtk_editable_delete_text (GTK_EDITABLE (label), start_pos, end_pos);
2878         break;
2879     case GTK_DELETE_DISPLAY_LINE_ENDS:
2880     case GTK_DELETE_PARAGRAPH_ENDS:
2881         end_pos = g_utf8_pointer_to_offset (label->text, label->text + label->selection_anchor);
2882         if (count < 0)
2883             gtk_editable_delete_text (GTK_EDITABLE (label), 0, end_pos);
2884         else
2885             gtk_editable_delete_text (GTK_EDITABLE (label), end_pos, -1);
2886         break;
2887     case GTK_DELETE_DISPLAY_LINES:
2888     case GTK_DELETE_PARAGRAPHS:
2889         gtk_editable_delete_text (GTK_EDITABLE (label), 0, -1);
2890         break;
2891     case GTK_DELETE_WHITESPACE:
2892         /* TODO eel_editable_label_delete_whitespace (label); */
2893         break;
2894     }
2895 
2896     eel_editable_label_pend_cursor_blink (label);
2897 }
2898 
2899 
2900 static void
eel_editable_label_copy_clipboard(EelEditableLabel * label)2901 eel_editable_label_copy_clipboard (EelEditableLabel *label)
2902 {
2903     if (label->text)
2904     {
2905         gint start, end;
2906         gint len;
2907 
2908         start = MIN (label->selection_anchor,
2909                      label->selection_end);
2910         end = MAX (label->selection_anchor,
2911                    label->selection_end);
2912 
2913         len = strlen (label->text);
2914 
2915         if (end > len)
2916             end = len;
2917 
2918         if (start > len)
2919             start = len;
2920 
2921         if (start != end)
2922             gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
2923                                     label->text + start, end - start);
2924     }
2925 }
2926 
2927 static void
eel_editable_label_cut_clipboard(EelEditableLabel * label)2928 eel_editable_label_cut_clipboard (EelEditableLabel *label)
2929 {
2930     if (label->text)
2931     {
2932         gint start, end;
2933         gint len;
2934 
2935         start = MIN (label->selection_anchor,
2936                      label->selection_end);
2937         end = MAX (label->selection_anchor,
2938                    label->selection_end);
2939 
2940         len = strlen (label->text);
2941 
2942         if (end > len)
2943             end = len;
2944 
2945         if (start > len)
2946             start = len;
2947 
2948         if (start != end)
2949         {
2950             gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
2951                                     label->text + start, end - start);
2952             start = g_utf8_pointer_to_offset (label->text, label->text + start);
2953             end = g_utf8_pointer_to_offset (label->text, label->text + end);
2954             gtk_editable_delete_text (GTK_EDITABLE (label), start, end);
2955         }
2956     }
2957 }
2958 
2959 static void
paste_received(GtkClipboard * clipboard,const gchar * text,gpointer data)2960 paste_received (GtkClipboard *clipboard,
2961                 const gchar  *text,
2962                 gpointer      data)
2963 {
2964     EelEditableLabel *label = EEL_EDITABLE_LABEL (data);
2965     GtkEditable *editable = GTK_EDITABLE (label);
2966     gint tmp_pos;
2967 
2968     if (text)
2969     {
2970         if (label->selection_end != label->selection_anchor)
2971             gtk_editable_delete_selection (editable);
2972 
2973         tmp_pos = g_utf8_pointer_to_offset (label->text,
2974                                             label->text + label->selection_anchor);
2975         gtk_editable_insert_text (GTK_EDITABLE (label), text, strlen (text), &tmp_pos);
2976         tmp_pos = g_utf8_offset_to_pointer (label->text, tmp_pos) - label->text;
2977         eel_editable_label_select_region_index (label, tmp_pos, tmp_pos);
2978     }
2979 
2980     g_object_unref (G_OBJECT (label));
2981 }
2982 
2983 static void
eel_editable_label_paste(EelEditableLabel * label,GdkAtom selection)2984 eel_editable_label_paste (EelEditableLabel *label,
2985                           GdkAtom   selection)
2986 {
2987     g_object_ref (G_OBJECT (label));
2988     gtk_clipboard_request_text (gtk_widget_get_clipboard (GTK_WIDGET (label), selection),
2989                                 paste_received, label);
2990 }
2991 
2992 static void
eel_editable_label_paste_clipboard(EelEditableLabel * label)2993 eel_editable_label_paste_clipboard (EelEditableLabel *label)
2994 {
2995     eel_editable_label_paste (label, GDK_NONE);
2996 }
2997 
2998 static void
eel_editable_label_select_all(EelEditableLabel * label)2999 eel_editable_label_select_all (EelEditableLabel *label)
3000 {
3001     eel_editable_label_select_region_index (label, 0, strlen (label->text));
3002 }
3003 
3004 /* Quick hack of a popup menu
3005  */
3006 static void
activate_cb(GtkWidget * menuitem,EelEditableLabel * label)3007 activate_cb (GtkWidget *menuitem,
3008              EelEditableLabel  *label)
3009 {
3010     const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
3011     g_signal_emit_by_name (label, signal);
3012 }
3013 
3014 static void
append_action_signal(EelEditableLabel * label,GtkWidget * menu,const gchar * icon_name,const gchar * label_name,const gchar * signal,gboolean sensitive)3015 append_action_signal (EelEditableLabel     *label,
3016                       GtkWidget    *menu,
3017                       const gchar  *icon_name,
3018                       const gchar  *label_name,
3019                       const gchar  *signal,
3020                       gboolean      sensitive)
3021 {
3022     GtkWidget *menuitem = eel_image_menu_item_new_from_icon (icon_name, label_name);
3023 
3024     g_object_set_data (G_OBJECT (menuitem), "gtk-signal", (char *)signal);
3025     g_signal_connect (menuitem, "activate",
3026                       G_CALLBACK (activate_cb), label);
3027 
3028     gtk_widget_set_sensitive (menuitem, sensitive);
3029 
3030     gtk_widget_show (menuitem);
3031     gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
3032 }
3033 
3034 static void
popup_menu_detach(GtkWidget * attach_widget,GtkMenu * menu)3035 popup_menu_detach (GtkWidget *attach_widget,
3036                    GtkMenu   *menu)
3037 {
3038     EelEditableLabel *label;
3039     label = EEL_EDITABLE_LABEL (attach_widget);
3040 
3041     label->popup_menu = NULL;
3042 }
3043 
3044 static void
eel_editable_label_toggle_overwrite(EelEditableLabel * label)3045 eel_editable_label_toggle_overwrite (EelEditableLabel *label)
3046 {
3047     label->overwrite_mode = !label->overwrite_mode;
3048     gtk_widget_queue_draw (GTK_WIDGET (label));
3049 }
3050 
3051 typedef struct
3052 {
3053     EelEditableLabel *label;
3054     gint button;
3055     guint time;
3056 } PopupInfo;
3057 
3058 static void
popup_targets_received(GtkClipboard * clipboard,GtkSelectionData * data,gpointer user_data)3059 popup_targets_received (GtkClipboard     *clipboard,
3060                         GtkSelectionData *data,
3061                         gpointer          user_data)
3062 {
3063     gboolean have_selection;
3064     gboolean clipboard_contains_text;
3065     PopupInfo *info;
3066     EelEditableLabel *label;
3067 
3068     info = user_data;
3069     label = info->label;
3070 
3071     if (gtk_widget_get_realized (GTK_WIDGET (label)))
3072     {
3073         GtkWidget *menuitem;
3074 
3075         if (label->popup_menu)
3076             gtk_widget_destroy (label->popup_menu);
3077 
3078         label->popup_menu = gtk_menu_new ();
3079 
3080         gtk_menu_set_reserve_toggle_size (GTK_MENU (label->popup_menu), FALSE);
3081 
3082         gtk_menu_attach_to_widget (GTK_MENU (label->popup_menu),
3083                                    GTK_WIDGET (label),
3084                                    popup_menu_detach);
3085 
3086         have_selection =
3087             label->selection_anchor != label->selection_end;
3088 
3089         clipboard_contains_text = gtk_selection_data_targets_include_text (data);
3090 
3091         append_action_signal (label, label->popup_menu, "edit-cut", _("Cu_t"), "cut_clipboard",
3092                               have_selection);
3093         append_action_signal (label, label->popup_menu, "edit-copy", _("_Copy"), "copy_clipboard",
3094                               have_selection);
3095         append_action_signal (label, label->popup_menu, "edit-paste", _("_Paste"), "paste_clipboard",
3096                               clipboard_contains_text);
3097 
3098         menuitem = eel_image_menu_item_new_from_icon ("edit-select-all", _("Select All"));
3099         g_signal_connect_object (menuitem, "activate",
3100                                  G_CALLBACK (eel_editable_label_select_all), label,
3101                                  G_CONNECT_SWAPPED);
3102         gtk_widget_show (menuitem);
3103         gtk_menu_shell_append (GTK_MENU_SHELL (label->popup_menu), menuitem);
3104 
3105         g_signal_emit (label,
3106                        signals[POPULATE_POPUP], 0,
3107                        label->popup_menu);
3108 
3109         if (info->button)
3110             gtk_menu_popup_at_pointer (GTK_MENU (label->popup_menu), NULL);
3111         else
3112         {
3113             gtk_menu_popup_at_pointer (GTK_MENU (label->popup_menu), NULL);
3114             gtk_menu_shell_select_first (GTK_MENU_SHELL (label->popup_menu), FALSE);
3115         }
3116     }
3117 
3118     g_object_unref (label);
3119     g_free (info);
3120 }
3121 
3122 static void
eel_editable_label_do_popup(EelEditableLabel * label,GdkEventButton * event)3123 eel_editable_label_do_popup (EelEditableLabel *label,
3124                              GdkEventButton   *event)
3125 {
3126     PopupInfo *info = g_new (PopupInfo, 1);
3127 
3128     /* In order to know what entries we should make sensitive, we
3129      * ask for the current targets of the clipboard, and when
3130      * we get them, then we actually pop up the menu.
3131      */
3132     info->label = g_object_ref (label);
3133 
3134     if (event)
3135     {
3136         info->button = event->button;
3137         info->time = event->time;
3138     }
3139     else
3140     {
3141         info->button = 0;
3142         info->time = gtk_get_current_event_time ();
3143     }
3144 
3145     gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD),
3146                                     gdk_atom_intern ("TARGETS", FALSE),
3147                                     popup_targets_received,
3148                                     info);
3149 }
3150 
3151 /************ Editable implementation ****************/
3152 
3153 static void
editable_insert_text_emit(GtkEditable * editable,const gchar * new_text,gint new_text_length,gint * position)3154 editable_insert_text_emit  (GtkEditable *editable,
3155                             const gchar *new_text,
3156                             gint         new_text_length,
3157                             gint        *position)
3158 {
3159     EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
3160     gchar buf[64];
3161     gchar *text;
3162     int text_length;
3163 
3164     text_length = g_utf8_strlen (label->text, -1);
3165 
3166     if (*position < 0 || *position > text_length)
3167         *position = text_length;
3168 
3169     g_object_ref (G_OBJECT (editable));
3170 
3171     if (new_text_length <= 63)
3172         text = buf;
3173     else
3174         text = g_new (gchar, new_text_length + 1);
3175 
3176     text[new_text_length] = '\0';
3177     strncpy (text, new_text, new_text_length);
3178 
3179     g_signal_emit_by_name (editable, "insert_text", text, new_text_length, position);
3180 
3181     if (new_text_length > 63)
3182         g_free (text);
3183 
3184     g_object_unref (G_OBJECT (editable));
3185 }
3186 
3187 static void
editable_delete_text_emit(GtkEditable * editable,gint start_pos,gint end_pos)3188 editable_delete_text_emit (GtkEditable *editable,
3189                            gint         start_pos,
3190                            gint         end_pos)
3191 {
3192     EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
3193     int text_length;
3194 
3195     text_length = g_utf8_strlen (label->text, -1);
3196 
3197     if (end_pos < 0 || end_pos > text_length)
3198         end_pos = text_length;
3199     if (start_pos < 0)
3200         start_pos = 0;
3201     if (start_pos > end_pos)
3202         start_pos = end_pos;
3203 
3204     g_object_ref (G_OBJECT (editable));
3205 
3206     g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos);
3207 
3208     g_object_unref (G_OBJECT (editable));
3209 }
3210 
3211 static void
editable_insert_text(GtkEditable * editable,const gchar * new_text,gint new_text_length,gint * position)3212 editable_insert_text (GtkEditable *editable,
3213                       const gchar *new_text,
3214                       gint         new_text_length,
3215                       gint        *position)
3216 {
3217     EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
3218     gint index;
3219 
3220     if (new_text_length < 0)
3221         new_text_length = strlen (new_text);
3222 
3223     index = g_utf8_offset_to_pointer (label->text, *position) - label->text;
3224 
3225     eel_editable_label_insert_text (label,
3226                                     new_text,
3227                                     new_text_length,
3228                                     &index);
3229 
3230     *position = g_utf8_pointer_to_offset (label->text, label->text + index);
3231 }
3232 
3233 static void
editable_delete_text(GtkEditable * editable,gint start_pos,gint end_pos)3234 editable_delete_text (GtkEditable *editable,
3235                       gint         start_pos,
3236                       gint         end_pos)
3237 {
3238     EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
3239     int text_length;
3240     gint start_index, end_index;
3241 
3242     text_length = g_utf8_strlen (label->text, -1);
3243 
3244     if (end_pos < 0 || end_pos > text_length)
3245         end_pos = text_length;
3246     if (start_pos < 0)
3247         start_pos = 0;
3248     if (start_pos > end_pos)
3249         start_pos = end_pos;
3250 
3251     start_index = g_utf8_offset_to_pointer (label->text, start_pos) - label->text;
3252     end_index = g_utf8_offset_to_pointer (label->text, end_pos) - label->text;
3253 
3254     eel_editable_label_delete_text (label, start_index, end_index);
3255 }
3256 
3257 static gchar *
editable_get_chars(GtkEditable * editable,gint start_pos,gint end_pos)3258 editable_get_chars (GtkEditable   *editable,
3259                     gint           start_pos,
3260                     gint           end_pos)
3261 {
3262     EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
3263     int text_length;
3264     gint start_index, end_index;
3265 
3266     text_length = g_utf8_strlen (label->text, -1);
3267 
3268     if (end_pos < 0 || end_pos > text_length)
3269         end_pos = text_length;
3270     if (start_pos < 0)
3271         start_pos = 0;
3272     if (start_pos > end_pos)
3273         start_pos = end_pos;
3274 
3275     start_index = g_utf8_offset_to_pointer (label->text, start_pos) - label->text;
3276     end_index = g_utf8_offset_to_pointer (label->text, end_pos) - label->text;
3277 
3278     return g_strndup (label->text + start_index, end_index - start_index);
3279 }
3280 
3281 static void
editable_set_selection_bounds(GtkEditable * editable,gint start,gint end)3282 editable_set_selection_bounds (GtkEditable *editable,
3283                                gint         start,
3284                                gint         end)
3285 {
3286     EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
3287     int text_length;
3288     gint start_index, end_index;
3289 
3290     text_length = g_utf8_strlen (label->text, -1);
3291 
3292     if (end < 0 || end > text_length)
3293         end = text_length;
3294     if (start < 0)
3295         start = text_length;
3296     if (start > text_length)
3297         start = text_length;
3298 
3299     eel_editable_label_reset_im_context (label);
3300 
3301     start_index = g_utf8_offset_to_pointer (label->text, start) - label->text;
3302     end_index = g_utf8_offset_to_pointer (label->text, end) - label->text;
3303 
3304     eel_editable_label_select_region_index (label, start_index, end_index);
3305 }
3306 
3307 static gboolean
editable_get_selection_bounds(GtkEditable * editable,gint * start,gint * end)3308 editable_get_selection_bounds (GtkEditable *editable,
3309                                gint        *start,
3310                                gint        *end)
3311 {
3312     EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
3313 
3314     *start = g_utf8_pointer_to_offset (label->text, label->text + label->selection_anchor);
3315     *end = g_utf8_pointer_to_offset (label->text, label->text + label->selection_end);
3316 
3317     return (label->selection_anchor != label->selection_end);
3318 }
3319 
3320 static void
editable_real_set_position(GtkEditable * editable,gint position)3321 editable_real_set_position (GtkEditable *editable,
3322                             gint         position)
3323 {
3324     EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
3325     int text_length;
3326     int index;
3327 
3328     text_length = g_utf8_strlen (label->text, -1);
3329 
3330     if (position < 0 || position > text_length)
3331         position = text_length;
3332 
3333     index = g_utf8_offset_to_pointer (label->text, position) - label->text;
3334 
3335     if (index != label->selection_anchor ||
3336             index != label->selection_end)
3337     {
3338         eel_editable_label_select_region_index (label, index, index);
3339     }
3340 }
3341 
3342 static gint
editable_get_position(GtkEditable * editable)3343 editable_get_position (GtkEditable *editable)
3344 {
3345     EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
3346 
3347     return g_utf8_pointer_to_offset (label->text, label->text + label->selection_anchor);
3348 }
3349 
3350 
3351 static AtkObjectClass *a11y_parent_class = NULL;
3352 
3353 static const char* eel_editable_label_accessible_data = "eel-editable-label-accessible-data";
3354 
3355 /************ Accessible implementation ****************/
3356 
3357 typedef struct
3358 {
3359     GailTextUtil *textutil;
3360     gint         selection_anchor;
3361     gint         selection_end;
3362     gchar        *signal_name;
3363     gint         position;
3364     gint         length;
3365 } EelEditableLabelAccessiblePrivate;
3366 
3367 typedef struct
3368 {
3369     EelEditableLabel* label;
3370     gint position;
3371 } EelEditableLabelAccessiblePaste;
3372 
3373 typedef struct _EelEditableLabelAccessible EelEditableLabelAccessible;
3374 typedef struct _EelEditableLabelAccessibleClass EelEditableLabelAccessibleClass;
3375 
3376 struct _EelEditableLabelAccessible
3377 {
3378     GtkWidgetAccessible parent;
3379 };
3380 
3381 struct _EelEditableLabelAccessibleClass
3382 {
3383     GtkWidgetAccessibleClass parent_class;
3384 };
3385 
3386 static gchar*
eel_editable_label_accessible_get_text(AtkText * text,gint start_pos,gint end_pos)3387 eel_editable_label_accessible_get_text (AtkText *text,
3388                                         gint    start_pos,
3389                                         gint    end_pos)
3390 {
3391     GtkWidget *widget;
3392     EelEditableLabelAccessiblePrivate *priv;
3393 
3394     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3395     if (widget == NULL)
3396         /* State is defunct */
3397         return NULL;
3398 
3399     priv = g_object_get_data (G_OBJECT (text), eel_editable_label_accessible_data);
3400     return gail_text_util_get_substring (priv->textutil, start_pos, end_pos);
3401 }
3402 
3403 static gunichar
eel_editable_label_accessible_get_character_at_offset(AtkText * text,gint offset)3404 eel_editable_label_accessible_get_character_at_offset (AtkText *text,
3405         gint     offset)
3406 {
3407     GtkWidget *widget;
3408     EelEditableLabelAccessiblePrivate *priv;
3409     gchar *string;
3410     gunichar unichar;
3411 
3412     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3413     if (widget == NULL)
3414         /* State is defunct */
3415         return '\0';
3416 
3417     priv = g_object_get_data (G_OBJECT (text), eel_editable_label_accessible_data);
3418     string = gail_text_util_get_substring (priv->textutil, 0, -1);
3419     if (offset >= g_utf8_strlen (string, -1))
3420     {
3421         unichar = '\0';
3422     }
3423     else
3424     {
3425         gchar *index;
3426 
3427         index = g_utf8_offset_to_pointer (string, offset);
3428 
3429         unichar = g_utf8_get_char(index);
3430     }
3431 
3432     g_free(string);
3433     return unichar;
3434 }
3435 
3436 static gchar*
eel_editable_label_accessible_get_text_before_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)3437 eel_editable_label_accessible_get_text_before_offset (AtkText         *text,
3438         gint            offset,
3439         AtkTextBoundary boundary_type,
3440         gint            *start_offset,
3441         gint            *end_offset)
3442 {
3443     GtkWidget *widget;
3444     EelEditableLabel *label;
3445     EelEditableLabelAccessiblePrivate *priv;
3446 
3447     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3448     if (widget == NULL)
3449         /* State is defunct */
3450         return NULL;
3451 
3452     label = EEL_EDITABLE_LABEL (widget);
3453     priv = g_object_get_data (G_OBJECT (text), eel_editable_label_accessible_data);
3454 
3455     return gail_text_util_get_text (priv->textutil,
3456                                     eel_editable_label_get_layout (label),
3457                                     GAIL_BEFORE_OFFSET,
3458                                     boundary_type, offset,
3459                                     start_offset, end_offset);
3460 }
3461 
3462 static gchar*
eel_editable_label_accessible_get_text_at_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)3463 eel_editable_label_accessible_get_text_at_offset (AtkText          *text,
3464         gint             offset,
3465         AtkTextBoundary  boundary_type,
3466         gint             *start_offset,
3467         gint             *end_offset)
3468 {
3469     GtkWidget *widget;
3470     EelEditableLabel *label;
3471     EelEditableLabelAccessiblePrivate *priv;
3472 
3473     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3474     if (widget == NULL)
3475         /* State is defunct */
3476         return NULL;
3477 
3478 
3479     label = EEL_EDITABLE_LABEL (widget);
3480     priv = g_object_get_data (G_OBJECT (text), eel_editable_label_accessible_data);
3481     return gail_text_util_get_text (priv->textutil,
3482                                     eel_editable_label_get_layout (label),
3483                                     GAIL_AT_OFFSET,
3484                                     boundary_type, offset,
3485                                     start_offset, end_offset);
3486 }
3487 
3488 static gchar*
eel_editable_label_accessible_get_text_after_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)3489 eel_editable_label_accessible_get_text_after_offset  (AtkText          *text,
3490         gint             offset,
3491         AtkTextBoundary  boundary_type,
3492         gint             *start_offset,
3493         gint             *end_offset)
3494 {
3495     GtkWidget *widget;
3496     EelEditableLabel *label;
3497     EelEditableLabelAccessiblePrivate *priv;
3498 
3499     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3500     if (widget == NULL)
3501         /* State is defunct */
3502         return NULL;
3503 
3504     label = EEL_EDITABLE_LABEL (widget);
3505     priv = g_object_get_data (G_OBJECT (text), eel_editable_label_accessible_data);
3506     return gail_text_util_get_text (priv->textutil,
3507                                     eel_editable_label_get_layout (label),
3508                                     GAIL_AFTER_OFFSET,
3509                                     boundary_type, offset,
3510                                     start_offset, end_offset);
3511 }
3512 
3513 static gint
eel_editable_label_accessible_get_caret_offset(AtkText * text)3514 eel_editable_label_accessible_get_caret_offset (AtkText *text)
3515 {
3516     GtkWidget *widget;
3517 
3518     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3519     if (widget == NULL)
3520         /* State is defunct */
3521         return 0;
3522 
3523     return gtk_editable_get_position (GTK_EDITABLE (widget));
3524 }
3525 
3526 static gboolean
eel_editable_label_accessible_set_caret_offset(AtkText * text,gint offset)3527 eel_editable_label_accessible_set_caret_offset (AtkText *text, gint offset)
3528 {
3529     GtkWidget *widget;
3530 
3531     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3532     if (widget == NULL)
3533         /* State is defunct */
3534         return FALSE;
3535 
3536     gtk_editable_set_position (GTK_EDITABLE (widget), offset);
3537     return TRUE;
3538 }
3539 
3540 static gint
eel_editable_label_accessible_get_character_count(AtkText * text)3541 eel_editable_label_accessible_get_character_count (AtkText *text)
3542 {
3543     GtkWidget *widget;
3544     EelEditableLabel *label;
3545 
3546     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3547     if (widget == NULL)
3548         /* State is defunct */
3549         return 0;
3550 
3551     label = EEL_EDITABLE_LABEL (widget);
3552     return g_utf8_strlen (eel_editable_label_get_text (label), -1);
3553 }
3554 
3555 static gint
eel_editable_label_accessible_get_n_selections(AtkText * text)3556 eel_editable_label_accessible_get_n_selections (AtkText *text)
3557 {
3558     GtkWidget *widget;
3559     EelEditableLabel *label;
3560     gint select_start, select_end;
3561 
3562     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3563     if (widget == NULL)
3564         /* State is defunct */
3565         return -1;
3566 
3567     label = EEL_EDITABLE_LABEL (widget);
3568     gtk_editable_get_selection_bounds (GTK_EDITABLE (label), &select_start,
3569                                        &select_end);
3570 
3571     if (select_start != select_end)
3572         return 1;
3573     else
3574         return 0;
3575 }
3576 
3577 static gchar*
eel_editable_label_accessible_get_selection(AtkText * text,gint selection_num,gint * start_pos,gint * end_pos)3578 eel_editable_label_accessible_get_selection (AtkText *text,
3579         gint    selection_num,
3580         gint    *start_pos,
3581         gint    *end_pos)
3582 {
3583     GtkWidget *widget;
3584     EelEditableLabel *label;
3585 
3586     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3587     if (widget == NULL)
3588         /* State is defunct */
3589         return NULL;
3590 
3591     /* Only let the user get the selection if one is set, and if the
3592      * selection_num is 0.
3593      */
3594     if (selection_num != 0)
3595         return NULL;
3596 
3597     label = EEL_EDITABLE_LABEL (widget);
3598     gtk_editable_get_selection_bounds (GTK_EDITABLE (label), start_pos, end_pos);
3599 
3600     if (*start_pos != *end_pos)
3601         return gtk_editable_get_chars (GTK_EDITABLE (label), *start_pos, *end_pos);
3602     else
3603         return NULL;
3604 }
3605 
3606 static gboolean
eel_editable_label_accessible_add_selection(AtkText * text,gint start_pos,gint end_pos)3607 eel_editable_label_accessible_add_selection (AtkText *text,
3608         gint    start_pos,
3609         gint    end_pos)
3610 {
3611     GtkWidget *widget;
3612     EelEditableLabel *label;
3613     gint select_start, select_end;
3614 
3615     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3616     if (widget == NULL)
3617         /* State is defunct */
3618         return FALSE;
3619 
3620     label = EEL_EDITABLE_LABEL (widget);
3621     gtk_editable_get_selection_bounds (GTK_EDITABLE (label), &select_start,
3622                                        &select_end);
3623 
3624     /* If there is already a selection, then don't allow another to be added,
3625      * since EelEditableLabel only supports one selected region.
3626      */
3627     if (select_start == select_end)
3628     {
3629         gtk_editable_select_region (GTK_EDITABLE (label), start_pos, end_pos);
3630         return TRUE;
3631     }
3632     else
3633         return FALSE;
3634 }
3635 
3636 static gboolean
eel_editable_label_accessible_remove_selection(AtkText * text,gint selection_num)3637 eel_editable_label_accessible_remove_selection (AtkText *text,
3638         gint    selection_num)
3639 {
3640     GtkWidget *widget;
3641     EelEditableLabel *label;
3642     gint select_start, select_end, caret_pos;
3643 
3644     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3645     if (widget == NULL)
3646         /* State is defunct */
3647         return FALSE;
3648 
3649     if (selection_num != 0)
3650         return FALSE;
3651 
3652     label = EEL_EDITABLE_LABEL (widget);
3653     gtk_editable_get_selection_bounds (GTK_EDITABLE (label), &select_start,
3654                                        &select_end);
3655 
3656     if (select_start != select_end)
3657     {
3658         /* Setting the start & end of the selected region to the caret position
3659          * turns off the selection.
3660          */
3661         caret_pos = gtk_editable_get_position (GTK_EDITABLE (label));
3662         gtk_editable_select_region (GTK_EDITABLE (label), caret_pos, caret_pos);
3663         return TRUE;
3664     }
3665     else
3666         return FALSE;
3667 }
3668 
3669 static gboolean
eel_editable_label_accessible_set_selection(AtkText * text,gint selection_num,gint start_pos,gint end_pos)3670 eel_editable_label_accessible_set_selection (AtkText *text,
3671         gint    selection_num,
3672         gint    start_pos,
3673         gint    end_pos)
3674 {
3675     GtkWidget *widget;
3676     EelEditableLabel *label;
3677     gint select_start, select_end;
3678 
3679     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3680     if (widget == NULL)
3681         /* State is defunct */
3682         return FALSE;
3683 
3684     /* Only let the user move the selection if one is set, and if the
3685      * selection_num is 0
3686      */
3687     if (selection_num != 0)
3688         return FALSE;
3689 
3690     label = EEL_EDITABLE_LABEL (widget);
3691     gtk_editable_get_selection_bounds (GTK_EDITABLE (label), &select_start,
3692                                        &select_end);
3693 
3694     if (select_start != select_end)
3695     {
3696         gtk_editable_select_region (GTK_EDITABLE (label), start_pos, end_pos);
3697         return TRUE;
3698     }
3699     else
3700         return FALSE;
3701 }
3702 
3703 static AtkAttributeSet*
eel_editable_label_accessible_get_run_attributes(AtkText * text,gint offset,gint * start_offset,gint * end_offset)3704 eel_editable_label_accessible_get_run_attributes (AtkText *text,
3705         gint    offset,
3706         gint    *start_offset,
3707         gint    *end_offset)
3708 {
3709     GtkWidget *widget;
3710     EelEditableLabel *label;
3711     AtkAttributeSet *at_set = NULL;
3712     GtkTextDirection dir;
3713 
3714     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3715     if (widget == NULL)
3716         /* State is defunct */
3717         return NULL;
3718 
3719     label = EEL_EDITABLE_LABEL (widget);
3720 
3721     dir = gtk_widget_get_direction (widget);
3722     if (dir == GTK_TEXT_DIR_RTL)
3723     {
3724         at_set = gail_misc_add_attribute (at_set,
3725                                           ATK_TEXT_ATTR_DIRECTION,
3726                                           g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
3727     }
3728 
3729     at_set = gail_misc_layout_get_run_attributes (at_set,
3730              eel_editable_label_get_layout (label),
3731              label->text,
3732              offset,
3733              start_offset,
3734              end_offset);
3735     return at_set;
3736 }
3737 
3738 static AtkAttributeSet*
eel_editable_label_accessible_get_default_attributes(AtkText * text)3739 eel_editable_label_accessible_get_default_attributes (AtkText *text)
3740 {
3741     GtkWidget *widget;
3742     EelEditableLabel *label;
3743     AtkAttributeSet *at_set = NULL;
3744 
3745     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3746     if (widget == NULL)
3747         /* State is defunct */
3748         return NULL;
3749 
3750     label = EEL_EDITABLE_LABEL (widget);
3751 
3752     at_set = gail_misc_get_default_attributes (at_set,
3753              eel_editable_label_get_layout (label),
3754              widget);
3755     return at_set;
3756 }
3757 
3758 static void
eel_editable_label_accessible_get_character_extents(AtkText * text,gint offset,gint * x,gint * y,gint * width,gint * height,AtkCoordType coords)3759 eel_editable_label_accessible_get_character_extents (AtkText      *text,
3760         gint         offset,
3761         gint         *x,
3762         gint         *y,
3763         gint         *width,
3764         gint         *height,
3765         AtkCoordType coords)
3766 {
3767     GtkWidget *widget;
3768     EelEditableLabel *label;
3769     PangoRectangle char_rect;
3770     gint index, cursor_index, x_layout, y_layout;
3771 
3772     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3773     if (widget == NULL)
3774         /* State is defunct */
3775         return;
3776 
3777     label = EEL_EDITABLE_LABEL (widget);
3778     eel_editable_label_get_layout_offsets (label, &x_layout, &y_layout);
3779     index = g_utf8_offset_to_pointer (label->text, offset) - label->text;
3780     cursor_index = label->selection_anchor;
3781     if (index > cursor_index)
3782         index += label->preedit_length;
3783     pango_layout_index_to_pos (eel_editable_label_get_layout(label), index, &char_rect);
3784 
3785     gail_misc_get_extents_from_pango_rectangle (widget, &char_rect,
3786             x_layout, y_layout, x, y, width, height, coords);
3787 }
3788 
3789 static gint
eel_editable_label_accessible_get_offset_at_point(AtkText * text,gint x,gint y,AtkCoordType coords)3790 eel_editable_label_accessible_get_offset_at_point (AtkText      *text,
3791         gint         x,
3792         gint         y,
3793         AtkCoordType coords)
3794 {
3795     GtkWidget *widget;
3796     EelEditableLabel *label;
3797     gint index, cursor_index, x_layout, y_layout;
3798 
3799     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3800     if (widget == NULL)
3801         /* State is defunct */
3802         return -1;
3803 
3804     label = EEL_EDITABLE_LABEL (widget);
3805 
3806     eel_editable_label_get_layout_offsets (label, &x_layout, &y_layout);
3807 
3808     index = gail_misc_get_index_at_point_in_layout (widget,
3809             eel_editable_label_get_layout(label), x_layout, y_layout, x, y, coords);
3810     if (index == -1)
3811     {
3812         if (coords == ATK_XY_SCREEN || coords == ATK_XY_WINDOW)
3813             return g_utf8_strlen (label->text, -1);
3814 
3815         return index;
3816     }
3817     else
3818     {
3819         cursor_index = label->selection_anchor;
3820         if (index >= cursor_index && label->preedit_length)
3821         {
3822             if (index >= cursor_index + label->preedit_length)
3823                 index -= label->preedit_length;
3824             else
3825                 index = cursor_index;
3826         }
3827         return g_utf8_pointer_to_offset (label->text, label->text + index);
3828     }
3829 }
3830 
3831 static void
atk_text_interface_init(AtkTextIface * iface)3832 atk_text_interface_init (AtkTextIface *iface)
3833 {
3834     g_assert (iface != NULL);
3835 
3836     iface->get_text = eel_editable_label_accessible_get_text;
3837     iface->get_character_at_offset = eel_editable_label_accessible_get_character_at_offset;
3838     iface->get_text_before_offset = eel_editable_label_accessible_get_text_before_offset;
3839     iface->get_text_at_offset = eel_editable_label_accessible_get_text_at_offset;
3840     iface->get_text_after_offset = eel_editable_label_accessible_get_text_after_offset;
3841     iface->get_caret_offset = eel_editable_label_accessible_get_caret_offset;
3842     iface->set_caret_offset = eel_editable_label_accessible_set_caret_offset;
3843     iface->get_character_count = eel_editable_label_accessible_get_character_count;
3844     iface->get_n_selections = eel_editable_label_accessible_get_n_selections;
3845     iface->get_selection = eel_editable_label_accessible_get_selection;
3846     iface->add_selection = eel_editable_label_accessible_add_selection;
3847     iface->remove_selection = eel_editable_label_accessible_remove_selection;
3848     iface->set_selection = eel_editable_label_accessible_set_selection;
3849     iface->get_run_attributes = eel_editable_label_accessible_get_run_attributes;
3850     iface->get_default_attributes = eel_editable_label_accessible_get_default_attributes;
3851     iface->get_character_extents = eel_editable_label_accessible_get_character_extents;
3852     iface->get_offset_at_point = eel_editable_label_accessible_get_offset_at_point;
3853 }
3854 
3855 static void
eel_editable_label_accessible_set_text_contents(AtkEditableText * text,const gchar * string)3856 eel_editable_label_accessible_set_text_contents (AtkEditableText *text,
3857         const gchar     *string)
3858 {
3859     GtkWidget *widget;
3860     EelEditableLabel *label;
3861 
3862     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3863     if (widget == NULL)
3864         /* State is defunct */
3865         return;
3866 
3867     label = EEL_EDITABLE_LABEL (widget);
3868 
3869     eel_editable_label_set_text (label, string);
3870 }
3871 
3872 static void
eel_editable_label_accessible_insert_text(AtkEditableText * text,const gchar * string,gint length,gint * position)3873 eel_editable_label_accessible_insert_text (AtkEditableText *text,
3874         const gchar     *string,
3875         gint            length,
3876         gint            *position)
3877 {
3878     GtkWidget *widget;
3879     EelEditableLabel *label;
3880     GtkEditable *editable;
3881 
3882     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3883     if (widget == NULL)
3884         /* State is defunct */
3885         return;
3886 
3887     label = EEL_EDITABLE_LABEL (widget);
3888     editable = GTK_EDITABLE (label);
3889 
3890     gtk_editable_insert_text (editable, string, length, position);
3891 }
3892 
3893 static void
eel_editable_label_accessible_copy_text(AtkEditableText * text,gint start_pos,gint end_pos)3894 eel_editable_label_accessible_copy_text   (AtkEditableText *text,
3895         gint            start_pos,
3896         gint            end_pos)
3897 {
3898     GtkWidget *widget;
3899     EelEditableLabel *label;
3900     GtkEditable *editable;
3901     gchar *str;
3902 
3903     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3904     if (widget == NULL)
3905         /* State is defunct */
3906         return;
3907 
3908     label = EEL_EDITABLE_LABEL (widget);
3909     editable = GTK_EDITABLE (label);
3910     str = gtk_editable_get_chars (editable, start_pos, end_pos);
3911     gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1);
3912 }
3913 
3914 static void
eel_editable_label_accessible_cut_text(AtkEditableText * text,gint start_pos,gint end_pos)3915 eel_editable_label_accessible_cut_text (AtkEditableText *text,
3916                                         gint            start_pos,
3917                                         gint            end_pos)
3918 {
3919     GtkWidget *widget;
3920     EelEditableLabel *label;
3921     GtkEditable *editable;
3922     gchar *str;
3923 
3924     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3925     if (widget == NULL)
3926         /* State is defunct */
3927         return;
3928 
3929     label = EEL_EDITABLE_LABEL (widget);
3930     editable = GTK_EDITABLE (label);
3931     str = gtk_editable_get_chars (editable, start_pos, end_pos);
3932     gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1);
3933     gtk_editable_delete_text (editable, start_pos, end_pos);
3934 }
3935 
3936 static void
eel_editable_label_accessible_delete_text(AtkEditableText * text,gint start_pos,gint end_pos)3937 eel_editable_label_accessible_delete_text (AtkEditableText *text,
3938         gint            start_pos,
3939         gint            end_pos)
3940 {
3941     GtkWidget *widget;
3942     EelEditableLabel *label;
3943     GtkEditable *editable;
3944 
3945     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3946     if (widget == NULL)
3947         /* State is defunct */
3948         return;
3949 
3950     label = EEL_EDITABLE_LABEL (widget);
3951     editable = GTK_EDITABLE (label);
3952 
3953     gtk_editable_delete_text (editable, start_pos, end_pos);
3954 }
3955 
3956 static void
eel_editable_label_accessible_paste_received(GtkClipboard * clipboard,const gchar * text,gpointer data)3957 eel_editable_label_accessible_paste_received (GtkClipboard *clipboard,
3958         const gchar  *text,
3959         gpointer     data)
3960 {
3961     EelEditableLabelAccessiblePaste* paste_struct = (EelEditableLabelAccessiblePaste *)data;
3962 
3963     if (text)
3964         gtk_editable_insert_text (GTK_EDITABLE (paste_struct->label), text, -1,
3965                                   &(paste_struct->position));
3966 
3967     g_object_unref (paste_struct->label);
3968 }
3969 
3970 static void
eel_editable_label_accessible_paste_text(AtkEditableText * text,gint position)3971 eel_editable_label_accessible_paste_text (AtkEditableText *text,
3972         gint            position)
3973 {
3974     GtkWidget *widget;
3975     GtkEditable *editable;
3976     EelEditableLabelAccessiblePaste paste_struct;
3977 
3978     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
3979     if (widget == NULL)
3980         /* State is defunct */
3981         return;
3982 
3983     editable = GTK_EDITABLE (widget);
3984     if (!gtk_editable_get_editable (editable))
3985         return;
3986     paste_struct.label = EEL_EDITABLE_LABEL (widget);
3987     paste_struct.position = position;
3988 
3989     g_object_ref (paste_struct.label);
3990     gtk_clipboard_request_text (gtk_clipboard_get (GDK_NONE),
3991                                 eel_editable_label_accessible_paste_received, &paste_struct);
3992 }
3993 
3994 static void
atk_editable_text_interface_init(AtkEditableTextIface * iface)3995 atk_editable_text_interface_init (AtkEditableTextIface *iface)
3996 {
3997     g_assert (iface != NULL);
3998 
3999     iface->set_text_contents = eel_editable_label_accessible_set_text_contents;
4000     iface->insert_text = eel_editable_label_accessible_insert_text;
4001     iface->copy_text = eel_editable_label_accessible_copy_text;
4002     iface->cut_text = eel_editable_label_accessible_cut_text;
4003     iface->delete_text = eel_editable_label_accessible_delete_text;
4004     iface->paste_text = eel_editable_label_accessible_paste_text;
4005 }
4006 
4007 static void
eel_editable_label_accessible_notify_insert(AtkObject * accessible)4008 eel_editable_label_accessible_notify_insert (AtkObject *accessible)
4009 {
4010     EelEditableLabelAccessiblePrivate *priv;
4011 
4012     priv = g_object_get_data (G_OBJECT (accessible), eel_editable_label_accessible_data);
4013     if (priv->signal_name)
4014     {
4015         g_signal_emit_by_name (accessible,
4016                                priv->signal_name,
4017                                priv->position,
4018                                priv->length);
4019         priv->signal_name = NULL;
4020     }
4021 }
4022 
4023 static gboolean
eel_editable_label_accessible_idle_notify_insert(gpointer data)4024 eel_editable_label_accessible_idle_notify_insert (gpointer data)
4025 {
4026     eel_editable_label_accessible_notify_insert (data);
4027     return FALSE;
4028 }
4029 
4030 /* Note arg1 returns the character at the start of the insert.
4031  * arg2 returns the number of characters inserted.
4032  */
4033 static void
eel_editable_label_accessible_insert_text_cb(EelEditableLabel * label,gchar * arg1,gint arg2,gpointer arg3)4034 eel_editable_label_accessible_insert_text_cb (EelEditableLabel *label,
4035         gchar            *arg1,
4036         gint             arg2,
4037         gpointer         arg3)
4038 {
4039     AtkObject *accessible;
4040     EelEditableLabelAccessiblePrivate *priv;
4041     gint *position = (gint *) arg3;
4042 
4043     accessible = gtk_widget_get_accessible (GTK_WIDGET (label));
4044     priv = g_object_get_data (G_OBJECT (accessible), eel_editable_label_accessible_data);
4045     if (!priv->signal_name)
4046     {
4047         priv->signal_name = "text_changed::insert";
4048         priv->position = *position;
4049         priv->length = arg2;
4050     }
4051     /*
4052      * The signal will be emitted when the cursor position is updated.
4053      * or in an idle handler if it not updated.
4054      */
4055     g_idle_add (eel_editable_label_accessible_idle_notify_insert, accessible);
4056 }
4057 
4058 /* Note arg1 returns the start of the delete range, arg2 returns the
4059  * end of the delete range if multiple characters are deleted.
4060  */
4061 static void
eel_editable_label_accessible_delete_text_cb(EelEditableLabel * label,gint arg1,gint arg2)4062 eel_editable_label_accessible_delete_text_cb (EelEditableLabel *label,
4063         gint             arg1,
4064         gint             arg2)
4065 {
4066     AtkObject *accessible;
4067 
4068     accessible = gtk_widget_get_accessible (GTK_WIDGET (label));
4069 
4070     /*
4071      * Zero length text deleted so ignore
4072      */
4073     if (arg2 - arg1 == 0)
4074         return;
4075 
4076     g_signal_emit_by_name (accessible, "text_changed::delete", arg1, arg2 - arg1);
4077 }
4078 
4079 static void
eel_editable_label_accessible_changed_cb(EelEditableLabel * label)4080 eel_editable_label_accessible_changed_cb (EelEditableLabel *label)
4081 {
4082     AtkObject *accessible;
4083     EelEditableLabelAccessiblePrivate *priv;
4084 
4085     accessible = gtk_widget_get_accessible (GTK_WIDGET (label));
4086     priv = g_object_get_data (G_OBJECT (accessible), eel_editable_label_accessible_data);
4087     gail_text_util_text_setup (priv->textutil, eel_editable_label_get_text (label));
4088 }
4089 
4090 static gboolean
check_for_selection_change(AtkObject * accessible,GtkWidget * widget)4091 check_for_selection_change (AtkObject   *accessible,
4092                             GtkWidget   *widget)
4093 {
4094     EelEditableLabelAccessiblePrivate *priv;
4095     EelEditableLabel *label;
4096     gboolean ret_val = FALSE;
4097 
4098     priv = g_object_get_data (G_OBJECT (accessible), eel_editable_label_accessible_data);
4099     label = EEL_EDITABLE_LABEL (widget);
4100 
4101     if (label->selection_anchor != label->selection_end)
4102     {
4103         if (label->selection_anchor != priv->selection_anchor ||
4104                 label->selection_end != priv->selection_end)
4105             /*
4106              * This check is here as this function can be called
4107              * for notification of selection_end and selection_anchor.
4108              * The values of selection_anchor and selection_end may be the same
4109              * for both notifications and we only want to generate one
4110              * text_selection_changed signal.
4111              */
4112             ret_val = TRUE;
4113     }
4114     else
4115     {
4116         /* We had a selection */
4117         ret_val = (priv->selection_anchor != priv->selection_end);
4118     }
4119     priv->selection_anchor = label->selection_anchor;
4120     priv->selection_end = label->selection_end;
4121 
4122     return ret_val;
4123 }
4124 
4125 static void
eel_editable_label_accessible_notify_gtk(GObject * obj,GParamSpec * pspec)4126 eel_editable_label_accessible_notify_gtk (GObject    *obj,
4127         GParamSpec *pspec)
4128 {
4129     GtkWidget *widget;
4130     AtkObject *accessible;
4131     EelEditableLabel *label;
4132 
4133     widget = GTK_WIDGET (obj);
4134     label = EEL_EDITABLE_LABEL (widget);
4135     accessible = gtk_widget_get_accessible (widget);
4136 
4137     if (strcmp (pspec->name, "cursor-position") == 0)
4138     {
4139         eel_editable_label_accessible_notify_insert (accessible);
4140         if (check_for_selection_change (accessible, widget))
4141             g_signal_emit_by_name (accessible, "text_selection_changed");
4142         /*
4143          * The label cursor position has moved so generate the signal.
4144          */
4145         g_signal_emit_by_name (accessible, "text_caret_moved",
4146                                g_utf8_pointer_to_offset (label->text,
4147                                        label->text + label->selection_anchor));
4148     }
4149     else if (strcmp (pspec->name, "selection-bound") == 0)
4150     {
4151         eel_editable_label_accessible_notify_insert (accessible);
4152 
4153         if (check_for_selection_change (accessible, widget))
4154             g_signal_emit_by_name (accessible, "text_selection_changed");
4155     }
4156 }
4157 
4158 static void
eel_editable_label_accessible_initialize(AtkObject * accessible,gpointer widget)4159 eel_editable_label_accessible_initialize (AtkObject *accessible,
4160         gpointer   widget)
4161 {
4162     EelEditableLabelAccessiblePrivate *priv;
4163     EelEditableLabel *label;
4164 
4165     a11y_parent_class->initialize (accessible, widget);
4166 
4167     label = EEL_EDITABLE_LABEL (widget);
4168     priv = g_new0 (EelEditableLabelAccessiblePrivate, 1);
4169     priv->textutil = gail_text_util_new ();
4170     gail_text_util_text_setup (priv->textutil, eel_editable_label_get_text (EEL_EDITABLE_LABEL (widget)));
4171     priv->selection_anchor = label->selection_anchor;
4172     priv->selection_end = label->selection_end;
4173     g_object_set_data (G_OBJECT (accessible), eel_editable_label_accessible_data, priv);
4174     g_signal_connect (widget, "insert-text",
4175                       G_CALLBACK (eel_editable_label_accessible_insert_text_cb), NULL);
4176     g_signal_connect (widget, "delete-text",
4177                       G_CALLBACK (eel_editable_label_accessible_delete_text_cb), NULL);
4178     g_signal_connect (widget, "changed",
4179                       G_CALLBACK (eel_editable_label_accessible_changed_cb), NULL);
4180 
4181     g_signal_connect (widget,
4182                       "notify",
4183                       G_CALLBACK (eel_editable_label_accessible_notify_gtk),
4184                       NULL);
4185     atk_object_set_role (accessible, ATK_ROLE_TEXT);
4186 }
4187 
eel_editable_label_accessible_get_name(AtkObject * accessible)4188 static const gchar* eel_editable_label_accessible_get_name(AtkObject* accessible)
4189 {
4190     if (accessible->name != NULL)
4191     {
4192         return accessible->name;
4193     }
4194     else
4195     {
4196         GtkWidget* widget;
4197 
4198         widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(accessible));
4199 
4200         if (widget == NULL)
4201         {
4202             /* State is defunct */
4203             return NULL;
4204         }
4205 
4206         g_assert(EEL_IS_EDITABLE_LABEL(widget));
4207 
4208         return eel_editable_label_get_text(EEL_EDITABLE_LABEL(widget));
4209     }
4210 }
4211 
4212 static AtkStateSet*
eel_editable_label_accessible_ref_state_set(AtkObject * accessible)4213 eel_editable_label_accessible_ref_state_set (AtkObject *accessible)
4214 {
4215     AtkStateSet *state_set;
4216     GtkWidget *widget;
4217 
4218     state_set = a11y_parent_class->ref_state_set (accessible);
4219     widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
4220 
4221     if (widget == NULL)
4222         return state_set;
4223 
4224     atk_state_set_add_state (state_set, ATK_STATE_EDITABLE);
4225     atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE);
4226     return state_set;
4227 }
4228 
4229 static void
eel_editable_label_accessible_finalize(GObject * object)4230 eel_editable_label_accessible_finalize (GObject *object)
4231 {
4232     EelEditableLabelAccessiblePrivate *priv;
4233 
4234     priv = g_object_get_data (object, eel_editable_label_accessible_data);
4235     g_object_unref (priv->textutil);
4236     g_free (priv);
4237     G_OBJECT_CLASS (a11y_parent_class)->finalize (object);
4238 }
4239 
4240 G_DEFINE_TYPE_WITH_CODE (EelEditableLabelAccessible,
4241                          eel_editable_label_accessible,
4242                          GTK_TYPE_WIDGET_ACCESSIBLE,
4243                          G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT,
4244                                                 atk_editable_text_interface_init)
4245                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,
4246                                                 atk_text_interface_init));
4247 
4248 static void
eel_editable_label_accessible_class_init(EelEditableLabelAccessibleClass * klass)4249 eel_editable_label_accessible_class_init (EelEditableLabelAccessibleClass *klass)
4250 {
4251     AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
4252     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
4253 
4254     a11y_parent_class = g_type_class_peek_parent (klass);
4255 
4256     atk_class->initialize = eel_editable_label_accessible_initialize;
4257     atk_class->get_name = eel_editable_label_accessible_get_name;
4258     atk_class->ref_state_set = eel_editable_label_accessible_ref_state_set;
4259     gobject_class->finalize = eel_editable_label_accessible_finalize;
4260 }
4261 
4262 static void
eel_editable_label_accessible_init(EelEditableLabelAccessible * accessible)4263 eel_editable_label_accessible_init (EelEditableLabelAccessible *accessible)
4264 {
4265 }
4266