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