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