2009-01-13 Claudio Saavedra <csaavedra@igalia.com>
[hildon] / src / hildon-caption.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-caption
27  * @short_description: A single-child container widget that precedes the 
28  * contained widget with a field label and an optional icon.
29  *
30  * #HildonCaption is a single-child container widget that precedes the 
31  * contained widget with a field label and an optional icon. It allows 
32  * grouping of several controls together. When a captioned widget has focus, 
33  * both widget and caption label are displayed with active focus. 
34  */
35
36 #ifdef                                          HAVE_CONFIG_H
37 #include                                        <config.h>
38 #endif
39
40 #include                                        <libintl.h>
41 #include                                        <gtk/gtk.h>
42
43 #include                                        "hildon-defines.h"
44 #include                                        "hildon-caption.h"
45 #include                                        "hildon-caption-private.h"
46
47 #define                                         _(String)\
48                                                 dgettext("hildon-libs", String)
49
50 #define                                         HILDON_CAPTION_SPACING 6
51
52 static GtkEventBox*                             parent_class = NULL;
53
54 static void 
55 hildon_caption_class_init                       (HildonCaptionClass *caption_class);
56
57 static void
58 hildon_caption_init                             (HildonCaption *caption);
59
60 static void 
61 hildon_caption_size_request                     (GtkWidget *widget, 
62                                                  GtkRequisition *requisition);
63
64 static void 
65 hildon_caption_size_allocate                    (GtkWidget *widget, 
66                                                  GtkAllocation *allocation);
67
68 static void 
69 hildon_caption_forall                           (GtkContainer *container,
70                                                  gboolean include_internals,
71                                                  GtkCallback callback, 
72                                                  gpointer data );
73
74 static void
75 hildon_caption_hierarchy_changed                (GtkWidget *widget,
76                                                  GtkWidget *previous_toplevel);
77
78 static void 
79 hildon_caption_set_focus                        (GtkWindow *window, 
80                                                  GtkWidget *widget,
81                                                  GtkWidget *caption);
82
83 static void 
84 hildon_caption_grab_focus                       (GtkWidget *widget);
85
86 static void 
87 hildon_caption_activate                         (GtkWidget *widget);
88
89 static void
90 hildon_caption_set_property                     (GObject *object, 
91                                                  guint param_id,
92                                                  const GValue *value, 
93                                                  GParamSpec *pspec );
94
95 static void
96 hildon_caption_get_property                     (GObject *object, 
97                                                  guint param_id,
98                                                  GValue *value, 
99                                                  GParamSpec *pspec);
100
101 static gboolean 
102 hildon_caption_expose                           (GtkWidget *widget,
103                                                  GdkEventExpose *event);
104
105 static void 
106 hildon_caption_destroy                          (GtkObject *self);
107
108 static gboolean 
109 hildon_caption_button_press                     (GtkWidget *widget, 
110                                                  GdkEventButton *event);
111
112 static void
113 hildon_caption_set_label_text                   (HildonCaptionPrivate *priv, 
114                                                  gboolean markup);
115
116 static void 
117 hildon_caption_set_child_property               (GtkContainer *container,
118                                                  GtkWidget *child,
119                                                  guint property_id,
120                                                  const GValue *value,
121                                                  GParamSpec *pspec);
122
123 static void 
124 hildon_caption_get_child_property               (GtkContainer *container,
125                                                  GtkWidget *child,
126                                                  guint property_id,
127                                                  GValue *value,
128                                                  GParamSpec *pspec);
129
130 enum
131 {
132     PROP_0,
133     PROP_LABEL,
134     PROP_MARKUP,
135     PROP_ICON,
136     PROP_STATUS,
137     PROP_SEPARATOR,
138     PROP_SIZE_GROUP,
139     PROP_ICON_POSITION,
140 };
141
142 enum
143 {
144     CHILD_PROP_NONE,
145     CHILD_PROP_EXPAND
146 };
147
148 /**
149  * hildon_caption_get_type:
150  *
151  * Initializes and returns the type of a hildon caption.
152  *
153  * Returns: GType of #HildonCaption
154  */
155 GType G_GNUC_CONST 
156 hildon_caption_get_type                         (void)
157 {
158     static GType caption_type = 0;
159
160     if (!caption_type)
161     {
162         static const GTypeInfo caption_info = {
163             sizeof (HildonCaptionClass),
164             NULL,       /* base_init */
165             NULL,       /* base_finalize */
166             (GClassInitFunc) hildon_caption_class_init,
167             NULL,       /* class_finalize */
168             NULL,       /* class_data */
169             sizeof (HildonCaption),
170             0,  /* n_preallocs */
171             (GInstanceInitFunc) hildon_caption_init,
172         };
173         caption_type = g_type_register_static (GTK_TYPE_EVENT_BOX,
174                 "HildonCaption", &caption_info, 0 );
175     }
176     return caption_type;
177 }
178
179 /*
180  * Initialises the caption class.
181  */
182 static void 
183 hildon_caption_class_init                       (HildonCaptionClass *caption_class)
184 {
185     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (caption_class);
186     GObjectClass *gobject_class = G_OBJECT_CLASS (caption_class);
187     GtkContainerClass *container_class = GTK_CONTAINER_CLASS (caption_class);
188
189     parent_class = g_type_class_peek_parent (caption_class);
190
191     g_type_class_add_private (caption_class, sizeof (HildonCaptionPrivate));
192
193     /* Override virtual functions */
194     gobject_class->get_property                 = hildon_caption_get_property;
195     gobject_class->set_property                 = hildon_caption_set_property;
196     caption_class->activate                     = (gpointer) hildon_caption_activate;
197     GTK_OBJECT_CLASS(caption_class)->destroy    = hildon_caption_destroy;
198
199     container_class->forall                     = hildon_caption_forall;
200     container_class->set_child_property         = hildon_caption_set_child_property;
201     container_class->get_child_property         = hildon_caption_get_child_property;
202
203     widget_class->expose_event                  = hildon_caption_expose;
204     widget_class->hierarchy_changed             = hildon_caption_hierarchy_changed;
205     widget_class->size_request                  = hildon_caption_size_request;
206     widget_class->size_allocate                 = hildon_caption_size_allocate;
207     widget_class->button_press_event            = hildon_caption_button_press;
208     widget_class->grab_focus                    = hildon_caption_grab_focus;
209
210     /* Create new signals and properties */
211     widget_class->activate_signal = g_signal_new ("activate",
212             G_OBJECT_CLASS_TYPE (
213                 gobject_class),
214             G_SIGNAL_RUN_FIRST |
215             G_SIGNAL_ACTION,
216             G_STRUCT_OFFSET (HildonCaptionClass,
217                 activate), NULL, NULL,
218             g_cclosure_marshal_VOID__VOID,
219             G_TYPE_NONE, 0);
220
221     /**
222      * HildonCaption:label:
223      *
224      * Caption label.
225      */
226     g_object_class_install_property (gobject_class, PROP_LABEL,
227             g_param_spec_string ("label",
228                 "Current label", "Caption label",
229                 NULL, G_PARAM_READABLE | G_PARAM_WRITABLE) );
230     
231     /**
232      * HildonCaption:markup:
233      *
234      * Caption markup. Mutually exclusive with label.
235      */
236     g_object_class_install_property (gobject_class, PROP_MARKUP,
237             g_param_spec_string ("markup",
238                 "Current markup", "Caption markup",
239                 NULL, G_PARAM_WRITABLE) );
240
241     /**
242      * HildonCaption:icon:
243      *
244      * The icon shown on the caption area.
245      */
246     g_object_class_install_property (gobject_class, PROP_ICON,
247             g_param_spec_object ("icon",
248                 "Current icon",
249                 "The icon shown on the caption area",
250                 GTK_TYPE_WIDGET, G_PARAM_READABLE | 
251                 G_PARAM_WRITABLE) );
252     /**
253      * HildonCaption:status:
254      *
255      * Mandatory or optional status.
256      */
257     g_object_class_install_property (gobject_class, PROP_STATUS,
258             g_param_spec_enum ("status",
259                 "Current status",
260                 "Mandatory or optional status",
261                 HILDON_TYPE_CAPTION_STATUS,
262                 HILDON_CAPTION_OPTIONAL,
263                 G_PARAM_READABLE | G_PARAM_WRITABLE) );
264     /**
265      * HildonCaption:icon-position:
266      *
267      * If the icon is positioned on the left or right side.
268      */
269     g_object_class_install_property (gobject_class, PROP_ICON_POSITION,
270             g_param_spec_enum ("icon-position",
271                 "Icon position",
272                 "If the icon is on the left or right side",
273                 HILDON_TYPE_CAPTION_ICON_POSITION,
274                 HILDON_CAPTION_POSITION_RIGHT,
275                 G_PARAM_READABLE | G_PARAM_WRITABLE) );
276
277     /**
278      * HildonCaption:size_group:
279      *
280      * Current size group the caption is in.
281      */
282     g_object_class_install_property (gobject_class, PROP_SIZE_GROUP,
283             g_param_spec_object ("size_group",
284                 "Current size group",
285                 "Current size group the caption is in",
286                 GTK_TYPE_SIZE_GROUP, G_PARAM_READABLE | 
287                 G_PARAM_WRITABLE) );
288
289     /**
290      * HildonCaption:separator:
291      *
292      * The current separator.
293      */
294     g_object_class_install_property (gobject_class, PROP_SEPARATOR,
295             g_param_spec_string ("separator",
296                 "Current separator", "Current separator",
297                 _("ecdg_ti_caption_separator"),
298                 G_PARAM_READABLE | G_PARAM_WRITABLE) );
299
300     /* Create child properties. These are related to
301        child <-> parent relationship, not to either of objects itself */
302     gtk_container_class_install_child_property (container_class,
303             CHILD_PROP_EXPAND,
304             g_param_spec_boolean ("expand",
305                 "Same as GtkBox expand.",
306                 "Same as GtkBox expand. Wheter the child should be expanded or not.",
307                 FALSE,
308                 G_PARAM_READWRITE));
309 }
310
311 /* Destroy can be called multiple times, remember to set pointers to NULL */
312 static void 
313 hildon_caption_destroy                          (GtkObject *self)
314 {
315     HildonCaptionPrivate *priv = HILDON_CAPTION_GET_PRIVATE (self);
316
317     /* Free our internal child */
318     if (priv && priv->caption_area)
319     {
320         gtk_widget_unparent (priv->caption_area);
321         priv->caption_area = NULL;
322     }
323
324     /* Free user provided strings */
325     if (priv && priv->text)
326     {
327         g_free (priv->text);
328         priv->text = NULL;
329     }
330
331     if (priv && priv->separator)
332     {
333         g_free (priv->separator);
334         priv->separator = NULL;
335     }
336
337     /* Parent classes destroy takes care of user packed contents */
338     if (GTK_OBJECT_CLASS (parent_class)->destroy)
339         GTK_OBJECT_CLASS (parent_class)->destroy (self);
340 }
341
342 /* Parent, eventbox will run allocate also for the child which may be
343  * owning a window too. This window is then moved and resized
344  * and we do not want to do that (it causes flickering).
345  */
346 static gboolean 
347 hildon_caption_expose                           (GtkWidget *widget,
348                                                  GdkEventExpose *event)
349 {
350     HildonCaptionPrivate *priv = NULL;
351     GtkRequisition req;
352     GtkAllocation alloc;
353     gfloat align;
354
355     g_assert (HILDON_IS_CAPTION (widget));
356     priv = HILDON_CAPTION_GET_PRIVATE (widget);
357     g_assert (priv);
358
359     if (! GTK_WIDGET_DRAWABLE (widget))
360         return FALSE;
361
362     GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
363
364     /* If our child control is focused, we draw nice looking focus
365        graphics for the caption */  
366     if (priv->is_focused && priv->text)
367     {
368         /* Determine focus box dimensions */
369         gtk_widget_get_child_requisition (priv->caption_area, &req);
370         align = hildon_caption_get_label_alignment (HILDON_CAPTION(widget));
371
372         alloc.width = priv->caption_area->allocation.width + HILDON_CAPTION_SPACING;
373         alloc.height = MIN (req.height + (2 * widget->style->ythickness), priv->caption_area->allocation.height);
374
375         alloc.x = priv->caption_area->allocation.x - HILDON_CAPTION_SPACING; /* - left margin */
376         alloc.y = priv->caption_area->allocation.y + 
377             MAX (((priv->caption_area->allocation.height - alloc.height) * align), 0);
378
379         /* Paint the focus box */
380         gtk_paint_box (widget->style, widget->window, GTK_STATE_ACTIVE,
381                 GTK_SHADOW_OUT, NULL, widget, "selection",
382                 alloc.x, alloc.y, alloc.width, alloc.height);
383
384         /* Paint caption contents on top of the focus box */
385         GTK_WIDGET_GET_CLASS (priv->caption_area)->expose_event (priv->caption_area, event);
386     }
387
388     return FALSE;
389 }
390
391 static void 
392 hildon_caption_set_property                     (GObject *object, 
393                                                  guint param_id,
394                                                  const GValue *value,
395                                                  GParamSpec *pspec)
396 {
397     HildonCaptionPrivate *priv = HILDON_CAPTION_GET_PRIVATE (object);
398     g_assert (priv);
399
400     switch (param_id) 
401     {
402         case PROP_ICON_POSITION:
403             hildon_caption_set_icon_position (HILDON_CAPTION (object), g_value_get_enum (value));
404             break;
405
406         case PROP_LABEL:
407             /* Free old label string */
408             if (priv->text)
409             {
410                 g_free (priv->text);
411                 priv->text = NULL;
412             }
413
414             /* Update label */
415             priv->text = g_value_dup_string (value);
416             hildon_caption_set_label_text (priv, FALSE);
417             break;
418
419         case PROP_MARKUP:
420             /* Free old label string */
421             if (priv->text)
422             {
423                 g_free (priv->text);
424                 priv->text = NULL;
425             }
426
427             /* Update label */
428             priv->text = g_value_dup_string (value);
429             hildon_caption_set_label_text (priv, TRUE);
430             break;
431
432         case PROP_ICON:
433             /* Remove old icon */
434             if (priv->icon)
435                 gtk_container_remove (GTK_CONTAINER (priv->icon_align), priv->icon);
436
437             /* Pack and display new icon */
438             priv->icon = g_value_get_object (value);
439             if (priv->icon)
440             {
441                 gtk_container_add (GTK_CONTAINER (priv->icon_align), priv->icon);
442                 gtk_widget_show_all (priv->caption_area);
443             }
444             break;
445
446         case PROP_STATUS:
447             priv->status = g_value_get_enum (value);
448             break;
449
450         case PROP_SIZE_GROUP:
451             /* Detach from previous size group */
452             if (priv->group)
453                 gtk_size_group_remove_widget (priv->group, priv->caption_area);
454
455             priv->group = g_value_get_object (value);
456
457             /* Attach to new size group */
458             if (priv->group)
459                 gtk_size_group_add_widget (priv->group, priv->caption_area);
460
461             gtk_widget_queue_draw (GTK_WIDGET(object));
462             break;
463
464         case PROP_SEPARATOR:
465
466             /* Free old separator */
467             if (priv->separator)
468             {
469                 g_free (priv->separator);
470                 priv->separator = NULL;
471             }
472
473             priv->separator = g_value_dup_string (value);
474             hildon_caption_set_label_text (priv, FALSE);
475             break;
476
477         default:
478             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
479             break;
480     }
481 }
482
483 static void 
484 hildon_caption_get_property                     (GObject *object, 
485                                                  guint param_id,
486                                                  GValue *value, 
487                                                  GParamSpec *pspec)
488 {
489     HildonCaptionPrivate *priv = HILDON_CAPTION_GET_PRIVATE (object);
490
491     switch (param_id) 
492     {
493         case PROP_LABEL:
494             g_value_set_string (value, priv->text);
495             break;
496
497         case PROP_ICON:
498             g_value_set_object (value, priv->icon);
499             break;
500
501         case PROP_STATUS:
502             g_value_set_enum (value, priv->status);
503             break;
504
505         case PROP_ICON_POSITION:
506             g_value_set_enum (value, priv->icon_position);
507             break;
508
509         case PROP_SIZE_GROUP:
510             g_value_set_object (value, priv->group);
511             break;
512
513         case PROP_SEPARATOR:
514             g_value_set_string (value, priv->separator);
515             break;
516
517         default:
518             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
519             break;
520     }
521 }
522
523 static void 
524 hildon_caption_set_child_property               (GtkContainer *container,
525                                                  GtkWidget *child,
526                                                  guint property_id,
527                                                  const GValue *value,
528                                                  GParamSpec *pspec)
529 {
530     switch (property_id)
531     {
532         case CHILD_PROP_EXPAND:
533             hildon_caption_set_child_expand (HILDON_CAPTION (container),
534                     g_value_get_boolean (value));
535             break;
536
537         default:
538             G_OBJECT_WARN_INVALID_PROPERTY_ID (container, property_id, pspec);
539             break;
540     }
541 }
542
543 static void 
544 hildon_caption_get_child_property               (GtkContainer *container,
545                                                  GtkWidget *child,
546                                                  guint property_id,
547                                                  GValue *value,
548                                                  GParamSpec *pspec)
549 {
550     switch (property_id)
551     {
552         case CHILD_PROP_EXPAND:
553             g_value_set_boolean (value, hildon_caption_get_child_expand(
554                         HILDON_CAPTION (container)));
555             break;
556
557         default:
558             G_OBJECT_WARN_INVALID_PROPERTY_ID(container, property_id, pspec);
559             break;
560     }
561 }
562
563 /* We want to activate out child control on button press */
564 static gboolean
565 hildon_caption_button_press                     (GtkWidget *widget, 
566                                                  GdkEventButton *event)
567 {
568     gtk_widget_grab_focus (GTK_BIN (widget)->child);
569     
570     /* we'll update our focused state in set-focus when/if the child receives
571      * focus */
572
573     return FALSE;
574 }
575
576 static void 
577 hildon_caption_init                             (HildonCaption *caption)
578 {
579     HildonCaptionPrivate *priv = NULL;
580
581     /* Initialize startup state */
582     priv = HILDON_CAPTION_GET_PRIVATE (caption);
583     g_assert (priv);
584
585     priv->status = HILDON_CAPTION_OPTIONAL;
586     priv->icon = NULL;
587     priv->group = NULL;
588     priv->is_focused = FALSE;
589     priv->text = NULL;
590
591     priv->separator = g_strdup(_("ecdg_ti_caption_separator"));
592
593     gtk_widget_push_composite_child();
594
595     /* Create caption text */
596     priv->caption_area = gtk_hbox_new (FALSE, HILDON_CAPTION_SPACING); 
597     priv->label = gtk_label_new (NULL);
598     priv->icon_align = gtk_alignment_new (0.5f, 0.5f, 0.0f, 0.0f);
599     priv->icon_position = HILDON_CAPTION_POSITION_RIGHT;
600
601     /* We want to receive button presses for child widget activation */
602     gtk_event_box_set_above_child (GTK_EVENT_BOX (caption), FALSE);
603     gtk_widget_add_events (GTK_WIDGET (caption), GDK_BUTTON_PRESS_MASK);
604
605     /* Pack text label caption layout */
606     gtk_box_pack_end (GTK_BOX (priv->caption_area), priv->icon_align, FALSE, FALSE, 0);
607     gtk_box_pack_end (GTK_BOX (priv->caption_area), priv->label, FALSE, FALSE, 0);
608     gtk_widget_set_parent (priv->caption_area, GTK_WIDGET (caption));
609
610     gtk_widget_pop_composite_child ();
611
612     hildon_caption_set_child_expand (caption, TRUE);
613
614     gtk_widget_show_all (priv->caption_area);
615 }
616
617 static void 
618 hildon_caption_set_focus                        (GtkWindow *window, 
619                                                  GtkWidget *widget,
620                                                  GtkWidget *caption)
621 {
622     HildonCaptionPrivate *priv = HILDON_CAPTION_GET_PRIVATE (caption);
623     g_assert (priv);
624
625     /* check if ancestor gone */
626     if (! widget)
627     {
628         return;
629     }
630
631     /* Try to find caption among the ancestors of widget */
632     if (gtk_widget_is_ancestor (widget, caption))
633     {
634         priv->is_focused = TRUE;
635         gtk_widget_queue_draw (caption);
636         return;
637     }
638
639     if (priv->is_focused == TRUE)
640     {
641         /* Caption wasn't found, so cannot focus */
642         priv->is_focused = FALSE;
643         gtk_widget_queue_draw (caption);
644     }
645 }
646
647 /* We need to connect/disconnect signal handlers to toplevel window
648    in which we reside. Ww want to update connected signals if our
649    parent changes */
650 static void 
651 hildon_caption_hierarchy_changed                (GtkWidget *widget,
652                                                  GtkWidget *previous_toplevel)
653 {
654     GtkWidget *current_ancestor;
655     HildonCaptionPrivate *priv;
656
657     priv = HILDON_CAPTION_GET_PRIVATE (widget);
658     g_assert (priv);
659
660     if (GTK_WIDGET_CLASS (parent_class)->hierarchy_changed)
661         GTK_WIDGET_CLASS (parent_class)->hierarchy_changed (widget, previous_toplevel);
662
663     /* If we already were inside a window, remove old handler */
664     if (previous_toplevel) {
665         /* This is a compilation workaround for gcc > 3.3 since glib is buggy */
666         /* see http://bugzilla.gnome.org/show_bug.cgi?id=310175 */
667 #ifdef __GNUC__
668         __extension__
669 #endif
670             g_signal_handlers_disconnect_by_func
671             (previous_toplevel, (gpointer) hildon_caption_set_focus, widget);
672     }
673
674     current_ancestor = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
675
676     /* Install new handler for focus movement */
677     if (current_ancestor)
678         g_signal_connect( current_ancestor, "set-focus", 
679                 G_CALLBACK (hildon_caption_set_focus), widget );
680 }
681
682 static void 
683 hildon_caption_size_request                     (GtkWidget *widget,
684                                                  GtkRequisition *requisition)
685 {
686     GtkRequisition req;
687     HildonCaptionPrivate *priv = NULL;
688     g_return_if_fail (HILDON_IS_CAPTION(widget));
689
690     priv = HILDON_CAPTION_GET_PRIVATE (widget);
691     g_assert (priv);
692
693     /* Use the same size requisition for the main box of the caption */
694     gtk_widget_size_request (priv->caption_area, &req);
695
696     if (GTK_WIDGET_CLASS (parent_class)->size_request)
697         GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
698
699     requisition->width += req.width + HILDON_CAPTION_SPACING * 3;
700
701     if ((req.height + (2 * widget->style->ythickness)) > requisition->height)
702         requisition->height = req.height + (2 * widget->style->ythickness);
703 }
704
705 /* We use HILDON_CAPTION_SPACING to make it look a bit nicer */
706 static void 
707 hildon_caption_size_allocate                    (GtkWidget *widget,
708                                                  GtkAllocation *allocation)
709 {
710     GtkAllocation child_alloc;
711     GtkAllocation caption_alloc;
712     GtkRequisition req, child_req;
713     GtkWidget *child = NULL;
714     HildonCaptionPrivate *priv = NULL;
715     gboolean rtl;
716
717     g_assert (HILDON_IS_CAPTION (widget));
718     priv = HILDON_CAPTION_GET_PRIVATE (widget);
719     g_assert (priv);
720
721     /* Get the rtl status */
722     rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
723
724     /* Position the caption to its allocated location */
725     if (GTK_WIDGET_REALIZED (widget))
726         gdk_window_move_resize (widget->window,
727                 allocation->x + GTK_CONTAINER (widget)->border_width,
728                 allocation->y + GTK_CONTAINER (widget)->border_width,
729                 MAX (allocation->width - GTK_CONTAINER (widget)->border_width * 2, 0),
730                 MAX (allocation->height - GTK_CONTAINER (widget)->border_width * 2, 0));
731
732     child = GTK_BIN (widget)->child;
733     if (child)
734         gtk_widget_get_child_requisition (child, &child_req);
735
736     widget->allocation = *allocation;
737     gtk_widget_get_child_requisition (priv->caption_area, &req);
738
739     child_alloc.height = caption_alloc.height = allocation->height;
740     child_alloc.width  = caption_alloc.width  = allocation->width;
741     child_alloc.x = caption_alloc.x = caption_alloc.y = child_alloc.y = 0;
742
743     /* Center the captioned widget */
744     if (rtl)
745     {
746         if (caption_alloc.width > child_req.width + HILDON_CAPTION_SPACING)
747         {
748             caption_alloc.x = caption_alloc.width - req.width;
749             child_alloc.width = child_req.width;
750         }
751         caption_alloc.width -= child_req.width + HILDON_CAPTION_SPACING * 2;
752     }
753     else
754     {
755         if (child_alloc.width > req.width + HILDON_CAPTION_SPACING)
756         {
757             child_alloc.x += req.width + HILDON_CAPTION_SPACING * 2;
758             caption_alloc.width = req.width;
759         }
760         /* Leave at least the space of the HILDON_CAPTION_SPACING in the left */
761         caption_alloc.x = HILDON_CAPTION_SPACING;
762
763         /* Leave room for the other drawable parts of the caption control */
764         child_alloc.width -= req.width + HILDON_CAPTION_SPACING * 2;
765     }
766
767     /* Give the child at least its minimum requisition, unless it is expandable */
768     if (! priv->expand && child && GTK_WIDGET_VISIBLE(child))
769     {
770         child_alloc.width  = MIN (child_alloc.width,  child_req.width);
771         child_alloc.height = MIN (child_alloc.height, child_req.height);
772         /* Center the child */
773         child_alloc.y = (allocation->height - child_alloc.height -
774                          2 * GTK_CONTAINER (widget)->border_width)/2;
775     }
776
777     /* Ensure there are no negative dimensions */
778     if (child_alloc.width < 0)
779     {
780         caption_alloc.width = req.width + child_alloc.width;
781         child_alloc.width = 0;
782         caption_alloc.width = MAX (caption_alloc.width, 0);
783     }
784
785     if (rtl)
786         child_alloc.x = caption_alloc.x - child_req.width - HILDON_CAPTION_SPACING * 2;
787
788     child_alloc.height = MAX (child_alloc.height, 0);
789     caption_alloc.height = MAX (caption_alloc.height, 0);
790
791     if (child && GTK_WIDGET_VISIBLE(child) )
792         gtk_widget_size_allocate (child, &child_alloc );
793
794     gtk_widget_size_allocate (priv->caption_area, &caption_alloc);
795 }
796
797 static void 
798 hildon_caption_forall                           (GtkContainer *container,
799                                                  gboolean include_internals,
800                                                  GtkCallback callback, 
801                                                  gpointer data)
802 {
803     HildonCaptionPrivate *priv = NULL;
804
805     g_assert (HILDON_IS_CAPTION (container));
806     g_assert (callback != NULL);
807
808     priv = HILDON_CAPTION_GET_PRIVATE (container);
809     g_assert (priv);
810
811     /* Execute callback for the child widgets */
812     if (GTK_CONTAINER_CLASS (parent_class)->forall)
813         GTK_CONTAINER_CLASS (parent_class)->forall (container, include_internals, callback, data);
814
815     if (include_internals)
816         /* Execute callback for the parent box as well */
817         (*callback) (priv->caption_area, data);
818 }
819
820 /**
821  * hildon_caption_set_size_group:
822  * @caption : a #HildonCaption
823  * @new_group : a #GtkSizeGroup
824  *
825  * Sets a #GtkSizeGroup of a given captioned control.
826  *
827  */
828 void 
829 hildon_caption_set_size_group                   (const HildonCaption *self,
830                                                  GtkSizeGroup *group)
831 {
832     g_object_set (G_OBJECT(self), "size_group", group, NULL);
833 }
834
835 /**
836  * hildon_caption_get_size_group:
837  * @caption : a #HildonCaption
838  *
839  * Query given captioned control for the #GtkSizeGroup assigned to it.
840  *
841  * Returns: a #GtkSizeGroup
842  * 
843  */
844 GtkSizeGroup*
845 hildon_caption_get_size_group                   (const HildonCaption *self)
846 {
847     HildonCaptionPrivate *priv;
848     g_return_val_if_fail (HILDON_IS_CAPTION (self), NULL);
849
850     priv = HILDON_CAPTION_GET_PRIVATE(self);
851     g_assert (priv);
852
853     return priv->group;
854 }
855
856 /**
857  * hildon_caption_new:
858  * @group : a #GtkSizeGroup for controlling the size of related captions,
859  *          Can be NULL
860  * @value : the caption text to accompany the text entry.  The widget makes
861  *          a copy of this text.
862  * @control : the control that is to be captioned
863  * @icon : an icon to accompany the label - can be NULL in which case no
864  *         icon is displayed
865  * @flag : indicates whether this captioned control is mandatory or
866  *         optional
867  *         
868  * Creates a new instance of hildon_caption widget, with a specific
869  * control and image.
870  * Note: Clicking on a focused caption will trigger the activate signal.
871  * The default behaviour for the caption's activate signal is to call    
872  * gtk_widget_activate on it's control.
873  * 
874  * Returns: a #GtkWidget pointer of Caption
875  */
876 GtkWidget*
877 hildon_caption_new                              (GtkSizeGroup *group, 
878                                                  const gchar *value,
879                                                  GtkWidget *child, 
880                                                  GtkWidget *icon,
881                                                  HildonCaptionStatus flag)
882 {
883     GtkWidget *widget;
884     g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
885
886     widget = g_object_new (HILDON_TYPE_CAPTION, 
887             "label", value,
888             "child", child, 
889             "size_group", group, 
890             "icon", icon, 
891             "status", flag,
892             NULL);
893
894     /* Do not expand GtkCheckButton by default, we want to reduce its activation area */
895     if (GTK_IS_CHECK_BUTTON (child))
896       hildon_caption_set_child_expand (HILDON_CAPTION (widget), FALSE);
897
898     return widget;
899 }
900
901 /**
902  * hildon_caption_is_mandatory:
903  * @caption : a #HildonCaption
904  * 
905  * Query #HildonCaption whether this captioned control is a mandatory one.
906  *
907  * Returns: is this captioned control a mandatory one?
908  */
909 gboolean 
910 hildon_caption_is_mandatory                     (const HildonCaption *caption)
911 {
912     HildonCaptionPrivate *priv;
913
914     g_return_val_if_fail (HILDON_IS_CAPTION (caption), FALSE);
915
916     priv = HILDON_CAPTION_GET_PRIVATE (caption);
917     g_assert (priv);
918
919     return (priv->status == HILDON_CAPTION_MANDATORY);
920 }
921
922 /**
923  * hildon_caption_set_icon_position:
924  * @caption : a #HildonCaption
925  * @pos : one of the values from #HildonCaptionIconPosition
926  *
927  * Sets #HildonCaption icon position.
928  *
929  */
930 void 
931 hildon_caption_set_icon_position                (HildonCaption *caption,
932                                                  HildonCaptionIconPosition pos)
933 {
934     g_return_if_fail (HILDON_IS_CAPTION (caption));
935     HildonCaptionPrivate *priv = HILDON_CAPTION_GET_PRIVATE (caption);
936     g_assert (priv);
937
938     g_return_if_fail (priv->caption_area != NULL);
939     
940     int order = (pos == HILDON_CAPTION_POSITION_LEFT) ? -1 : 0;
941     gtk_box_reorder_child (GTK_BOX (priv->caption_area), priv->icon_align, order);
942
943     priv->icon_position = pos;
944 }
945
946 /**
947  * hildon_caption_get_icon_position:
948  * @caption : a #HildonCaption
949  *
950  * Gets #HildonCaption icon position.
951  *
952  * Returns: one of the values from #HildonCaptionIconPosition.
953  *
954  */
955
956 HildonCaptionIconPosition 
957 hildon_caption_get_icon_position                (const HildonCaption *caption)
958 {
959     g_return_val_if_fail (HILDON_IS_CAPTION (caption), 0);
960
961     HildonCaptionPrivate *priv = HILDON_CAPTION_GET_PRIVATE (caption);
962     g_assert (priv);
963
964     return priv->icon_position;
965 }
966
967 /**
968  * hildon_caption_set_status:
969  * @caption : a #HildonCaption
970  * @flag : one of the values from #HildonCaptionStatus
971  *
972  * Sets #HildonCaption status.
973  */
974 void 
975 hildon_caption_set_status                       (HildonCaption *caption,
976                                                  HildonCaptionStatus flag)
977 {
978     g_return_if_fail (HILDON_IS_CAPTION(caption));
979
980     g_object_set (G_OBJECT(caption), "status", flag, NULL);
981 }
982
983 /**
984  * hildon_caption_get_status:
985  * @caption: a #HildonCaption
986  *
987  * Gets #HildonCaption status.
988  *
989  * Returns: one of the values from #HildonCaptionStatus
990  */
991 HildonCaptionStatus 
992 hildon_caption_get_status                       (const HildonCaption *caption)
993 {
994     HildonCaptionPrivate *priv;
995     g_return_val_if_fail (HILDON_IS_CAPTION (caption), HILDON_CAPTION_OPTIONAL);
996
997     priv = HILDON_CAPTION_GET_PRIVATE(caption);
998     g_assert (priv);
999
1000     return priv->status;
1001 }
1002
1003 /**
1004  * hildon_caption_set_icon_image:
1005  * @caption : a #HildonCaption
1006  * @icon : the #GtkImage to use as the icon. 
1007  *         calls gtk_widget_show on the icon if !GTK_WIDGET_VISIBLE(icon)
1008  *
1009  * Sets the icon image widget to be used by this hildon_caption widget.
1010  */
1011 void 
1012 hildon_caption_set_icon_image                   (HildonCaption *caption, 
1013                                                  GtkWidget *icon)
1014 {
1015     g_return_if_fail (HILDON_IS_CAPTION(caption));
1016
1017     g_object_set (G_OBJECT(caption), "icon", icon, NULL);
1018 }
1019
1020 /**
1021  * hildon_caption_get_icon_image:
1022  * @caption : a #HildonCaption
1023  *
1024  * Gets icon of #HildonCaption
1025  *
1026  * Returns: the #GtkImage widget that is being used as the icon by the
1027  *            hildon_caption, or NULL if no icon image is in use.
1028  */
1029 GtkWidget*
1030 hildon_caption_get_icon_image                   (const HildonCaption *caption)
1031 {
1032     HildonCaptionPrivate *priv;
1033
1034     g_return_val_if_fail (HILDON_IS_CAPTION (caption), NULL);
1035
1036     priv = HILDON_CAPTION_GET_PRIVATE (caption);
1037     g_assert (priv);
1038
1039     return priv->icon;
1040 }
1041
1042 /**
1043  * hildon_caption_set_label:
1044  * @caption : a #HildonCaption
1045  * @label : the text to use
1046  *
1047  * Sets the label text that appears before the control.  
1048  * Separator character is added to the end of the label string. By default
1049  * the separator is ":".
1050  */
1051 void 
1052 hildon_caption_set_label                        (HildonCaption *caption, 
1053                                                  const gchar *label)
1054 {
1055     g_return_if_fail (HILDON_IS_CAPTION (caption));
1056
1057     g_object_set (G_OBJECT(caption), "label", label, NULL);
1058 }
1059
1060 /**
1061  * hildon_caption_set_label_markup:
1062  * @caption : a #HildonCaption
1063  * @markup : the markup text to use
1064  *
1065  * Sets the label markup text that appears before the control. It acts like
1066  * #hildon_caption_set_label but is using the markup text that allows to specify
1067  * text properties such as bold or italic.
1068  */
1069 void 
1070 hildon_caption_set_label_markup                 (HildonCaption *caption, 
1071                                                  const gchar *markup)
1072 {
1073     g_return_if_fail (HILDON_IS_CAPTION (caption));
1074
1075     g_object_set (G_OBJECT(caption), "markup", markup, NULL);
1076 }
1077
1078 /**
1079  * hildon_caption_get_label:
1080  * @caption : a #HildonCaption
1081  *
1082  * Gets label of #HildonCaption
1083  * 
1084  * Returns: the text currently being used as the label of the caption
1085  *  control. The string is owned by the label and the caller should never 
1086  *  free or modify this value.
1087  */
1088 gchar*
1089 hildon_caption_get_label                        (const HildonCaption *caption)
1090 {
1091     HildonCaptionPrivate *priv;
1092     g_return_val_if_fail (HILDON_IS_CAPTION (caption), "");
1093     priv = HILDON_CAPTION_GET_PRIVATE (caption);
1094     g_assert (priv);
1095
1096     return (gchar*) gtk_label_get_text (GTK_LABEL (priv->label));
1097 }
1098
1099 /**
1100  * hildon_caption_set_separator:
1101  * @caption : a #HildonCaption
1102  * @separator : the separator to use
1103  *
1104  * Sets the separator character that appears after the label.  
1105  * The default seaparator character is ":"
1106  * separately.
1107  */
1108 void 
1109 hildon_caption_set_separator                    (HildonCaption *caption, 
1110                                                  const gchar *separator)
1111 {
1112     g_return_if_fail (HILDON_IS_CAPTION (caption));
1113
1114     g_object_set (G_OBJECT (caption), "separator", separator, NULL);
1115 }
1116
1117 /**
1118  * hildon_caption_get_separator:
1119  * @caption : a #HildonCaption
1120  *
1121  * Gets separator string of #HildonCaption
1122  *
1123  * Returns: the text currently being used as the separator of the caption
1124  *  control. The string is owned by the caption control and the caller should
1125  *  never free or modify this value.   
1126  */
1127 gchar*
1128 hildon_caption_get_separator                    (const HildonCaption *caption)
1129 {
1130     HildonCaptionPrivate *priv;
1131     g_return_val_if_fail (HILDON_IS_CAPTION (caption), "");
1132
1133     priv = HILDON_CAPTION_GET_PRIVATE (caption);
1134     g_assert (priv);
1135
1136     return priv->separator;
1137 }
1138
1139 /* Activates the child control
1140  * We have this signal so that if needed an application can 
1141  * know when we've been activated (useful for captions with
1142  * multiple children
1143  * FIXME: There never are multiple children. Possibly activate
1144  *  signal could be removed entirely? (does anyone use it?) 
1145  */
1146 static void 
1147 hildon_caption_activate                         (GtkWidget *widget)
1148 {
1149     GtkWidget *child = GTK_BIN (widget)->child;
1150     gtk_widget_grab_focus (child);
1151 }
1152
1153 static void
1154 hildon_caption_grab_focus                       (GtkWidget *widget)
1155 {
1156     gtk_widget_grab_focus (GTK_BIN (widget)->child);
1157 }
1158
1159 /**
1160  * hildon_caption_set_child_expand:
1161  * @caption : a #HildonCaption
1162  * @expand : gboolean to determine if the child is expandable
1163  *
1164  * Sets child expandability.
1165  */
1166 void 
1167 hildon_caption_set_child_expand                 (HildonCaption *caption, 
1168                                                  gboolean expand)
1169 {
1170     HildonCaptionPrivate *priv = NULL;
1171     GtkWidget *child = NULL;
1172     g_return_if_fail (HILDON_IS_CAPTION (caption));
1173
1174     priv = HILDON_CAPTION_GET_PRIVATE (caption);
1175     g_assert (priv);
1176
1177     /* Did the setting really change? */
1178     if (priv->expand == expand)
1179         return;
1180
1181     priv->expand = expand;
1182     child = GTK_BIN (caption)->child;
1183
1184     /* We do not have a child, nothing to do */
1185     if (! GTK_IS_WIDGET (child))
1186         return;
1187
1188     if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (caption))
1189         gtk_widget_queue_resize (child);
1190
1191     gtk_widget_child_notify (child, "expand");
1192 }
1193
1194 /**
1195  * hildon_caption_get_child_expand:
1196  * @caption : a #HildonCaption
1197  *
1198  * Gets childs expandability.
1199  *
1200  * Returns: wheter the child is expandable or not.
1201  */
1202 gboolean 
1203 hildon_caption_get_child_expand                 (const HildonCaption *caption)
1204 {
1205     HildonCaptionPrivate *priv = NULL;
1206     g_return_val_if_fail (HILDON_IS_CAPTION (caption), FALSE);
1207
1208     priv = HILDON_CAPTION_GET_PRIVATE (caption);
1209     g_assert (priv);
1210
1211     return priv->expand;
1212 }
1213
1214 static void
1215 hildon_caption_set_label_text                   (HildonCaptionPrivate *priv, 
1216                                                  gboolean markup)
1217 {
1218     gchar *tmp = NULL;
1219     g_assert (priv != NULL);
1220
1221     if (priv->text)
1222     {
1223         if (priv->separator)
1224         {
1225             /* Don't duplicate the separator, if the string already contains one */
1226             if (g_str_has_suffix (priv->text, priv->separator))
1227             {
1228                 if (markup)
1229                     gtk_label_set_markup (GTK_LABEL (priv->label), priv->text);
1230                 else
1231                     gtk_label_set_text (GTK_LABEL (priv->label), priv->text);
1232             }
1233             else
1234             {
1235                 /* Append separator and set text */
1236                 tmp = g_strconcat( priv->text, priv->separator, NULL );
1237                 
1238                 if (markup)
1239                     gtk_label_set_markup (GTK_LABEL (priv->label), tmp);
1240                 else
1241                     gtk_label_set_text (GTK_LABEL (priv->label), tmp);
1242
1243                 g_free (tmp);
1244             }
1245         }
1246         else
1247         {
1248             if (markup) 
1249                 gtk_label_set_markup (GTK_LABEL (priv->label), priv->text);
1250             else
1251                 gtk_label_set_text (GTK_LABEL (priv->label), priv->text);
1252         }
1253     }
1254     else
1255     {
1256         /* Clear the label */
1257         if (markup)
1258             gtk_label_set_markup (GTK_LABEL (priv->label), "");
1259         else
1260             gtk_label_set_text (GTK_LABEL (priv->label), "" );
1261     }
1262 }
1263
1264 /**
1265  * hildon_caption_set_label_alignment:
1266  * @caption: a #HildonCaption widget
1267  * @alignment: new vertical alignment
1268  *
1269  * Sets the vertical alignment to be used for the
1270  * text part of the caption. Applications need to
1271  * align the child control themselves.
1272  *
1273  */   
1274 void 
1275 hildon_caption_set_label_alignment              (HildonCaption *caption, 
1276                                                  gfloat alignment)
1277 {
1278     HildonCaptionPrivate *priv;
1279
1280     g_return_if_fail (HILDON_IS_CAPTION (caption));
1281
1282     priv = HILDON_CAPTION_GET_PRIVATE (caption);
1283     g_assert (priv);
1284
1285     g_object_set (priv->label, "yalign", alignment, NULL);
1286     g_object_set (priv->icon_align, "yalign", alignment, NULL);
1287
1288 }
1289
1290 /**
1291  * hildon_caption_get_label_alignment:
1292  * @caption: a #HildonCaption widget
1293  *
1294  * Gets current vertical alignment for the text part.
1295  *
1296  * Returns: vertical alignment
1297  *
1298  */
1299 gfloat 
1300 hildon_caption_get_label_alignment              (HildonCaption *caption)
1301 {
1302     HildonCaptionPrivate *priv;
1303     gfloat result;
1304
1305     g_return_val_if_fail (HILDON_IS_CAPTION (caption), 0);
1306     priv = HILDON_CAPTION_GET_PRIVATE (caption);
1307     g_assert (priv);
1308
1309     g_object_get (priv->label, "yalign", &result, NULL);
1310
1311     return result;
1312 }