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