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