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