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