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