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