No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * st-entry.c: Plain entry actor
4 *
5 * Copyright 2008, 2009 Intel Corporation
6 * Copyright 2009, 2010 Red Hat, Inc.
7 * Copyright 2010 Florian Mç«Żllner
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms and conditions of the GNU Lesser General Public License,
11 * version 2.1, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope it will be useful, but WITHOUT ANY
14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /**
23 * SECTION:st-entry
24 * @short_description: Widget for displaying text
25 *
26 * #StEntry is a simple widget for displaying text. It derives from
27 * #StWidget to add extra style and placement functionality over
28 * #ClutterText. The internal #ClutterText is publicly accessibly to allow
29 * applications to set further properties.
30 *
31 * #StEntry supports the following pseudo style states:
32 * <itemizedlist>
33 * <listitem>
34 * <para>focus: the widget has focus</para>
35 * </listitem>
36 * <listitem>
37 * <para>indeterminate: the widget is showing the hint text</para>
38 * </listitem>
39 * <listitem>
40 * <para>hover: the widget is showing the hint text and is underneath the
41 * pointer</para>
42 * </listitem>
43 * </itemizedlist>
44 */
45
46 #ifdef HAVE_CONFIG_H
47 #include "config.h"
48 #endif
49
50 #include <math.h>
51
52 #include <stdlib.h>
53 #include <string.h>
54
55 #include <glib.h>
56
57 #include <clutter/clutter.h>
58
59 #include "st-entry.h"
60
61 #include "st-im-text.h"
62 #include "st-icon.h"
63 #include "st-widget.h"
64 #include "st-texture-cache.h"
65 #include "st-clipboard.h"
66 #include "st-private.h"
67
68 #include "st-widget-accessible.h"
69
70 #define HAS_FOCUS(actor) (clutter_actor_get_stage (actor) && clutter_stage_get_key_focus ((ClutterStage *) clutter_actor_get_stage (actor)) == actor)
71
72
73 /* properties */
74 enum
75 {
76 PROP_0,
77
78 PROP_CLUTTER_TEXT,
79 PROP_HINT_TEXT,
80 PROP_TEXT,
81 };
82
83 /* signals */
84 enum
85 {
86 PRIMARY_ICON_CLICKED,
87 SECONDARY_ICON_CLICKED,
88
89 LAST_SIGNAL
90 };
91
92 #define ST_ENTRY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_ENTRY, StEntryPrivate))
93 #define ST_ENTRY_PRIV(x) ((StEntry *) x)->priv
94
95
96 struct _StEntryPrivate
97 {
98 ClutterActor *entry;
99 gchar *hint;
100
101 ClutterActor *primary_icon;
102 ClutterActor *secondary_icon;
103
104 gfloat spacing;
105
106 gboolean hint_visible;
107 gboolean capslock_warning_shown;
108 };
109
110 static guint entry_signals[LAST_SIGNAL] = { 0, };
111
112 G_DEFINE_TYPE (StEntry, st_entry, ST_TYPE_WIDGET);
113
114 static GType st_entry_accessible_get_type (void) G_GNUC_CONST;
115
116 static void
117 st_entry_set_property (GObject *gobject,
118 guint prop_id,
119 const GValue *value,
120 GParamSpec *pspec)
121 {
122 StEntry *entry = ST_ENTRY (gobject);
123
124 switch (prop_id)
125 {
126 case PROP_HINT_TEXT:
127 st_entry_set_hint_text (entry, g_value_get_string (value));
128 break;
129
130 case PROP_TEXT:
131 st_entry_set_text (entry, g_value_get_string (value));
132 break;
133
134 default:
135 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
136 break;
137 }
138 }
139
140 static void
141 st_entry_get_property (GObject *gobject,
142 guint prop_id,
143 GValue *value,
144 GParamSpec *pspec)
145 {
146 StEntryPrivate *priv = ST_ENTRY_PRIV (gobject);
147
148 switch (prop_id)
149 {
150 case PROP_CLUTTER_TEXT:
151 g_value_set_object (value, priv->entry);
152 break;
153
154 case PROP_HINT_TEXT:
155 g_value_set_string (value, priv->hint);
156 break;
157
158 case PROP_TEXT:
159 g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->entry)));
160 break;
161
162 default:
163 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
164 break;
165 }
166 }
167
168 static void
169 show_capslock_feedback (StEntry *entry)
170 {
171 if (entry->priv->secondary_icon == NULL)
172 {
173 ClutterActor *icon = g_object_new (ST_TYPE_ICON,
174 "style-class", "capslock-warning",
175 "icon-name", "dialog-warning-symbolic",
176 NULL);
177
178 st_entry_set_secondary_icon (entry, icon);
179 entry->priv->capslock_warning_shown = TRUE;
180 }
181 }
182
183 static void
184 remove_capslock_feedback (StEntry *entry)
185 {
186 if (entry->priv->capslock_warning_shown)
187 {
188 st_entry_set_secondary_icon (entry, NULL);
189 entry->priv->capslock_warning_shown = FALSE;
190 }
191 }
192
193 static void
194 keymap_state_changed (GdkKeymap *keymap,
195 gpointer user_data)
196 {
197 StEntry *entry = ST_ENTRY (user_data);
198
199 if (clutter_text_get_password_char (CLUTTER_TEXT (entry->priv->entry)) != 0)
200 {
201 if (gdk_keymap_get_caps_lock_state (keymap))
202 show_capslock_feedback (entry);
203 else
204 remove_capslock_feedback (entry);
205 }
206 }
207
208 static void
209 st_entry_dispose (GObject *object)
210 {
211 StEntry *entry = ST_ENTRY (object);
212 StEntryPrivate *priv = entry->priv;
213 GdkKeymap *keymap;
214
215 if (priv->entry)
216 {
217 clutter_actor_destroy (priv->entry);
218 priv->entry = NULL;
219 }
220
221 keymap = gdk_keymap_get_for_display (gdk_display_get_default ());
222 g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry);
223
224 G_OBJECT_CLASS (st_entry_parent_class)->dispose (object);
225 }
226
227 static void
228 st_entry_finalize (GObject *object)
229 {
230 StEntryPrivate *priv = ST_ENTRY_PRIV (object);
231
232 g_free (priv->hint);
233 priv->hint = NULL;
234
235 G_OBJECT_CLASS (st_entry_parent_class)->finalize (object);
236 }
237
238 static void
239 st_entry_style_changed (StWidget *self)
240 {
241 StEntryPrivate *priv = ST_ENTRY_PRIV (self);
242 StThemeNode *theme_node;
243 ClutterColor color;
244 const PangoFontDescription *font;
245 gchar *font_string, *font_name;
246 gdouble size;
247
248 theme_node = st_widget_get_theme_node (self);
249
250 st_theme_node_get_foreground_color (theme_node, &color);
251 clutter_text_set_color (CLUTTER_TEXT (priv->entry), &color);
252
253 if (st_theme_node_lookup_length (theme_node, "caret-size", TRUE, &size))
254 clutter_text_set_cursor_size (CLUTTER_TEXT (priv->entry), (int)(.5 + size));
255
256 if (st_theme_node_lookup_color (theme_node, "caret-color", TRUE, &color))
257 clutter_text_set_cursor_color (CLUTTER_TEXT (priv->entry), &color);
258
259 if (st_theme_node_lookup_color (theme_node, "selection-background-color", TRUE, &color))
260 clutter_text_set_selection_color (CLUTTER_TEXT (priv->entry), &color);
261
262 if (st_theme_node_lookup_color (theme_node, "selected-color", TRUE, &color))
263 clutter_text_set_selected_text_color (CLUTTER_TEXT (priv->entry), &color);
264
265 font = st_theme_node_get_font (theme_node);
266 font_string = pango_font_description_to_string (font);
267 font_name = g_strdup (clutter_text_get_font_name (CLUTTER_TEXT (priv->entry)));
268 clutter_text_set_font_name (CLUTTER_TEXT (priv->entry), font_string);
269 if (strcmp (clutter_text_get_font_name (CLUTTER_TEXT (priv->entry)), font_name) != 0)
270 clutter_actor_queue_relayout (priv->entry);
271 g_free (font_string);
272 g_free (font_name);
273
274 ST_WIDGET_CLASS (st_entry_parent_class)->style_changed (self);
275 }
276
277 static gboolean
278 st_entry_navigate_focus (StWidget *widget,
279 ClutterActor *from,
280 GtkDirectionType direction)
281 {
282 StEntryPrivate *priv = ST_ENTRY_PRIV (widget);
283
284 /* This is basically the same as st_widget_real_navigate_focus(),
285 * except that widget is behaving as a proxy for priv->entry (which
286 * isn't an StWidget and so has no can-focus flag of its own).
287 */
288
289 if (from == priv->entry)
290 return FALSE;
291 else if (st_widget_get_can_focus (widget) &&
292 CLUTTER_ACTOR_IS_MAPPED (priv->entry))
293 {
294 clutter_actor_grab_key_focus (priv->entry);
295 return TRUE;
296 }
297 else
298 return FALSE;
299 }
300
301 static void
302 st_entry_get_preferred_width (ClutterActor *actor,
303 gfloat for_height,
304 gfloat *min_width_p,
305 gfloat *natural_width_p)
306 {
307 StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
308 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
309 gfloat icon_w;
310
311 st_theme_node_adjust_for_height (theme_node, &for_height);
312
313 clutter_actor_get_preferred_width (priv->entry, for_height,
314 min_width_p,
315 natural_width_p);
316
317 if (priv->primary_icon)
318 {
319 clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w);
320
321 if (min_width_p)
322 *min_width_p += icon_w + priv->spacing;
323
324 if (natural_width_p)
325 *natural_width_p += icon_w + priv->spacing;
326 }
327
328 if (priv->secondary_icon)
329 {
330 clutter_actor_get_preferred_width (priv->secondary_icon,
331 -1, NULL, &icon_w);
332
333 if (min_width_p)
334 *min_width_p += icon_w + priv->spacing;
335
336 if (natural_width_p)
337 *natural_width_p += icon_w + priv->spacing;
338 }
339
340 st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
341 }
342
343 static void
344 st_entry_get_preferred_height (ClutterActor *actor,
345 gfloat for_width,
346 gfloat *min_height_p,
347 gfloat *natural_height_p)
348 {
349 StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
350 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
351 gfloat icon_h;
352
353 st_theme_node_adjust_for_width (theme_node, &for_width);
354
355 clutter_actor_get_preferred_height (priv->entry, for_width,
356 min_height_p,
357 natural_height_p);
358
359 if (priv->primary_icon)
360 {
361 clutter_actor_get_preferred_height (priv->primary_icon,
362 -1, NULL, &icon_h);
363
364 if (min_height_p && icon_h > *min_height_p)
365 *min_height_p = icon_h;
366
367 if (natural_height_p && icon_h > *natural_height_p)
368 *natural_height_p = icon_h;
369 }
370
371 if (priv->secondary_icon)
372 {
373 clutter_actor_get_preferred_height (priv->secondary_icon,
374 -1, NULL, &icon_h);
375
376 if (min_height_p && icon_h > *min_height_p)
377 *min_height_p = icon_h;
378
379 if (natural_height_p && icon_h > *natural_height_p)
380 *natural_height_p = icon_h;
381 }
382
383 st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
384 }
385
386 static void
387 st_entry_allocate (ClutterActor *actor,
388 const ClutterActorBox *box,
389 ClutterAllocationFlags flags)
390 {
391 StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
392 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
393 ClutterActorBox content_box, child_box, icon_box;
394 gfloat icon_w, icon_h;
395 gfloat entry_h, min_h, pref_h, avail_h;
396
397 clutter_actor_set_allocation (actor, box, flags);
398
399 st_theme_node_get_content_box (theme_node, box, &content_box);
400
401 avail_h = content_box.y2 - content_box.y1;
402
403 child_box.x1 = content_box.x1;
404 child_box.x2 = content_box.x2;
405
406 if (priv->primary_icon)
407 {
408 clutter_actor_get_preferred_width (priv->primary_icon,
409 -1, NULL, &icon_w);
410 clutter_actor_get_preferred_height (priv->primary_icon,
411 -1, NULL, &icon_h);
412
413 icon_box.x1 = content_box.x1;
414 icon_box.x2 = icon_box.x1 + icon_w;
415
416 icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2);
417 icon_box.y2 = icon_box.y1 + icon_h;
418
419 clutter_actor_allocate (priv->primary_icon,
420 &icon_box,
421 flags);
422
423 /* reduce the size for the entry */
424 child_box.x1 += icon_w + priv->spacing;
425 }
426
427 if (priv->secondary_icon)
428 {
429 clutter_actor_get_preferred_width (priv->secondary_icon,
430 -1, NULL, &icon_w);
431 clutter_actor_get_preferred_height (priv->secondary_icon,
432 -1, NULL, &icon_h);
433
434 icon_box.x2 = content_box.x2;
435 icon_box.x1 = icon_box.x2 - icon_w;
436
437 icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2);
438 icon_box.y2 = icon_box.y1 + icon_h;
439
440 clutter_actor_allocate (priv->secondary_icon,
441 &icon_box,
442 flags);
443
444 /* reduce the size for the entry */
445 child_box.x2 -= icon_w - priv->spacing;
446 }
447
448 clutter_actor_get_preferred_height (priv->entry, child_box.x2 - child_box.x1,
449 &min_h, &pref_h);
450
451 entry_h = CLAMP (pref_h, min_h, avail_h);
452
453 child_box.y1 = (int) (content_box.y1 + avail_h / 2 - entry_h / 2);
454 child_box.y2 = child_box.y1 + entry_h;
455
456 clutter_actor_allocate (priv->entry, &child_box, flags);
457 }
458
459 static void
460 clutter_text_focus_in_cb (ClutterText *text,
461 ClutterActor *actor)
462 {
463 StEntry *entry = ST_ENTRY (actor);
464 StEntryPrivate *priv = entry->priv;
465 GdkKeymap *keymap;
466
467 /* remove the hint if visible */
468 if (priv->hint && priv->hint_visible)
469 {
470 priv->hint_visible = FALSE;
471
472 clutter_text_set_text (text, "");
473 }
474
475 keymap = gdk_keymap_get_for_display (gdk_display_get_default ());
476 keymap_state_changed (keymap, entry);
477 g_signal_connect (keymap, "state-changed",
478 G_CALLBACK (keymap_state_changed), entry);
479
480 st_widget_remove_style_pseudo_class (ST_WIDGET (actor), "indeterminate");
481 st_widget_add_style_pseudo_class (ST_WIDGET (actor), "focus");
482 clutter_text_set_cursor_visible (text, TRUE);
483 }
484
485 static void
486 clutter_text_focus_out_cb (ClutterText *text,
487 ClutterActor *actor)
488 {
489 StEntry *entry = ST_ENTRY (actor);
490 StEntryPrivate *priv = entry->priv;
491 GdkKeymap *keymap;
492
493 st_widget_remove_style_pseudo_class (ST_WIDGET (actor), "focus");
494
495 /* add a hint if the entry is empty */
496 if (priv->hint && !strcmp (clutter_text_get_text (text), ""))
497 {
498 priv->hint_visible = TRUE;
499
500 clutter_text_set_text (text, priv->hint);
501 st_widget_add_style_pseudo_class (ST_WIDGET (actor), "indeterminate");
502 }
503 clutter_text_set_cursor_visible (text, FALSE);
504 remove_capslock_feedback (entry);
505
506 keymap = gdk_keymap_get_for_display (gdk_display_get_default ());
507 g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry);
508 }
509
510 static void
511 clutter_text_password_char_cb (GObject *object,
512 GParamSpec *pspec,
513 gpointer user_data)
514 {
515 StEntry *entry = ST_ENTRY (user_data);
516
517 if (clutter_text_get_password_char (CLUTTER_TEXT (entry->priv->entry)) == 0)
518 remove_capslock_feedback (entry);
519 }
520
521 static void
522 st_entry_clipboard_callback (StClipboard *clipboard,
523 const gchar *text,
524 gpointer data)
525 {
526 ClutterText *ctext = (ClutterText*)((StEntry *) data)->priv->entry;
527 gint cursor_pos;
528
529 if (!text)
530 return;
531
532 /* delete the current selection before pasting */
533 clutter_text_delete_selection (ctext);
534
535 /* "paste" the clipboard text into the entry */
536 cursor_pos = clutter_text_get_cursor_position (ctext);
537 clutter_text_insert_text (ctext, text, cursor_pos);
538 }
539
540 static gboolean
541 st_entry_key_press_event (ClutterActor *actor,
542 ClutterKeyEvent *event)
543 {
544 StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
545
546 /* This is expected to handle events that were emitted for the inner
547 ClutterText. They only reach this function if the ClutterText
548 didn't handle them */
549
550 /* paste */
551 if ((event->modifier_state & CLUTTER_CONTROL_MASK)
552 && event->keyval == CLUTTER_v)
553 {
554 StClipboard *clipboard;
555
556 clipboard = st_clipboard_get_default ();
557
558 st_clipboard_get_text (clipboard, st_entry_clipboard_callback, actor);
559
560 return TRUE;
561 }
562
563 /* copy */
564 if ((event->modifier_state & CLUTTER_CONTROL_MASK)
565 && event->keyval == CLUTTER_c)
566 {
567 StClipboard *clipboard;
568 gchar *text;
569
570 clipboard = st_clipboard_get_default ();
571
572 text = clutter_text_get_selection ((ClutterText*) priv->entry);
573
574 if (text && strlen (text))
575 st_clipboard_set_text (clipboard, text);
576
577 return TRUE;
578 }
579
580
581 /* cut */
582 if ((event->modifier_state & CLUTTER_CONTROL_MASK)
583 && event->keyval == CLUTTER_x)
584 {
585 StClipboard *clipboard;
586 gchar *text;
587
588 clipboard = st_clipboard_get_default ();
589
590 text = clutter_text_get_selection ((ClutterText*) priv->entry);
591
592 if (text && strlen (text))
593 {
594 st_clipboard_set_text (clipboard, text);
595
596 /* now delete the text */
597 clutter_text_delete_selection ((ClutterText *) priv->entry);
598 }
599
600 return TRUE;
601 }
602
603 return CLUTTER_ACTOR_CLASS (st_entry_parent_class)->key_press_event (actor, event);
604 }
605
606 static void
607 st_entry_key_focus_in (ClutterActor *actor)
608 {
609 StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
610
611 /* We never want key focus. The ClutterText should be given first
612 pass for all key events */
613 clutter_actor_grab_key_focus (priv->entry);
614 }
615
616 static void
617 st_entry_class_init (StEntryClass *klass)
618 {
619 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
620 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
621 StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
622 GParamSpec *pspec;
623
624 g_type_class_add_private (klass, sizeof (StEntryPrivate));
625
626 gobject_class->set_property = st_entry_set_property;
627 gobject_class->get_property = st_entry_get_property;
628 gobject_class->finalize = st_entry_finalize;
629 gobject_class->dispose = st_entry_dispose;
630
631 actor_class->get_preferred_width = st_entry_get_preferred_width;
632 actor_class->get_preferred_height = st_entry_get_preferred_height;
633 actor_class->allocate = st_entry_allocate;
634
635 actor_class->key_press_event = st_entry_key_press_event;
636 actor_class->key_focus_in = st_entry_key_focus_in;
637
638 widget_class->style_changed = st_entry_style_changed;
639 widget_class->navigate_focus = st_entry_navigate_focus;
640 widget_class->get_accessible_type = st_entry_accessible_get_type;
641
642 pspec = g_param_spec_object ("clutter-text",
643 "Clutter Text",
644 "Internal ClutterText actor",
645 CLUTTER_TYPE_TEXT,
646 G_PARAM_READABLE);
647 g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec);
648
649 pspec = g_param_spec_string ("hint-text",
650 "Hint Text",
651 "Text to display when the entry is not focused "
652 "and the text property is empty",
653 NULL, G_PARAM_READWRITE);
654 g_object_class_install_property (gobject_class, PROP_HINT_TEXT, pspec);
655
656 pspec = g_param_spec_string ("text",
657 "Text",
658 "Text of the entry",
659 NULL, G_PARAM_READWRITE);
660 g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
661
662 /* signals */
663 /**
664 * StEntry::primary-icon-clicked:
665 *
666 * Emitted when the primary icon is clicked
667 */
668 entry_signals[PRIMARY_ICON_CLICKED] =
669 g_signal_new ("primary-icon-clicked",
670 G_TYPE_FROM_CLASS (klass),
671 G_SIGNAL_RUN_LAST,
672 G_STRUCT_OFFSET (StEntryClass, primary_icon_clicked),
673 NULL, NULL, NULL,
674 G_TYPE_NONE, 0);
675 /**
676 * StEntry::secondary-icon-clicked:
677 *
678 * Emitted when the secondary icon is clicked
679 */
680 entry_signals[SECONDARY_ICON_CLICKED] =
681 g_signal_new ("secondary-icon-clicked",
682 G_TYPE_FROM_CLASS (klass),
683 G_SIGNAL_RUN_LAST,
684 G_STRUCT_OFFSET (StEntryClass, secondary_icon_clicked),
685 NULL, NULL, NULL,
686 G_TYPE_NONE, 0);
687 }
688
689 static void
690 st_entry_init (StEntry *entry)
691 {
692 StEntryPrivate *priv;
693
694 priv = entry->priv = ST_ENTRY_GET_PRIVATE (entry);
695
696 priv->entry = g_object_new (ST_TYPE_IM_TEXT,
697 "line-alignment", PANGO_ALIGN_LEFT,
698 "editable", TRUE,
699 "reactive", TRUE,
700 "single-line-mode", TRUE,
701 NULL);
702
703 g_signal_connect (priv->entry, "key-focus-in",
704 G_CALLBACK (clutter_text_focus_in_cb), entry);
705
706 g_signal_connect (priv->entry, "key-focus-out",
707 G_CALLBACK (clutter_text_focus_out_cb), entry);
708
709 g_signal_connect (priv->entry, "notify::password-char",
710 G_CALLBACK (clutter_text_password_char_cb), entry);
711
712 priv->spacing = 6.0f;
713
714 clutter_actor_add_child (CLUTTER_ACTOR (entry), priv->entry);
715 clutter_actor_set_reactive ((ClutterActor *) entry, TRUE);
716
717 /* set cursor hidden until we receive focus */
718 clutter_text_set_cursor_visible ((ClutterText *) priv->entry, FALSE);
719 }
720
721 /**
722 * st_entry_new:
723 * @text: text to set the entry to
724 *
725 * Create a new #StEntry with the specified entry
726 *
727 * Returns: a new #StEntry
728 */
729 StWidget *
730 st_entry_new (const gchar *text)
731 {
732 StWidget *entry;
733
734 /* add the entry to the stage, but don't allow it to be visible */
735 entry = g_object_new (ST_TYPE_ENTRY,
736 "text", text,
737 NULL);
738
739 return entry;
740 }
741
742 /**
743 * st_entry_get_text:
744 * @entry: a #StEntry
745 *
746 * Get the text displayed on the entry
747 *
748 * Returns: the text for the entry. This must not be freed by the application
749 */
750 const gchar *
751 st_entry_get_text (StEntry *entry)
752 {
753 g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
754
755 if (entry->priv->hint_visible)
756 return "";
757 else
758 return clutter_text_get_text (CLUTTER_TEXT (entry->priv->entry));
759 }
760
761 /**
762 * st_entry_set_text:
763 * @entry: a #StEntry
764 * @text: (allow-none): text to set the entry to
765 *
766 * Sets the text displayed on the entry
767 */
768 void
769 st_entry_set_text (StEntry *entry,
770 const gchar *text)
771 {
772 StEntryPrivate *priv;
773
774 g_return_if_fail (ST_IS_ENTRY (entry));
775
776 priv = entry->priv;
777
778 /* set a hint if we are blanking the entry */
779 if (priv->hint
780 && text && !strcmp ("", text)
781 && !HAS_FOCUS (priv->entry))
782 {
783 text = priv->hint;
784 priv->hint_visible = TRUE;
785 st_widget_add_style_pseudo_class (ST_WIDGET (entry), "indeterminate");
786 }
787 else
788 {
789 st_widget_remove_style_pseudo_class (ST_WIDGET (entry), "indeterminate");
790
791 priv->hint_visible = FALSE;
792 }
793
794 clutter_text_set_text (CLUTTER_TEXT (priv->entry), text);
795
796 g_object_notify (G_OBJECT (entry), "text");
797 }
798
799 /**
800 * st_entry_get_clutter_text:
801 * @entry: a #StEntry
802 *
803 * Retrieve the internal #ClutterText so that extra parameters can be set
804 *
805 * Returns: (transfer none): the #ClutterText used by #StEntry. The entry is
806 * owned by the #StEntry and should not be unref'ed by the application.
807 */
808 ClutterActor*
809 st_entry_get_clutter_text (StEntry *entry)
810 {
811 g_return_val_if_fail (ST_ENTRY (entry), NULL);
812
813 return entry->priv->entry;
814 }
815
816 /**
817 * st_entry_set_hint_text:
818 * @entry: a #StEntry
819 * @text: (allow-none): text to set as the entry hint
820 *
821 * Sets the text to display when the entry is empty and unfocused. When the
822 * entry is displaying the hint, it has a pseudo class of "indeterminate".
823 * A value of NULL unsets the hint.
824 */
825 void
826 st_entry_set_hint_text (StEntry *entry,
827 const gchar *text)
828 {
829 StEntryPrivate *priv;
830
831 g_return_if_fail (ST_IS_ENTRY (entry));
832
833 priv = entry->priv;
834
835 g_free (priv->hint);
836
837 priv->hint = g_strdup (text);
838
839 if (!strcmp (clutter_text_get_text (CLUTTER_TEXT (priv->entry)), "")
840 && !HAS_FOCUS (priv->entry))
841 {
842 priv->hint_visible = TRUE;
843
844 clutter_text_set_text (CLUTTER_TEXT (priv->entry), priv->hint);
845 st_widget_add_style_pseudo_class (ST_WIDGET (entry), "indeterminate");
846 }
847 }
848
849 /**
850 * st_entry_get_hint_text:
851 * @entry: a #StEntry
852 *
853 * Gets the text that is displayed when the entry is empty and unfocused
854 *
855 * Returns: the current value of the hint property. This string is owned by the
856 * #StEntry and should not be freed or modified.
857 */
858 const gchar *
859 st_entry_get_hint_text (StEntry *entry)
860 {
861 g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
862
863 return entry->priv->hint;
864 }
865
866 static gboolean
867 _st_entry_icon_press_cb (ClutterActor *actor,
868 ClutterButtonEvent *event,
869 StEntry *entry)
870 {
871 StEntryPrivate *priv = entry->priv;
872
873 if (actor == priv->primary_icon)
874 g_signal_emit (entry, entry_signals[PRIMARY_ICON_CLICKED], 0);
875 else
876 g_signal_emit (entry, entry_signals[SECONDARY_ICON_CLICKED], 0);
877
878 return FALSE;
879 }
880
881 static void
882 _st_entry_set_icon (StEntry *entry,
883 ClutterActor **icon,
884 ClutterActor *new_icon)
885 {
886 if (*icon)
887 {
888 g_signal_handlers_disconnect_by_func (*icon,
889 _st_entry_icon_press_cb,
890 entry);
891 clutter_actor_remove_child (CLUTTER_ACTOR (entry), *icon);
892 *icon = NULL;
893 }
894
895 if (new_icon)
896 {
897 *icon = g_object_ref (new_icon);
898
899 clutter_actor_set_reactive (*icon, TRUE);
900 clutter_actor_add_child (CLUTTER_ACTOR (entry), *icon);
901 g_signal_connect (*icon, "button-release-event",
902 G_CALLBACK (_st_entry_icon_press_cb), entry);
903 }
904
905 clutter_actor_queue_relayout (CLUTTER_ACTOR (entry));
906 }
907
908 /**
909 * st_entry_set_primary_icon:
910 * @entry: a #StEntry
911 * @icon: (allow-none): a #ClutterActor
912 *
913 * Set the primary icon of the entry to @icon
914 */
915 void
916 st_entry_set_primary_icon (StEntry *entry,
917 ClutterActor *icon)
918 {
919 StEntryPrivate *priv;
920
921 g_return_if_fail (ST_IS_ENTRY (entry));
922
923 priv = entry->priv;
924
925 _st_entry_set_icon (entry, &priv->primary_icon, icon);
926 }
927
928 /**
929 * st_entry_set_secondary_icon:
930 * @entry: a #StEntry
931 * @icon: (allow-none): an #ClutterActor
932 *
933 * Set the secondary icon of the entry to @icon
934 */
935 void
936 st_entry_set_secondary_icon (StEntry *entry,
937 ClutterActor *icon)
938 {
939 StEntryPrivate *priv;
940
941 g_return_if_fail (ST_IS_ENTRY (entry));
942
943 priv = entry->priv;
944
945 _st_entry_set_icon (entry, &priv->secondary_icon, icon);
946 }
947
948 /******************************************************************************/
949 /*************************** ACCESSIBILITY SUPPORT ****************************/
950 /******************************************************************************/
951
952 #define ST_TYPE_ENTRY_ACCESSIBLE (st_entry_accessible_get_type ())
953 #define ST_ENTRY_ACCESSIBLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ST_TYPE_ENTRY_ACCESSIBLE, StEntryAccessible))
954 #define ST_IS_ENTRY_ACCESSIBLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ST_TYPE_ENTRY_ACCESSIBLE))
955 #define ST_ENTRY_ACCESSIBLE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), ST_TYPE_ENTRY_ACCESSIBLE, StEntryAccessibleClass))
956 #define ST_IS_ENTRY_ACCESSIBLE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), ST_TYPE_ENTRY_ACCESSIBLE))
957 #define ST_ENTRY_ACCESSIBLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ST_TYPE_ENTRY_ACCESSIBLE, StEntryAccessibleClass))
958
959 typedef struct _StEntryAccessible StEntryAccessible;
960 typedef struct _StEntryAccessibleClass StEntryAccessibleClass;
961
962 struct _StEntryAccessible
963 {
964 StWidgetAccessible parent;
965 };
966
967 struct _StEntryAccessibleClass
968 {
969 StWidgetAccessibleClass parent_class;
970 };
971
972 G_DEFINE_TYPE (StEntryAccessible, st_entry_accessible, ST_TYPE_WIDGET_ACCESSIBLE)
973
974 static void
975 st_entry_accessible_init (StEntryAccessible *self)
976 {
977 /* initialization done on AtkObject->initialize */
978 }
979
980 static void
981 st_entry_accessible_initialize (AtkObject *obj,
982 gpointer data)
983 {
984 ATK_OBJECT_CLASS (st_entry_accessible_parent_class)->initialize (obj, data);
985
986 /* StEntry is behaving as a StImText container */
987 atk_object_set_role (obj, ATK_ROLE_PANEL);
988 }
989
990 static gint
991 st_entry_accessible_get_n_children (AtkObject *obj)
992 {
993 StEntry *entry = NULL;
994
995 g_return_val_if_fail (ST_IS_ENTRY_ACCESSIBLE (obj), 0);
996
997 entry = ST_ENTRY (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
998
999 if (entry == NULL)
1000 return 0;
1001
1002 if (entry->priv->entry == NULL)
1003 return 0;
1004 else
1005 return 1;
1006 }
1007
1008 static AtkObject*
1009 st_entry_accessible_ref_child (AtkObject *obj,
1010 gint i)
1011 {
1012 StEntry *entry = NULL;
1013 AtkObject *result = NULL;
1014
1015 g_return_val_if_fail (ST_IS_ENTRY_ACCESSIBLE (obj), NULL);
1016 g_return_val_if_fail (i == 0, NULL);
1017
1018 entry = ST_ENTRY (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
1019
1020 if (entry == NULL)
1021 return NULL;
1022
1023 if (entry->priv->entry == NULL)
1024 return NULL;
1025
1026 result = clutter_actor_get_accessible (entry->priv->entry);
1027 g_object_ref (result);
1028
1029 return result;
1030 }
1031
1032
1033 static void
1034 st_entry_accessible_class_init (StEntryAccessibleClass *klass)
1035 {
1036 AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
1037
1038 atk_class->initialize = st_entry_accessible_initialize;
1039 atk_class->get_n_children = st_entry_accessible_get_n_children;
1040 atk_class->ref_child= st_entry_accessible_ref_child;
1041 }