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