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