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