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