No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * st-label.c: Plain label actor
4 *
5 * Copyright 2008,2009 Intel Corporation
6 * Copyright 2009 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-label
24 * @short_description: Widget for displaying text
25 *
26 * #StLabel 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
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include <glib.h>
40
41 #include <clutter/clutter.h>
42
43 #include "st-label.h"
44 #include "st-private.h"
45 #include "st-widget.h"
46
47 #include <st/st-widget-accessible.h>
48
49 enum
50 {
51 PROP_0,
52
53 PROP_CLUTTER_TEXT,
54 PROP_TEXT
55 };
56
57 #define ST_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_LABEL, StLabelPrivate))
58
59 struct _StLabelPrivate
60 {
61 ClutterActor *label;
62
63 CoglHandle text_shadow_material;
64 float shadow_width;
65 float shadow_height;
66 };
67
68 G_DEFINE_TYPE (StLabel, st_label, ST_TYPE_WIDGET);
69
70 static GType st_label_accessible_get_type (void) G_GNUC_CONST;
71
72 static void
73 st_label_set_property (GObject *gobject,
74 guint prop_id,
75 const GValue *value,
76 GParamSpec *pspec)
77 {
78 StLabel *label = ST_LABEL (gobject);
79
80 switch (prop_id)
81 {
82 case PROP_TEXT:
83 st_label_set_text (label, g_value_get_string (value));
84 break;
85
86 default:
87 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
88 break;
89 }
90 }
91
92 static void
93 st_label_get_property (GObject *gobject,
94 guint prop_id,
95 GValue *value,
96 GParamSpec *pspec)
97 {
98 StLabelPrivate *priv = ST_LABEL (gobject)->priv;
99
100 switch (prop_id)
101 {
102 case PROP_CLUTTER_TEXT:
103 g_value_set_object (value, priv->label);
104 break;
105
106 case PROP_TEXT:
107 g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label)));
108 break;
109
110 default:
111 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
112 break;
113 }
114 }
115
116 static void
117 st_label_style_changed (StWidget *self)
118 {
119 StLabelPrivate *priv = ST_LABEL(self)->priv;
120
121 if (priv->text_shadow_material != COGL_INVALID_HANDLE)
122 {
123 cogl_handle_unref (priv->text_shadow_material);
124 priv->text_shadow_material = COGL_INVALID_HANDLE;
125 }
126
127 _st_set_text_from_style ((ClutterText *)priv->label, st_widget_get_theme_node (self));
128
129 ST_WIDGET_CLASS (st_label_parent_class)->style_changed (self);
130 }
131
132 static void
133 st_label_get_preferred_width (ClutterActor *actor,
134 gfloat for_height,
135 gfloat *min_width_p,
136 gfloat *natural_width_p)
137 {
138 StLabelPrivate *priv = ST_LABEL (actor)->priv;
139 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
140
141 st_theme_node_adjust_for_height (theme_node, &for_height);
142
143 clutter_actor_get_preferred_width (priv->label, for_height,
144 min_width_p,
145 natural_width_p);
146
147 st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
148 }
149
150 static void
151 st_label_get_preferred_height (ClutterActor *actor,
152 gfloat for_width,
153 gfloat *min_height_p,
154 gfloat *natural_height_p)
155 {
156 StLabelPrivate *priv = ST_LABEL (actor)->priv;
157 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
158
159 st_theme_node_adjust_for_width (theme_node, &for_width);
160
161 clutter_actor_get_preferred_height (priv->label, for_width,
162 min_height_p,
163 natural_height_p);
164
165 st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
166 }
167
168 static void
169 st_label_allocate (ClutterActor *actor,
170 const ClutterActorBox *box,
171 ClutterAllocationFlags flags)
172 {
173 StLabelPrivate *priv = ST_LABEL (actor)->priv;
174 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
175 ClutterActorBox content_box;
176
177 clutter_actor_set_allocation (actor, box, flags);
178
179 st_theme_node_get_content_box (theme_node, box, &content_box);
180
181 clutter_actor_allocate (priv->label, &content_box, flags);
182 }
183
184 static void
185 st_label_dispose (GObject *object)
186 {
187 StLabelPrivate *priv = ST_LABEL (object)->priv;
188
189 if (priv->label)
190 {
191 clutter_actor_destroy (priv->label);
192 priv->label = NULL;
193 }
194
195 if (priv->text_shadow_material != COGL_INVALID_HANDLE)
196 {
197 cogl_handle_unref (priv->text_shadow_material);
198 priv->text_shadow_material = COGL_INVALID_HANDLE;
199 }
200
201 G_OBJECT_CLASS (st_label_parent_class)->dispose (object);
202 }
203
204 static void
205 st_label_paint (ClutterActor *actor)
206 {
207 StLabelPrivate *priv = ST_LABEL (actor)->priv;
208 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
209 StShadow *shadow_spec = st_theme_node_get_text_shadow (theme_node);
210
211 st_widget_paint_background (ST_WIDGET (actor));
212
213 if (shadow_spec)
214 {
215 ClutterActorBox allocation;
216 float width, height;
217
218 clutter_actor_get_allocation_box (priv->label, &allocation);
219 clutter_actor_box_get_size (&allocation, &width, &height);
220
221 allocation.x1 = allocation.y1 = 0;
222 allocation.x2 = width;
223 allocation.y2 = height;
224
225 if (priv->text_shadow_material == COGL_INVALID_HANDLE ||
226 width != priv->shadow_width ||
227 height != priv->shadow_height)
228 {
229 CoglHandle material;
230
231 if (priv->text_shadow_material != COGL_INVALID_HANDLE)
232 cogl_handle_unref (priv->text_shadow_material);
233
234 material = _st_create_shadow_material_from_actor (shadow_spec,
235 priv->label);
236
237 priv->shadow_width = width;
238 priv->shadow_height = height;
239 priv->text_shadow_material = material;
240 }
241
242 if (priv->text_shadow_material != COGL_INVALID_HANDLE)
243 _st_paint_shadow_with_opacity (shadow_spec,
244 priv->text_shadow_material,
245 &allocation,
246 clutter_actor_get_paint_opacity (priv->label));
247 }
248
249 clutter_actor_paint (priv->label);
250 }
251
252 static void
253 st_label_class_init (StLabelClass *klass)
254 {
255 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
256 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
257 StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
258 GParamSpec *pspec;
259
260 g_type_class_add_private (klass, sizeof (StLabelPrivate));
261
262 gobject_class->set_property = st_label_set_property;
263 gobject_class->get_property = st_label_get_property;
264 gobject_class->dispose = st_label_dispose;
265
266 actor_class->paint = st_label_paint;
267 actor_class->allocate = st_label_allocate;
268 actor_class->get_preferred_width = st_label_get_preferred_width;
269 actor_class->get_preferred_height = st_label_get_preferred_height;
270
271 widget_class->style_changed = st_label_style_changed;
272 widget_class->get_accessible_type = st_label_accessible_get_type;
273
274 pspec = g_param_spec_object ("clutter-text",
275 "Clutter Text",
276 "Internal ClutterText actor",
277 CLUTTER_TYPE_TEXT,
278 G_PARAM_READABLE);
279 g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec);
280
281 pspec = g_param_spec_string ("text",
282 "Text",
283 "Text of the label",
284 NULL, G_PARAM_READWRITE);
285 g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
286
287 }
288
289 static void
290 st_label_init (StLabel *label)
291 {
292 StLabelPrivate *priv;
293
294 label->priv = priv = ST_LABEL_GET_PRIVATE (label);
295
296 label->priv->label = g_object_new (CLUTTER_TYPE_TEXT,
297 "ellipsize", PANGO_ELLIPSIZE_END,
298 NULL);
299 label->priv->text_shadow_material = COGL_INVALID_HANDLE;
300 label->priv->shadow_width = -1.;
301 label->priv->shadow_height = -1.;
302
303 clutter_actor_add_child (CLUTTER_ACTOR (label), priv->label);
304 }
305
306 /**
307 * st_label_new:
308 * @text: text to set the label to
309 *
310 * Create a new #StLabel with the specified label
311 *
312 * Returns: a new #StLabel
313 */
314 StWidget *
315 st_label_new (const gchar *text)
316 {
317 if (text == NULL || *text == '\0')
318 return g_object_new (ST_TYPE_LABEL, NULL);
319 else
320 return g_object_new (ST_TYPE_LABEL,
321 "text", text,
322 NULL);
323 }
324
325 /**
326 * st_label_get_text:
327 * @label: a #StLabel
328 *
329 * Get the text displayed on the label
330 *
331 * Returns: the text for the label. This must not be freed by the application
332 */
333 const gchar *
334 st_label_get_text (StLabel *label)
335 {
336 g_return_val_if_fail (ST_IS_LABEL (label), NULL);
337
338 return clutter_text_get_text (CLUTTER_TEXT (label->priv->label));
339 }
340
341 /**
342 * st_label_set_text:
343 * @label: a #StLabel
344 * @text: text to set the label to
345 *
346 * Sets the text displayed on the label
347 */
348 void
349 st_label_set_text (StLabel *label,
350 const gchar *text)
351 {
352 StLabelPrivate *priv;
353 ClutterText *ctext;
354
355 g_return_if_fail (ST_IS_LABEL (label));
356 g_return_if_fail (text != NULL);
357
358 priv = label->priv;
359 ctext = CLUTTER_TEXT (priv->label);
360
361 if (clutter_text_get_editable (ctext) ||
362 g_strcmp0 (clutter_text_get_text (ctext), text) != 0)
363 {
364 if (priv->text_shadow_material != COGL_INVALID_HANDLE)
365 {
366 cogl_handle_unref (priv->text_shadow_material);
367 priv->text_shadow_material = COGL_INVALID_HANDLE;
368 }
369
370 clutter_text_set_text (ctext, text);
371
372 g_object_notify (G_OBJECT (label), "text");
373 }
374 }
375
376 /**
377 * st_label_get_clutter_text:
378 * @label: a #StLabel
379 *
380 * Retrieve the internal #ClutterText so that extra parameters can be set
381 *
382 * Returns: (transfer none): ethe #ClutterText used by #StLabel. The label
383 * is owned by the #StLabel and should not be unref'ed by the application.
384 */
385 ClutterActor*
386 st_label_get_clutter_text (StLabel *label)
387 {
388 g_return_val_if_fail (ST_LABEL (label), NULL);
389
390 return label->priv->label;
391 }
392
393
394 /******************************************************************************/
395 /*************************** ACCESSIBILITY SUPPORT ****************************/
396 /******************************************************************************/
397
398 #define ST_TYPE_LABEL_ACCESSIBLE st_label_accessible_get_type ()
399
400 #define ST_LABEL_ACCESSIBLE(obj) \
401 (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
402 ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessible))
403
404 #define ST_IS_LABEL_ACCESSIBLE(obj) \
405 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
406 ST_TYPE_LABEL_ACCESSIBLE))
407
408 #define ST_LABEL_ACCESSIBLE_CLASS(klass) \
409 (G_TYPE_CHECK_CLASS_CAST ((klass), \
410 ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessibleClass))
411
412 #define ST_IS_LABEL_ACCESSIBLE_CLASS(klass) \
413 (G_TYPE_CHECK_CLASS_TYPE ((klass), \
414 ST_TYPE_LABEL_ACCESSIBLE))
415
416 #define ST_LABEL_ACCESSIBLE_GET_CLASS(obj) \
417 (G_TYPE_INSTANCE_GET_CLASS ((obj), \
418 ST_TYPE_LABEL_ACCESSIBLE, StLabelAccessibleClass))
419
420 typedef struct _StLabelAccessible StLabelAccessible;
421 typedef struct _StLabelAccessibleClass StLabelAccessibleClass;
422
423 struct _StLabelAccessible
424 {
425 StWidgetAccessible parent;
426 };
427
428 struct _StLabelAccessibleClass
429 {
430 StWidgetAccessibleClass parent_class;
431 };
432
433 static void st_label_accessible_class_init (StLabelAccessibleClass *klass);
434 static void st_label_accessible_init (StLabelAccessible *label);
435
436 /* AtkObject */
437 static void st_label_accessible_initialize (AtkObject *obj,
438 gpointer data);
439 static const gchar * st_label_accessible_get_name (AtkObject *obj);
440
441 G_DEFINE_TYPE (StLabelAccessible, st_label_accessible, ST_TYPE_WIDGET_ACCESSIBLE)
442
443 static void
444 st_label_accessible_class_init (StLabelAccessibleClass *klass)
445 {
446 AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
447
448 atk_class->initialize = st_label_accessible_initialize;
449 atk_class->get_name = st_label_accessible_get_name;
450 }
451
452 static void
453 st_label_accessible_init (StLabelAccessible *self)
454 {
455 /* initialization done on AtkObject->initialize */
456 }
457
458 static void
459 label_text_notify_cb (StLabel *label,
460 GParamSpec *pspec,
461 AtkObject *accessible)
462 {
463 g_object_notify (G_OBJECT (accessible), "accessible-name");
464 }
465
466 static void
467 st_label_accessible_initialize (AtkObject *obj,
468 gpointer data)
469 {
470 ATK_OBJECT_CLASS (st_label_accessible_parent_class)->initialize (obj, data);
471
472 g_signal_connect (data, "notify::text",
473 G_CALLBACK (label_text_notify_cb),
474 obj);
475
476 obj->role = ATK_ROLE_LABEL;
477 }
478
479 static const gchar *
480 st_label_accessible_get_name (AtkObject *obj)
481 {
482 const gchar *name = NULL;
483
484 g_return_val_if_fail (ST_IS_LABEL_ACCESSIBLE (obj), NULL);
485
486 name = ATK_OBJECT_CLASS (st_label_accessible_parent_class)->get_name (obj);
487 if (name == NULL)
488 {
489 ClutterActor *actor = NULL;
490
491 actor = CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
492
493 if (actor == NULL || st_widget_has_style_class_name (ST_WIDGET (actor), "hidden"))
494 name = NULL;
495 else
496 name = st_label_get_text (ST_LABEL (actor));
497 }
498
499 return name;
500 }