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