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