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