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