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