2008-04-15 13:21:13 <timj@imendio.com>
[hildon] / src / hildon-bread-crumb-widget.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2007 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  * Author: Xan Lopez <xan.lopez@nokia.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301 USA
23  *
24  */
25
26 #include "hildon-bread-crumb-widget.h"
27 #include "hildon-defines.h"
28
29 #define HILDON_BREAD_CRUMB_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_BREAD_CRUMB_WIDGET, HildonBreadCrumbWidgetPrivate))
30
31 struct _HildonBreadCrumbWidgetPrivate
32 {
33   GtkWidget *hbox;
34   GtkWidget *label;
35   GtkWidget *icon;
36   GtkWidget *arrow;
37   gchar *text;
38
39   GtkPositionType icon_position;
40   gboolean constructed;
41   gboolean show_separator;
42 };
43
44 /* Signals */
45
46 enum {
47   LAST_SIGNAL
48 };
49
50 /* Properties */
51
52 enum {
53   PROP_0,
54   PROP_TEXT,
55   PROP_ICON,
56   PROP_ICON_POSITION,
57   PROP_SHOW_SEPARATOR
58 };
59
60 /*
61 static guint bread_crumb_item_signals[LAST_SIGNAL] = { 0 };
62 */
63
64 /* GType methods */
65
66 static void hildon_bread_crumb_widget_finalize (GObject *object);
67 static void hildon_bread_crumb_widget_set_property (GObject *object, guint prop_id,
68                                              const GValue *value, GParamSpec *pspec);
69 static void hildon_bread_crumb_widget_get_property (GObject *object, guint prop_id,
70                                              GValue *value, GParamSpec *pspec);
71 static GObject* hildon_bread_crumb_widget_constructor (GType                  type,
72                                                        guint                  n_construct_properties,
73                                                        GObjectConstructParam *construct_params);
74 static void hildon_bread_crumb_widget_set_contents (HildonBreadCrumbWidget *bread_crumb);
75
76 static void hildon_bread_crumb_widget_clicked (GtkButton *button);
77
78 static void hildon_bread_crumb_widget_bread_crumb_init (HildonBreadCrumbIface *iface);
79
80 static void hildon_bread_crumb_widget_get_natural_size (HildonBreadCrumb *bread_crumb,
81                                                         gint *width,
82                                                         gint *height);
83
84 G_DEFINE_TYPE_WITH_CODE (HildonBreadCrumbWidget, hildon_bread_crumb_widget, GTK_TYPE_BUTTON,
85                          G_IMPLEMENT_INTERFACE (HILDON_TYPE_BREAD_CRUMB,
86                                                 hildon_bread_crumb_widget_bread_crumb_init))
87
88 static void
89 hildon_bread_crumb_widget_class_init (HildonBreadCrumbWidgetClass *klass)
90 {
91   GObjectClass *gobject_class = (GObjectClass*)klass;
92   GtkButtonClass *button_class = (GtkButtonClass*)klass;
93
94     /* GObject signals */
95   gobject_class->constructor = hildon_bread_crumb_widget_constructor;
96   gobject_class->finalize = hildon_bread_crumb_widget_finalize;
97   gobject_class->set_property = hildon_bread_crumb_widget_set_property;
98   gobject_class->get_property = hildon_bread_crumb_widget_get_property;
99
100   /* GtkButton signals */
101   button_class->clicked = hildon_bread_crumb_widget_clicked;
102
103   /* Properties */
104   g_object_class_install_property (gobject_class,
105                                    PROP_TEXT,
106                                    g_param_spec_string ("text",
107                                                         "Text",
108                                                         "Text of the label widget inside the bread crumb",
109                                                         NULL,
110                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
111
112   g_object_class_install_property (gobject_class,
113                                    PROP_ICON,
114                                    g_param_spec_object ("icon",
115                                                         "Icon",
116                                                         "Image that will appear next to the bread crumb text",
117                                                         GTK_TYPE_WIDGET,
118                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
119
120   g_object_class_install_property (gobject_class,
121                                    PROP_ICON_POSITION,
122                                    g_param_spec_enum ("icon-position",
123                                                       "Icon position",
124                                                       "The position of the image relative to the text",
125                                                       GTK_TYPE_POSITION_TYPE,
126                                                       GTK_POS_LEFT,
127                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
128
129   g_object_class_install_property (gobject_class,
130                                    PROP_SHOW_SEPARATOR,
131                                    g_param_spec_boolean ("show-separator",
132                                                          "Show separator",
133                                                          "Show the separator attached to the item",
134                                                          TRUE,
135                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
136
137   /* Private data */
138   g_type_class_add_private (gobject_class, sizeof (HildonBreadCrumbWidgetPrivate));
139 }
140
141 static void
142 hildon_bread_crumb_widget_bread_crumb_init (HildonBreadCrumbIface *iface)
143 {
144   iface->get_natural_size = hildon_bread_crumb_widget_get_natural_size;
145 }
146
147 static GObject*
148 hildon_bread_crumb_widget_constructor (GType type,
149                                        guint n_construct_properties,
150                                        GObjectConstructParam *construct_params)
151 {
152   GObject *object;
153   HildonBreadCrumbWidget *bread_crumb;
154   HildonBreadCrumbWidgetPrivate *priv;
155
156   object = (* G_OBJECT_CLASS (hildon_bread_crumb_widget_parent_class)->constructor) (type,
157                                                                               n_construct_properties,
158                                                                               construct_params);
159
160   bread_crumb = HILDON_BREAD_CRUMB_WIDGET (object);
161   priv = bread_crumb->priv;
162   priv->constructed = TRUE;
163
164   priv->hbox = gtk_hbox_new (FALSE, 6);
165   gtk_container_add (GTK_CONTAINER (bread_crumb), priv->hbox);
166
167   /* Separator */
168   priv->arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
169   gtk_widget_set_name (priv->arrow, "hildon-bread-crumb-separator-arrow");
170   gtk_box_pack_start (GTK_BOX (priv->hbox), priv->arrow, FALSE, FALSE, 0);
171
172   if (priv->show_separator)
173     gtk_widget_show (priv->arrow);
174
175   gtk_widget_set_no_show_all (priv->arrow, TRUE);
176
177   /* Contents base container */
178   bread_crumb->contents = gtk_hbox_new (FALSE, HILDON_MARGIN_DEFAULT);
179   gtk_container_add (GTK_CONTAINER (priv->hbox), bread_crumb->contents);
180   gtk_widget_show (bread_crumb->contents);
181
182   if (priv->text || priv->icon)
183     hildon_bread_crumb_widget_set_contents (bread_crumb);
184
185   /* Show everything */
186   gtk_widget_show (priv->hbox);
187   
188   return object;
189 }
190
191 static void
192 hildon_bread_crumb_widget_init (HildonBreadCrumbWidget *item)
193 {
194   HildonBreadCrumbWidgetPrivate *priv = HILDON_BREAD_CRUMB_WIDGET_GET_PRIVATE (item);
195
196   item->priv = priv;
197
198   item->contents = NULL;
199
200   priv->constructed = FALSE;
201   priv->text = NULL;
202   priv->icon = NULL;
203   priv->icon_position = GTK_POS_LEFT;
204   priv->show_separator = TRUE;
205 }
206
207 static void
208 hildon_bread_crumb_widget_finalize (GObject *object)
209 {
210   HildonBreadCrumbWidgetPrivate *priv = HILDON_BREAD_CRUMB_WIDGET (object)->priv;
211
212   g_free (priv->text);
213
214   G_OBJECT_CLASS (hildon_bread_crumb_widget_parent_class)->finalize (object);
215 }
216
217 static void
218 hildon_bread_crumb_widget_clicked (GtkButton *button)
219 {
220   hildon_bread_crumb_activated (HILDON_BREAD_CRUMB (button));
221 }
222
223 static void
224 hildon_bread_crumb_widget_set_contents (HildonBreadCrumbWidget *bread_crumb)
225 {
226   GtkWidget *icon = NULL;
227   HildonBreadCrumbWidgetPrivate *priv = bread_crumb->priv;
228
229   if (!priv->constructed)
230     return;
231
232   if (!priv->text && !priv->icon)
233     return;
234
235   /* If the icon exists, keep it */
236   if (priv->icon)
237     {
238       icon = g_object_ref (priv->icon);
239       if (icon->parent)
240         gtk_container_remove (GTK_CONTAINER (icon->parent), icon);
241       priv->icon = NULL;
242     }
243
244   /* Reset contents */
245   if (bread_crumb->contents)
246     gtk_container_remove (GTK_CONTAINER (priv->hbox),
247                           bread_crumb->contents);
248
249   if (icon)
250     {
251       priv->icon = icon;
252       if (priv->icon_position == GTK_POS_LEFT ||
253           priv->icon_position == GTK_POS_RIGHT)
254           bread_crumb->contents = gtk_hbox_new (FALSE, HILDON_MARGIN_DEFAULT);
255       else
256           bread_crumb->contents = gtk_vbox_new (FALSE, HILDON_MARGIN_DEFAULT);
257
258       if (priv->icon_position == GTK_POS_LEFT ||
259           priv->icon_position == GTK_POS_TOP)
260         gtk_box_pack_start (GTK_BOX (bread_crumb->contents), priv->icon,
261                             FALSE, FALSE, 0);
262       else
263         gtk_box_pack_end (GTK_BOX (bread_crumb->contents), priv->icon,
264                           FALSE, FALSE, 0);
265         
266       if (priv->text)
267         {
268           priv->label = gtk_label_new (priv->text);
269           g_object_set (G_OBJECT (priv->label), "xalign", 0.0, NULL);
270           gtk_label_set_ellipsize (GTK_LABEL (priv->label),
271                                    PANGO_ELLIPSIZE_END);
272
273           if (priv->icon_position == GTK_POS_RIGHT ||
274               priv->icon_position == GTK_POS_BOTTOM)
275             gtk_box_pack_start (GTK_BOX (bread_crumb->contents), priv->label,
276                                 TRUE, TRUE, 0);
277           else
278             gtk_box_pack_end (GTK_BOX (bread_crumb->contents), priv->label,
279                               TRUE, TRUE, 0);
280           
281         }
282
283       gtk_box_pack_start (GTK_BOX (priv->hbox), bread_crumb->contents,
284                           TRUE, TRUE, 0);
285       gtk_widget_show_all (bread_crumb->contents);
286
287       g_object_unref (icon);
288     }
289   else
290     {
291       /* Only text */
292       bread_crumb->contents = gtk_hbox_new (FALSE, 0);
293       gtk_box_pack_start (GTK_BOX (priv->hbox), bread_crumb->contents,
294                           TRUE, TRUE, 0);
295
296       priv->label = gtk_label_new (priv->text);
297       g_object_set (G_OBJECT (priv->label), "xalign", 0.0, NULL);
298       gtk_label_set_ellipsize (GTK_LABEL (priv->label),
299                                PANGO_ELLIPSIZE_END);
300       gtk_box_pack_start (GTK_BOX (bread_crumb->contents), priv->label, TRUE, TRUE, 0);
301
302       gtk_widget_show_all (bread_crumb->contents);
303     }
304 }
305
306 static void
307 hildon_bread_crumb_widget_set_property (GObject *object, guint prop_id,
308                                         const GValue *value, GParamSpec *pspec)
309 {
310   HildonBreadCrumbWidget *item = HILDON_BREAD_CRUMB_WIDGET (object);
311
312   switch (prop_id)
313     {
314     case PROP_TEXT:
315       _hildon_bread_crumb_widget_set_text (item, g_value_get_string (value));
316       break;
317     case PROP_ICON:
318       _hildon_bread_crumb_widget_set_icon (item, (GtkWidget*)g_value_get_object (value));
319       break;
320     case PROP_ICON_POSITION:
321       _hildon_bread_crumb_widget_set_icon_position (item, g_value_get_enum (value));
322       break;
323     case PROP_SHOW_SEPARATOR:
324       _hildon_bread_crumb_widget_set_show_separator (item, g_value_get_boolean (value));
325       break;
326     default:
327       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
328       break;
329     }
330 }
331
332 static void
333 hildon_bread_crumb_widget_get_property (GObject *object, guint prop_id,
334                                         GValue *value, GParamSpec *pspec)
335 {
336   HildonBreadCrumbWidget *item = HILDON_BREAD_CRUMB_WIDGET (object);
337
338   switch (prop_id)
339     {
340     case PROP_TEXT:
341       g_value_set_string (value, item->priv->text);
342       break;
343     case PROP_ICON:
344       g_value_set_object (value, (GObject *)item->priv->icon);
345       break;
346     case PROP_ICON_POSITION:
347       g_value_set_enum (value, item->priv->icon_position);
348       break;
349     case PROP_SHOW_SEPARATOR:
350       g_value_set_boolean (value, item->priv->show_separator);
351       break;
352     default:
353       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
354       break;
355     }
356 }
357
358 void
359 _hildon_bread_crumb_widget_set_text (HildonBreadCrumbWidget *item,
360                                      const gchar *text)
361 {
362   HildonBreadCrumbWidgetPrivate *priv;
363
364   g_return_if_fail (HILDON_IS_BREAD_CRUMB_WIDGET (item));
365
366   priv = item->priv;
367
368   if (priv->text)
369     g_free (priv->text);
370
371   priv->text = g_strdup (text);
372
373   hildon_bread_crumb_widget_set_contents (item);
374
375   g_object_notify (G_OBJECT (item), "text");
376 }
377
378 const gchar*
379 _hildon_bread_crumb_widget_get_text (HildonBreadCrumbWidget *item)
380 {
381   HildonBreadCrumbWidgetPrivate *priv;
382
383   g_return_val_if_fail (HILDON_IS_BREAD_CRUMB_WIDGET (item), NULL);
384
385   priv = item->priv;
386
387   return priv->text;
388 }
389
390 void
391 _hildon_bread_crumb_widget_set_show_separator (HildonBreadCrumbWidget *item,
392                                                gboolean show_separator)
393 {
394   HildonBreadCrumbWidgetPrivate *priv;
395
396   g_return_if_fail (HILDON_IS_BREAD_CRUMB_WIDGET (item));
397
398   priv = item->priv;
399
400   if (priv->show_separator == show_separator)
401     return;
402   
403   priv->show_separator = show_separator;
404
405   if (!priv->constructed)
406     return;
407
408   if (show_separator)
409     gtk_widget_show (priv->arrow);
410   else
411     gtk_widget_hide (priv->arrow);
412
413   g_object_notify (G_OBJECT (item), "show-separator");
414 }
415
416 static void
417 hildon_bread_crumb_widget_get_natural_size (HildonBreadCrumb *bread_crumb,
418                                             gint *natural_width,
419                                             gint *natural_height)
420 {
421   GtkRequisition widget_req, label_req;
422   gint width, height;
423   PangoLayout *layout;
424   HildonBreadCrumbWidget *item;
425   HildonBreadCrumbWidgetPrivate *priv;
426
427   g_return_if_fail (HILDON_IS_BREAD_CRUMB_WIDGET (bread_crumb));
428
429   item = HILDON_BREAD_CRUMB_WIDGET (bread_crumb);
430   priv = item->priv;
431
432   gtk_widget_size_request (GTK_WIDGET (item), &widget_req);
433
434   layout = gtk_widget_create_pango_layout (priv->label, priv->text);
435   pango_layout_get_pixel_size (layout, &width, &height);
436   g_object_unref (layout);
437
438   if (natural_width)
439     {
440       *natural_width = widget_req.width;
441       /* Substract the size request of the label */
442       gtk_widget_size_request (priv->label, &label_req);
443       *natural_width -= label_req.width;
444
445       /* Add the "natural" width for the label */
446       *natural_width += width;
447       *natural_width += GTK_CONTAINER (item)->border_width * 2;
448     }
449
450   if (natural_height)
451     {
452       *natural_height = widget_req.height;
453       *natural_height += GTK_CONTAINER (item)->border_width * 2;
454     }
455 }
456
457 GtkWidget*
458 _hildon_bread_crumb_widget_new ()
459 {
460   return GTK_WIDGET (g_object_new (HILDON_TYPE_BREAD_CRUMB_WIDGET, NULL));
461 }
462
463 GtkWidget*
464 _hildon_bread_crumb_widget_new_with_text (const gchar *text)
465 {
466   return GTK_WIDGET (g_object_new (HILDON_TYPE_BREAD_CRUMB_WIDGET,
467                                    "text", text,
468                                    NULL));
469 }
470
471 GtkWidget*
472 _hildon_bread_crumb_widget_new_with_icon (GtkWidget *icon, const gchar *text)
473 {
474   return GTK_WIDGET (g_object_new (HILDON_TYPE_BREAD_CRUMB_WIDGET,
475                                    "icon", icon,
476                                    "text", text,
477                                    NULL));
478 }
479
480 void
481 _hildon_bread_crumb_widget_set_icon (HildonBreadCrumbWidget *bread_crumb,
482                                      GtkWidget *icon)
483 {
484   HildonBreadCrumbWidgetPrivate *priv;
485
486   g_return_if_fail (HILDON_IS_BREAD_CRUMB_WIDGET (bread_crumb));
487
488   priv = bread_crumb->priv;
489
490   priv->icon = icon;
491
492   hildon_bread_crumb_widget_set_contents (bread_crumb);
493
494   g_object_notify (G_OBJECT (bread_crumb), "icon");
495 }
496
497 void
498 _hildon_bread_crumb_widget_set_icon_position (HildonBreadCrumbWidget *bread_crumb,
499                                               GtkPositionType icon_position)
500 {
501   HildonBreadCrumbWidgetPrivate *priv;
502
503   g_return_if_fail (HILDON_IS_BREAD_CRUMB_WIDGET (bread_crumb));
504
505   priv = bread_crumb->priv;
506
507   if (priv->icon_position == icon_position)
508     return;
509
510   priv->icon_position = icon_position;
511
512   hildon_bread_crumb_widget_set_contents (bread_crumb);
513
514   g_object_notify (G_OBJECT (bread_crumb), "icon-position");
515 }