2006-09-19 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   /* nothing to do */
515   if (priv->is_focused == TRUE)
516     return;
517
518   /* If child can take focus, we simply grab focus to it */
519   if ((GTK_WIDGET_CAN_FOCUS(child) || GTK_IS_CONTAINER(child)) &&
520       GTK_WIDGET_IS_SENSITIVE(child))
521   {
522     /* Only if container can be focusable we must set is_focused to TRUE */ 
523     if (GTK_IS_CONTAINER(child))
524     {
525       if (gtk_widget_child_focus (child,
526                                   GTK_DIR_TAB_FORWARD))
527         priv->is_focused = TRUE;
528     }
529     else
530     {
531       priv->is_focused = TRUE;
532       gtk_widget_grab_focus (GTK_BIN (widget)->child);
533     }
534   }
535
536   return FALSE;
537 }
538
539 static void hildon_caption_init( HildonCaption *caption )
540 {
541   HildonCaptionPrivate *priv = NULL;
542
543   /* Initialize startup state */
544   priv = HILDON_CAPTION_GET_PRIVATE(caption);
545   priv->status = HILDON_CAPTION_OPTIONAL;
546   priv->icon = NULL;
547   priv->group = NULL;
548   priv->is_focused = FALSE;
549
550   priv->separator = g_strdup(_("ecdg_ti_caption_separator"));
551
552   gtk_widget_push_composite_child();
553   
554   /* Create caption text */
555   priv->caption_area = gtk_hbox_new( FALSE, HILDON_CAPTION_SPACING ); 
556   priv->label = gtk_label_new( NULL );
557   priv->icon_align = gtk_alignment_new(0.5f, 0.5f, 0.0f, 0.0f);
558
559   /* We want to receive button presses for child widget activation */
560   gtk_event_box_set_above_child( GTK_EVENT_BOX(caption), FALSE );
561   gtk_widget_add_events( GTK_WIDGET(caption), GDK_BUTTON_PRESS_MASK );
562
563   /* Pack text label caption layout */
564   gtk_box_pack_end( GTK_BOX(priv->caption_area), priv->icon_align, FALSE, FALSE, 0);
565   gtk_box_pack_end( GTK_BOX(priv->caption_area), priv->label, FALSE, FALSE, 0 );
566   gtk_widget_set_parent( priv->caption_area, GTK_WIDGET(caption) );
567
568   gtk_widget_pop_composite_child();
569
570   hildon_caption_set_child_expand( caption, TRUE );
571
572   gtk_widget_show_all( priv->caption_area );
573 }
574
575 static void hildon_caption_set_focus( GtkWindow *window, GtkWidget *widget,
576                                       GtkWidget *caption )
577 {
578   HildonCaptionPrivate *priv = HILDON_CAPTION_GET_PRIVATE(caption);
579
580   /* check if ancestor gone */
581   if (!widget)
582   {
583     return;
584   }
585   
586   /* Try to find caption among the ancestors of widget */
587   if (gtk_widget_is_ancestor(widget, caption))
588     {
589       priv->is_focused = TRUE;
590       gtk_widget_queue_draw( caption );
591       return;
592     }
593
594   if( priv->is_focused == TRUE )
595   {
596     /* Caption wasn't found, so cannot focus */
597     priv->is_focused = FALSE;
598     gtk_widget_queue_draw( caption );
599   }
600 }
601
602 /* We need to connect/disconnect signal handlers to toplevel window
603    in which we reside. Ww want to update connected signals if our
604    parent changes */
605 static void hildon_caption_hierarchy_changed( GtkWidget *widget,
606                                               GtkWidget *previous_toplevel)
607 {
608   GtkWidget *current_ancestor;
609   HildonCaptionPrivate *priv;
610
611   priv = HILDON_CAPTION_GET_PRIVATE(widget);
612
613   if( GTK_WIDGET_CLASS(parent_class)->hierarchy_changed )
614     GTK_WIDGET_CLASS(parent_class)->hierarchy_changed( widget,
615                                                        previous_toplevel );
616
617   /* If we already were inside a window, remove old handler */
618   if (previous_toplevel) {
619   /* This is a compilation workaround for gcc > 3.3 since glib is buggy */
620   /* see http://bugzilla.gnome.org/show_bug.cgi?id=310175 */
621 #ifdef __GNUC__
622   __extension__
623 #endif
624     g_signal_handlers_disconnect_by_func
625       (previous_toplevel, (gpointer) hildon_caption_set_focus, widget);
626   }
627   current_ancestor = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
628
629   /* Install new handler for focus movement */
630   if (current_ancestor)
631     g_signal_connect( current_ancestor, "set-focus", 
632       G_CALLBACK(hildon_caption_set_focus), widget );
633 }
634
635 static void hildon_caption_size_request( GtkWidget *widget,
636                                          GtkRequisition *requisition )
637 {
638   GtkRequisition req;
639   HildonCaptionPrivate *priv = NULL;
640   g_return_if_fail( HILDON_IS_CAPTION(widget) );
641   priv = HILDON_CAPTION_GET_PRIVATE(widget);
642
643   /* Use the same size requisition for the main box of the caption */
644   gtk_widget_size_request( priv->caption_area, &req );
645
646   if( GTK_WIDGET_CLASS(parent_class)->size_request )
647     GTK_WIDGET_CLASS(parent_class)->size_request( widget, requisition );
648
649   requisition->width += req.width + HILDON_CAPTION_SPACING * 3;
650
651   if( (req.height + (2 * widget->style->ythickness)) > requisition->height )
652    requisition->height = req.height + (2 * widget->style->ythickness);
653 }
654
655 /* We use HILDON_CAPTION_SPACING to make it look a bit nicer */
656 static void hildon_caption_size_allocate( GtkWidget *widget,
657                                           GtkAllocation *allocation )
658 {
659   GtkAllocation allocA;
660   GtkAllocation allocB;
661   GtkRequisition req;
662   GtkWidget *child = NULL;
663   HildonCaptionPrivate *priv = NULL;
664
665   g_assert( HILDON_IS_CAPTION(widget) );
666   priv = HILDON_CAPTION_GET_PRIVATE(widget);
667
668   /* Position the caption to its allocated location */
669   if( GTK_WIDGET_REALIZED(widget) )
670     gdk_window_move_resize (widget->window,
671                                 allocation->x + GTK_CONTAINER (widget)->border_width,
672                                 allocation->y + GTK_CONTAINER (widget)->border_width,
673                                 MAX (allocation->width - GTK_CONTAINER (widget)->border_width * 2, 0),
674                                 MAX (allocation->height - GTK_CONTAINER (widget)->border_width * 2, 0));
675
676   child = GTK_BIN(widget)->child;
677
678   widget->allocation = *allocation;  
679   gtk_widget_get_child_requisition( priv->caption_area, &req );
680
681   allocA.height = allocB.height = allocation->height;
682   allocA.width  = allocB.width  = allocation->width;
683   allocA.x = allocB.x = allocB.y = allocA.y = 0;
684   
685   /* Center the captioned widget */
686   if( allocA.width > req.width + HILDON_CAPTION_SPACING )
687   {
688     allocA.x += req.width + HILDON_CAPTION_SPACING * 2;
689     allocB.width = req.width;
690   }
691
692   /* Leave at least the space of the HILDON_CAPTION_SPACING in the left */
693   allocB.x = HILDON_CAPTION_SPACING;
694
695   /* Leave room for the other drawable parts of the caption control */
696   allocA.width -= req.width + HILDON_CAPTION_SPACING * 2;
697
698   /* Give the child at least its minimum requisition, unless it is expandable */
699   if( !priv->expand && child && GTK_WIDGET_VISIBLE(child) )
700   {
701     GtkRequisition child_req;
702     gtk_widget_get_child_requisition( child, &child_req );
703     allocA.width  = MIN( allocA.width,  child_req.width  );
704     allocA.height = MIN( allocA.height, child_req.height );
705   }
706
707   /* Ensure there are no negative dimensions */
708   if( allocA.width < 0 )
709   {
710     allocB.width = req.width + allocA.width;
711     allocA.width = 0;
712     allocB.width = MAX (allocB.width, 0);
713   }
714
715   allocA.height = MAX (allocA.height, 0);
716   allocB.height = MAX (allocB.height, 0);
717
718   if (child && GTK_WIDGET_VISIBLE(child) )
719     gtk_widget_size_allocate( child, &allocA );
720
721   gtk_widget_size_allocate( priv->caption_area, &allocB );
722 }
723
724 static void hildon_caption_forall( GtkContainer *container,
725                                    gboolean include_internals,
726                                    GtkCallback callback, gpointer data )
727 {
728   HildonCaptionPrivate *priv = NULL;
729
730   g_assert( HILDON_IS_CAPTION(container) );
731   g_assert( callback != NULL );
732
733   priv = HILDON_CAPTION_GET_PRIVATE(container);
734
735   /* Execute callback for the child widgets */
736   if( GTK_CONTAINER_CLASS(parent_class)->forall )
737     GTK_CONTAINER_CLASS(parent_class)->forall( container, include_internals,
738                                                callback, data );
739
740   if( include_internals )
741     /* Execute callback for the parent box as well */
742     (*callback)( priv->caption_area, data );
743 }
744
745
746 /**
747  * hildon_caption_set_sizegroup:
748  * @caption : a #HildonCaption
749  * @new_group : a #GtkSizeGroup
750  *
751  * Sets a #GtkSizeGroup of a given captioned control.
752  *
753  * Deprecated: use g_object_set, property :size-group
754  */
755 void hildon_caption_set_sizegroup( const HildonCaption *self,
756                                    GtkSizeGroup *group )
757 {
758   g_object_set( G_OBJECT(self), "size_group", group, NULL );
759 }
760
761 /**
762  * hildon_caption_get_sizegroup:
763  * @caption : a #HildonCaption
764  *
765  * Query given captioned control for the #GtkSizeGroup assigned to it.
766  *
767  * @Returns : a #GtkSizeGroup
768  * 
769  * Deprecated: Use g_object_get, property :size-group
770  */
771 GtkSizeGroup *hildon_caption_get_sizegroup( const HildonCaption *self )
772 {
773   HildonCaptionPrivate *priv;
774   g_return_val_if_fail( HILDON_IS_CAPTION (self), NULL );
775   priv = HILDON_CAPTION_GET_PRIVATE(self);
776   return priv->group;
777 }
778
779 /**
780  * hildon_caption_new:
781  * @group : a #GtkSizeGroup for controlling the size of related captions,
782  *          Can be NULL
783  * @value : the caption text to accompany the text entry.  The widget makes
784  *          a copy of this text.
785  * @control : the control that is to be captioned
786  * @icon : an icon to accompany the label - can be NULL in which case no
787  *         icon is displayed
788  * @flag : indicates whether this captioned control is mandatory or
789  *         optional
790  *         
791  * Creates a new instance of hildon_caption widget, with a specific
792  * control and image.
793  * Note: Clicking on a focused caption will trigger the activate signal.
794  * The default behaviour for the caption's activate signal is to call    
795  * gtk_widget_activate on it's control.
796  * 
797  * @Returns : a #GtkWidget pointer of Caption
798  */
799 GtkWidget *hildon_caption_new( GtkSizeGroup *group, const gchar *value,
800                                GtkWidget *child, GtkWidget *icon,
801                                HildonCaptionStatus flag)
802 {
803   GtkWidget *widget;
804   g_return_val_if_fail( GTK_IS_WIDGET(child), NULL );
805   
806   widget = g_object_new( HILDON_TYPE_CAPTION, "label", value,
807                         "child" /* From GtkContainer */, child, "size_group", group, "icon", icon, "status", flag,
808                         NULL );
809
810   return widget;
811 }
812
813 /**
814  * hildon_caption_is_mandatory:
815  * @caption : a #HildonCaption
816  * 
817  * Query #HildonCaption whether this captioned control is a mandatory one.
818  *
819  * @Returns : is this captioned control a mandatory one?
820  */
821
822 gboolean hildon_caption_is_mandatory( const HildonCaption *caption )
823 {
824   HildonCaptionPrivate *priv;
825   g_return_val_if_fail( HILDON_IS_CAPTION(caption), FALSE );
826   priv = HILDON_CAPTION_GET_PRIVATE(caption);
827
828   return priv->status == HILDON_CAPTION_MANDATORY;
829 }
830
831 /**
832  * hildon_caption_set_status:
833  * @caption : a #HildonCaption
834  * @flag : one of the values from #HildonCaptionStatus
835  *
836  * Sets #HildonCaption status.
837  */
838
839 void hildon_caption_set_status( HildonCaption *caption,
840                                 HildonCaptionStatus flag )
841 {
842   g_return_if_fail( HILDON_IS_CAPTION(caption) );
843
844   g_object_set( G_OBJECT(caption), "status", flag, NULL );
845 }
846
847 /**
848  * hildon_caption_get_status:
849  * @caption : a #HildonCaption
850  *
851  * Gets #HildonCaption status.
852  *
853  * @Returns : one of the values from #HildonCaptionStatus
854  */
855
856 HildonCaptionStatus hildon_caption_get_status( const HildonCaption *caption )
857 {
858   HildonCaptionPrivate *priv;
859   g_return_val_if_fail( HILDON_IS_CAPTION(caption), HILDON_CAPTION_OPTIONAL );
860   priv = HILDON_CAPTION_GET_PRIVATE(caption);
861
862   return priv->status;
863 }
864
865 /**
866  * hildon_caption_set_icon_image:
867  * @caption : a #HildonCaption
868  * @icon : the #GtkImage to use as the icon. 
869  *         calls gtk_widget_show on the icon if !GTK_WIDGET_VISIBLE(icon)
870  *
871  * Sets the icon to be used by this hildon_caption widget.
872  */
873
874 void hildon_caption_set_icon_image( HildonCaption *caption, GtkWidget *icon )
875 {
876   g_return_if_fail( HILDON_IS_CAPTION(caption) );
877
878   g_object_set( G_OBJECT(caption), "icon", icon, NULL );
879 }
880
881 /**
882  * hildon_caption_get_icon_image:
883  * @caption : a #HildonCaption
884  *
885  * Gets icon of #HildonCaption
886  *
887  * @Returns : the #GtkImage that is being used as the icon by the
888  *            hildon_caption, or NULL if no icon is in use
889  */
890
891 GtkWidget *hildon_caption_get_icon_image( const HildonCaption *caption )
892 {
893   HildonCaptionPrivate *priv;
894   g_return_val_if_fail( HILDON_IS_CAPTION(caption), NULL );
895   priv = HILDON_CAPTION_GET_PRIVATE(caption);
896
897   return priv->icon;
898 }
899
900 /**
901  * hildon_caption_set_label:
902  * @caption : a #HildonCaption
903  * @label : the text to use
904  *
905  * Sets the label text that appears before the control.  
906  * Separator character is added to the end of the label string. By default
907  * the separator is ":".
908  */
909
910 void hildon_caption_set_label( HildonCaption *caption, const gchar *label )
911 {
912   g_return_if_fail( HILDON_IS_CAPTION(caption) );
913
914   g_object_set( G_OBJECT(caption), "label", label, NULL );
915 }
916
917 /**
918  * hildon_caption_get_label:
919  * @caption : a #HildonCaption
920  *
921  * Gets label of #HildonCaption
922  * 
923  * @Returns : the text currently being used as the label of the caption
924  *  control. The string is owned by the label and the caller should never 
925  *  free or modify this value.
926  */
927
928 gchar *hildon_caption_get_label( const HildonCaption *caption )
929 {
930   HildonCaptionPrivate *priv;
931   g_return_val_if_fail(HILDON_IS_CAPTION(caption), "");
932   priv = HILDON_CAPTION_GET_PRIVATE(caption);
933
934   return (gchar*)gtk_label_get_text(GTK_LABEL(GTK_LABEL(priv->label)));
935 }
936
937 /**
938  * hildon_caption_set_separator:
939  * @caption : a #HildonCaption
940  * @separator : the separator to use
941  *
942  * Sets the separator character that appears after the label.  
943  * The default seaparator character is ":"
944  * separately.
945  */
946
947 void hildon_caption_set_separator( HildonCaption *caption, 
948                                    const gchar *separator )
949 {
950   g_return_if_fail( HILDON_IS_CAPTION(caption) );
951
952   g_object_set( G_OBJECT(caption), "separator", separator, NULL );
953 }
954
955 /**
956  * hildon_caption_get_separator:
957  * @caption : a #HildonCaption
958  *
959  * Gets separator string of #HildonCaption
960  *
961  * @Returns : the text currently being used as the separator of the caption
962  *  control. The string is owned by the caption control and the caller should
963  *  never free or modify this value.   
964  */
965
966 gchar *hildon_caption_get_separator( const HildonCaption *caption )
967 {
968   HildonCaptionPrivate *priv;
969   g_return_val_if_fail(HILDON_IS_CAPTION(caption), "");
970   priv = HILDON_CAPTION_GET_PRIVATE(caption);
971
972   return priv->separator;
973 }
974
975
976 /**
977  * hildon_caption_get_control:
978  * @caption : a #HildonCaption
979  *
980  * Gets caption's control.
981  * 
982  * @Returns : a #GtkWidget
983  *  
984  * Deprecated: use gtk_bin_get_child instead
985  */
986 GtkWidget *hildon_caption_get_control( const HildonCaption *caption )
987 {
988   g_return_val_if_fail( HILDON_IS_CAPTION(caption), NULL );
989   return GTK_BIN(caption)->child;
990 }
991
992 /*activates the child control
993  *We have this signal so that if needed an application can 
994  *know when we've been activated (useful for captions with
995  *multiple children
996  */
997 /* FIXME: There never are multiple children. Possibly activate
998    signal could be removed entirely? (does anyone use it?) */
999 static void hildon_caption_activate( GtkWidget *widget )
1000 {
1001   HildonCaptionPrivate *priv;
1002   GtkWidget *child = GTK_BIN(widget)->child;
1003   priv = HILDON_CAPTION_GET_PRIVATE(widget);
1004
1005   gtk_widget_grab_focus( child );
1006 }
1007
1008 /**
1009  * hildon_caption_set_child_expand:
1010  * @caption : a #HildonCaption
1011  * @expand : gboolean to determine is the child expandable
1012  *
1013  * Sets child expandability.
1014  */
1015 void hildon_caption_set_child_expand( HildonCaption *caption, gboolean expand )
1016 {
1017   HildonCaptionPrivate *priv = NULL;
1018   GtkWidget *child = NULL;
1019   g_return_if_fail( HILDON_IS_CAPTION(caption) );
1020
1021   priv = HILDON_CAPTION_GET_PRIVATE(caption);
1022
1023   /* Did the setting really change? */
1024   if( priv->expand == expand )
1025     return;
1026
1027   priv->expand = expand;
1028   child = GTK_BIN(caption)->child;
1029
1030   /* We do not have a child, nothing to do */
1031   if( !GTK_IS_WIDGET(child) )
1032     return;
1033
1034   if( GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (caption) )
1035     gtk_widget_queue_resize( child );
1036
1037   gtk_widget_child_notify( child, "expand" );
1038 }
1039
1040 /**
1041  * hildon_caption_get_child_expand:
1042  * @caption : a #HildonCaption
1043  *
1044  * Gets childs expandability.
1045  *
1046  * @Returns : wheter the child is expandable or not.
1047  */
1048 gboolean hildon_caption_get_child_expand( const HildonCaption *caption )
1049 {
1050   HildonCaptionPrivate *priv = NULL;
1051   g_return_val_if_fail( HILDON_IS_CAPTION(caption), FALSE );
1052   priv = HILDON_CAPTION_GET_PRIVATE(caption);
1053   return priv->expand;
1054 }
1055
1056 /**
1057  * hildon_caption_set_control: 
1058  * @caption : a #HildonCaption
1059  * @control : the control to use. Control should not be NULL.
1060  *
1061  * Sets the control of the caption. 
1062  * The old control will be destroyed, unless the caller has added a
1063  * reference to it.
1064  * Function unparents the old control (if there is one) and adds the new
1065  * control.
1066  *
1067  * Deprecated: use gtk_container_add 
1068  */
1069 void hildon_caption_set_control( HildonCaption *caption, GtkWidget *control )
1070 {
1071   GtkWidget *child = NULL;
1072   g_return_if_fail( HILDON_IS_CAPTION(caption) );
1073   child = GTK_BIN(caption)->child;
1074
1075   if( child )
1076     gtk_container_remove( GTK_CONTAINER(caption), child );
1077     
1078   if( control )
1079   {
1080     gtk_container_add( GTK_CONTAINER(caption), control );
1081     child = control;
1082   }
1083   else
1084     child = NULL;
1085 }
1086
1087 static void
1088 hildon_caption_set_label_text( HildonCaptionPrivate *priv )
1089 {
1090   gchar *tmp = NULL;
1091   g_assert ( priv != NULL );
1092
1093   if ( priv->text )
1094   {
1095     if( priv->separator )
1096     {
1097       /* Don't duplicate the separator, if the string already contains one */
1098       if (g_str_has_suffix(priv->text, priv->separator))
1099       {
1100         gtk_label_set_text( GTK_LABEL( priv->label ), priv->text );
1101       }
1102       else
1103       {
1104         /* Append separator and set text */
1105         tmp = g_strconcat( priv->text, priv->separator, NULL );
1106         gtk_label_set_text( GTK_LABEL( priv->label ), tmp );
1107         g_free( tmp );
1108       }
1109     }
1110     else
1111     {
1112       gtk_label_set_text( GTK_LABEL( priv->label ), priv->text );
1113     }
1114   }
1115   else
1116   {
1117     /* Clear the label */
1118     gtk_label_set_text( GTK_LABEL( priv->label ), "" );
1119   }
1120
1121 }
1122
1123 /**
1124  * hildon_caption_set_label_alignment:
1125  * @caption: a #HildonCaption widget
1126  * @alignment: new vertical alignment
1127  *
1128  * Sets the vertical alignment to be used for the
1129  * text part of the caption. Applications need to
1130  * align the child control themselves.
1131  *
1132  * Since: 0.12.0
1133  */   
1134 void hildon_caption_set_label_alignment(HildonCaption *caption, 
1135                                         gfloat alignment)
1136 {
1137   HildonCaptionPrivate *priv;
1138
1139   g_return_if_fail(HILDON_IS_CAPTION(caption));
1140
1141   priv = HILDON_CAPTION_GET_PRIVATE(caption);
1142   g_object_set(priv->label, "yalign", alignment, NULL);
1143   g_object_set(priv->icon_align, "yalign", alignment, NULL);
1144
1145 }
1146
1147 /**
1148  * hildon_caption_get_label_alignment:
1149  * @caption: a #HildonCaption widget
1150  *
1151  * Gets current vertical alignment for the text part.
1152  *
1153  * Returns: vertical alignment
1154  *
1155  * Since: 0.12.0
1156  */
1157 gfloat hildon_caption_get_label_alignment(HildonCaption *caption)
1158 {
1159   HildonCaptionPrivate *priv;
1160   gfloat result;
1161
1162   g_return_val_if_fail( HILDON_IS_CAPTION(caption), 0);
1163   priv = HILDON_CAPTION_GET_PRIVATE(caption);
1164   g_object_get(priv->label, "yalign", &result, NULL);
1165
1166   return result;
1167 }