Fixing documentation for HildonBanner. Fixing documentation for HildonCaption. Fixing...
[hildon] / src / hildon-banner.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.
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-banner 
27  * @short_description: A widget used to display timed notifications. 
28  *
29  * #HildonBanner can be used to display a short, timed notification 
30  * or information to the user. It can communicate that a 
31  * task has been finished or the application state has changed.
32  * Banners should be used only to display non-critical pieces of 
33  * information.
34  *
35  */
36
37 #ifdef                                          HAVE_CONFIG_H
38 #include                                        <config.h>
39 #endif
40
41 #include                                        "hildon-banner.h"
42 #include                                        <gtk/gtkhbox.h>
43 #include                                        <gtk/gtkimage.h>
44 #include                                        <gtk/gtkicontheme.h>
45 #include                                        <string.h>
46 #include                                        <X11/X.h>
47 #include                                        <X11/Xatom.h>
48 #include                                        "hildon-defines.h"
49 #include                                        "hildon-banner-private.h"
50
51 /* position relative to the screen */
52
53 #define                                         HILDON_BANNER_WINDOW_X 30
54
55 #define                                         HILDON_BANNER_WINDOW_Y 73
56
57 #define                                         HILDON_BANNER_WINDOW_FULLSCREEN_Y 20
58
59 /* max widths */
60
61 #define                                         HILDON_BANNER_PROGRESS_WIDTH 104
62
63 #define                                         HILDON_BANNER_LABEL_MAX_TIMED 375
64
65 #define                                         HILDON_BANNER_LABEL_MAX_PROGRESS 375 /*265*/
66
67 /* default timeout */
68
69 #define                                         HILDON_BANNER_TIMEOUT 3000
70
71 /* default icons */
72
73 #define                                         HILDON_BANNER_DEFAULT_ICON "qgn_note_infoprint"
74
75 #define                                         HILDON_BANNER_DEFAULT_PROGRESS_ANIMATION "qgn_indi_pball_a"
76
77 enum 
78 {
79     PROP_0,
80     PROP_PARENT_WINDOW, 
81     PROP_IS_TIMED
82 };
83
84 static GtkWidget*                               global_timed_banner = NULL;
85
86 static Window 
87 get_current_app_window                          (void);
88
89 static gboolean 
90 check_fullscreen_state                          (Window window);
91
92 static GQuark 
93 hildon_banner_timed_quark                       (void);
94
95 static void 
96 hildon_banner_bind_label_style                  (HildonBanner *self,
97                                                  const gchar *name);
98
99 static gboolean 
100 hildon_banner_timeout                           (gpointer data);
101
102 static gboolean 
103 hildon_banner_clear_timeout                     (HildonBanner *self);
104
105 static void 
106 hildon_banner_ensure_timeout                    (HildonBanner *self);
107
108 static void 
109 hildon_banner_set_property                      (GObject *object,
110                                                  guint prop_id,
111                                                  const GValue *value,
112                                                  GParamSpec *pspec);
113     
114 static void 
115 hildon_banner_get_property                      (GObject *object,
116                                                  guint prop_id,
117                                                  GValue *value,
118                                                  GParamSpec *pspec);
119
120 static void
121 hildon_banner_destroy                           (GtkObject *object);
122         
123 static GObject*
124 hildon_banner_real_get_instance                 (GObject *window, 
125                                                  gboolean timed);
126
127 static GObject* 
128 hildon_banner_constructor                       (GType type,
129                                                  guint n_construct_params,
130                                                  GObjectConstructParam *construct_params);
131
132 static gboolean 
133 hildon_banner_map_event                         (GtkWidget *widget, 
134                                                  GdkEventAny *event);
135
136 static void 
137 force_to_wrap_truncated                         (HildonBanner *banner);
138
139 static void
140 hildon_banner_check_position                    (GtkWidget *widget);
141
142 static void
143 hildon_banner_realize                           (GtkWidget *widget);
144
145 static void 
146 hildon_banner_class_init                        (HildonBannerClass *klass);
147
148 static void 
149 hildon_banner_init                              (HildonBanner *self);
150
151 static void
152 hildon_banner_ensure_child                      (HildonBanner *self, 
153                                                  GtkWidget *user_widget,
154                                                  guint pos,
155                                                  GType type,
156                                                  const gchar *first_property, 
157                                                  ...);
158
159 static HildonBanner*
160 hildon_banner_get_instance_for_widget           (GtkWidget *widget, 
161                                                  gboolean timed);
162
163 static GtkWindowClass*                          parent_class = NULL;
164
165 /**
166  * hildon_banner_get_type:
167  *
168  * Initializes and returns the type of a hildon banner.
169  *
170  * @Returns: GType of #HildonBanner
171  */
172 GType G_GNUC_CONST 
173 hildon_banner_get_type                          (void)
174 {
175     static GType banner_type = 0;
176
177     if (! banner_type)
178     {
179         static const GTypeInfo banner_info = {
180             sizeof (HildonBannerClass),
181             NULL,       /* base_init */
182             NULL,       /* base_finalize */
183             (GClassInitFunc) hildon_banner_class_init,
184             NULL,       /* class_finalize */
185             NULL,       /* class_data */
186             sizeof (HildonBanner),
187             0,  /* n_preallocs */
188             (GInstanceInitFunc) hildon_banner_init,
189         };
190         banner_type = g_type_register_static (GTK_TYPE_WINDOW,
191                 "HildonBanner", &banner_info, 0 );
192     }
193     return banner_type;
194 }
195
196 /* copy/paste from old infoprint implementation: Use matchbox 
197    properties to find the topmost application window */
198 static Window 
199 get_current_app_window                          (void)
200 {
201     unsigned long n;
202     unsigned long extra;
203     int format;
204     int status;
205
206     Atom atom_current_app_window = gdk_x11_get_xatom_by_name ("_MB_CURRENT_APP_WINDOW");
207     Atom realType;
208     Window win_result = None;
209     guchar *data_return = NULL;
210
211     status = XGetWindowProperty (GDK_DISPLAY(), GDK_ROOT_WINDOW (), 
212             atom_current_app_window, 0L, 16L,
213             0, XA_WINDOW, &realType, &format,
214             &n, &extra, 
215             &data_return);
216
217     if (status == Success && realType == XA_WINDOW && format == 32 && n == 1 && data_return != NULL)
218     {
219         win_result = ((Window*) data_return)[0];
220     } 
221
222     if (data_return) 
223         XFree (data_return);    
224
225     return win_result;
226 }
227
228 /* Checks if a window is in fullscreen state or not. This
229    information is needed when banners are positioned on screen.
230    copy/paste from old infoprint implementation.  */
231 static gboolean 
232 check_fullscreen_state                          (Window window)
233 {
234     unsigned long n;
235     unsigned long extra;
236     int format, status, i; 
237     guchar *data_return = NULL;
238
239     Atom realType;
240     Atom atom_window_state = gdk_x11_get_xatom_by_name ("_NET_WM_STATE");
241     Atom atom_fullscreen   = gdk_x11_get_xatom_by_name ("_NET_WM_STATE_FULLSCREEN");
242
243     if (window == None)
244         return FALSE;
245
246     /* in some cases XGetWindowProperty seems to generate BadWindow,
247        so at the moment this function does not always work perfectly */
248     gdk_error_trap_push ();
249     status = XGetWindowProperty (GDK_DISPLAY (), window,
250             atom_window_state, 0L, 1000000L,
251             0, XA_ATOM, &realType, &format,
252             &n, &extra, &data_return);
253
254     gdk_flush ();
255
256     if (gdk_error_trap_pop ())
257         return FALSE;
258
259     if (status == Success && realType == XA_ATOM && format == 32 && n > 0)
260     {
261         for (i=0; i < n; i++)
262             if  (((Atom*)data_return)[i] && ((Atom*)data_return)[i] == atom_fullscreen)
263             {
264                 if (data_return) XFree (data_return);
265                 return TRUE;
266             }
267     }
268
269     if (data_return) 
270         XFree (data_return);
271
272     return FALSE;
273 }
274
275 static GQuark 
276 hildon_banner_timed_quark                       (void)
277 {
278     static GQuark quark = 0;
279
280     if (G_UNLIKELY(quark == 0))
281         quark = g_quark_from_static_string ("hildon-banner-timed");
282
283     return quark;
284 }
285
286 /* Set the label name to make the correct rc-style attached into it */
287 static void 
288 hildon_banner_bind_label_style                  (HildonBanner *self,
289                                                  const gchar *name)
290 {
291     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
292     g_assert (priv);
293
294     GtkWidget *label = priv->label;
295
296     /* Too bad that we cannot really reset the widget name */
297     gtk_widget_set_name (label, name ? name : g_type_name (GTK_WIDGET_TYPE (label)));
298 }
299
300 /* In timeout function we automatically destroy timed banners */
301 static gboolean 
302 hildon_banner_timeout                           (gpointer data)
303 {
304     GtkWidget *widget;
305     GdkEvent *event;
306     gboolean continue_timeout = FALSE;
307
308     GDK_THREADS_ENTER ();
309
310     g_assert (HILDON_IS_BANNER (data));
311
312     widget = GTK_WIDGET (data);
313     g_object_ref (widget);
314
315     /* If the banner is currently visible (it normally should), 
316        we simulate clicking the close button of the window.
317        This allows applications to reuse the banner by prevent
318        closing it etc */
319     if (GTK_WIDGET_DRAWABLE (widget))
320     {
321         event = gdk_event_new (GDK_DELETE);
322         event->any.window = g_object_ref (widget->window);
323         event->any.send_event = FALSE;
324         continue_timeout = gtk_widget_event (widget, event);
325         gdk_event_free (event);
326     }
327
328     if (! continue_timeout)
329         gtk_widget_destroy (widget);
330
331     g_object_unref (widget);
332
333     GDK_THREADS_LEAVE ();
334
335     return continue_timeout;
336 }
337
338 static gboolean 
339 hildon_banner_clear_timeout                     (HildonBanner *self)
340 {
341     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
342     g_assert (priv);
343
344     if (priv->timeout_id != 0) {
345         g_source_remove (priv->timeout_id);
346         priv->timeout_id = 0;
347         return TRUE;
348     }
349
350     return FALSE;
351 }
352
353 static void 
354 hildon_banner_ensure_timeout                    (HildonBanner *self)
355 {
356     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
357     g_assert (priv);
358
359     if (priv->timeout_id == 0 && priv->is_timed)
360         priv->timeout_id = g_timeout_add (HILDON_BANNER_TIMEOUT, 
361                 hildon_banner_timeout, self);
362 }
363
364 static void 
365 hildon_banner_set_property                      (GObject *object,
366                                                  guint prop_id,
367                                                  const GValue *value,
368                                                  GParamSpec *pspec)
369 {
370     GtkWidget *window;
371     GdkGeometry geom;
372     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
373     g_assert (priv);
374
375     switch (prop_id) {
376
377         case PROP_IS_TIMED:
378             priv->is_timed = g_value_get_boolean (value);
379
380             /* Timed and progress notifications have different
381                pixel size values for text. 
382                We force to use requisition size for timed banners 
383                in order to avoid resize problems when reusing the
384                window (see bug #24339) */
385             geom.max_width = priv->is_timed ? -1
386                 : HILDON_BANNER_LABEL_MAX_PROGRESS;
387             geom.max_height = -1;
388             gtk_window_set_geometry_hints (GTK_WINDOW (object), 
389                     priv->label, &geom, GDK_HINT_MAX_SIZE);
390             break;
391
392         case PROP_PARENT_WINDOW:
393             window = g_value_get_object (value);         
394
395             gtk_window_set_transient_for (GTK_WINDOW (object), (GtkWindow *) window);
396
397             if (window)
398                 gtk_window_set_destroy_with_parent (GTK_WINDOW (object), TRUE);
399
400             break;
401
402         default:
403             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
404             break;
405     }
406 }
407
408 static void 
409 hildon_banner_get_property                      (GObject *object,
410                                                  guint prop_id,
411                                                  GValue *value,
412                                                  GParamSpec *pspec)
413 {
414     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
415     g_assert (priv);
416
417     switch (prop_id)
418     {
419         case PROP_IS_TIMED:
420             g_value_set_boolean (value, priv->is_timed);
421             break;
422
423         case PROP_PARENT_WINDOW:
424             g_value_set_object (value, gtk_window_get_transient_for (GTK_WINDOW (object)));
425             break;
426
427         default:
428             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
429             break;
430     }
431 }
432
433 static void
434 hildon_banner_destroy                           (GtkObject *object)
435 {
436     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
437     g_assert (priv);
438
439     HildonBanner *self;
440     GObject *parent_window;
441
442     g_assert (HILDON_IS_BANNER (object));
443     self = HILDON_BANNER (object);
444
445     /* Drop possible global pointer. That can hold reference to us */
446     if ((gpointer) object == (gpointer) global_timed_banner) {
447         global_timed_banner = NULL;
448         g_object_unref (object);
449     }
450
451     /* Remove the data from parent window for timed banners. Those hold reference */
452     if (priv->is_timed && (parent_window = (GObject *) gtk_window_get_transient_for (GTK_WINDOW (object))) != NULL)
453         g_object_set_qdata (parent_window, hildon_banner_timed_quark (), NULL);
454
455     (void) hildon_banner_clear_timeout (self);
456
457     if (GTK_OBJECT_CLASS (parent_class)->destroy)
458         GTK_OBJECT_CLASS (parent_class)->destroy (object);
459 }
460
461 /* Search a previous banner instance */
462 static GObject*
463 hildon_banner_real_get_instance                 (GObject *window, 
464                                                  gboolean timed)
465 {
466     g_assert (GTK_IS_WINDOW (window));
467
468     if (timed) {
469         /* If we have a parent window, the previous instance is stored there */
470         if (window) 
471             return g_object_get_qdata(window, hildon_banner_timed_quark ());
472
473         /* System notification instance is stored into global pointer */
474         return (GObject *) global_timed_banner;
475     }
476
477     /* Non-timed banners are normal (non-singleton) objects */
478     return NULL;
479 }
480
481 /* By overriding constructor we force timed banners to be
482    singletons for each window */
483 static GObject* 
484 hildon_banner_constructor                       (GType type,
485                                                  guint n_construct_params,
486                                                  GObjectConstructParam *construct_params)
487 {
488     GObject *banner, *window = NULL;
489     gboolean timed = FALSE;
490     guint i;
491
492     /* Search banner type information from parameters in order
493        to locate the possible previous banner instance. */
494     for (i = 0; i < n_construct_params; i++)
495     {
496         if (strcmp(construct_params[i].pspec->name, "parent-window") == 0)
497             window = g_value_get_object (construct_params[i].value);       
498         else if (strcmp(construct_params[i].pspec->name, "is-timed") == 0)
499             timed = g_value_get_boolean (construct_params[i].value);
500     }
501
502     /* Try to get a previous instance if such exists */
503     banner = hildon_banner_real_get_instance (window, timed);
504     if (! banner)
505     {
506         /* We have to create a new banner */
507         banner = G_OBJECT_CLASS (parent_class)->constructor (type, n_construct_params, construct_params);
508
509         /* Store the newly created singleton instance either into parent 
510            window data or into global variables. */
511         if (timed) {
512             if (window) {
513                 g_object_set_qdata_full (G_OBJECT (window), hildon_banner_timed_quark (), 
514                         g_object_ref (banner), g_object_unref); 
515             } else {
516                 g_assert (global_timed_banner == NULL);
517                 global_timed_banner = g_object_ref (banner);
518             }
519         }
520     }
521     else {
522         /* FIXME: This is a hack! We have to manually freeze
523            notifications. This is normally done by g_object_init, but we
524            are not going to call that. g_object_newv will otherwise give
525            a critical like this:
526
527            GLIB CRITICAL ** GLib-GObject - g_object_notify_queue_thaw: 
528            assertion `nqueue->freeze_count > 0' failed */
529
530         g_object_freeze_notify (banner);
531     }
532
533     /* We restart possible timeouts for each new timed banner request */
534     if (timed && hildon_banner_clear_timeout (HILDON_BANNER (banner)))
535         hildon_banner_ensure_timeout (HILDON_BANNER(banner));
536
537     return banner;
538 }
539
540 /* We start the timer for timed notifications after the window appears on screen */
541 static gboolean 
542 hildon_banner_map_event                         (GtkWidget *widget, 
543                                                  GdkEventAny *event)
544 {
545     gboolean result = FALSE;
546
547     if (GTK_WIDGET_CLASS (parent_class)->map_event)
548         result = GTK_WIDGET_CLASS (parent_class)->map_event (widget, event);
549
550     hildon_banner_ensure_timeout (HILDON_BANNER(widget));
551
552     return result;
553 }  
554
555
556 /* force to wrap truncated label by setting explicit size request
557  * see N#27000 and G#329646 */
558 static void 
559 force_to_wrap_truncated                         (HildonBanner *banner)
560 {
561     GtkLabel *label;
562     PangoLayout *layout;
563     int width_text, width_max;
564     int width = -1;
565     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (banner);
566
567     g_assert (priv);
568     label = GTK_LABEL (priv->label);
569
570     layout = gtk_label_get_layout (label);
571     width_text  = PANGO_PIXELS(pango_layout_get_width (layout));
572     /* = width to which the lines of the PangoLayout should be wrapped */
573
574     width_max = priv->is_timed ? HILDON_BANNER_LABEL_MAX_TIMED
575         : HILDON_BANNER_LABEL_MAX_PROGRESS;
576
577     if (width_text >= width_max) {
578         /* explicitly request maximum size to force wrapping */
579         PangoRectangle logical;
580
581         pango_layout_set_width (layout, width_max * PANGO_SCALE);
582         pango_layout_get_extents (layout, NULL, &logical);
583
584         width = PANGO_PIXELS (logical.width);
585     }
586
587     /* use fixed width when wrapping or natural one otherwise */
588     gtk_widget_set_size_request (GTK_WIDGET (label), width, -1);
589 }
590
591
592 static void
593 hildon_banner_check_position                    (GtkWidget *widget)
594 {
595     gint x, y;
596     GtkRequisition req;
597
598     force_to_wrap_truncated (HILDON_BANNER(widget)); /* see N#27000 and G#329646 */
599
600     gtk_widget_size_request (widget, &req);
601
602     if (req.width == 0)
603     {
604         return;
605     }
606
607     x = gdk_screen_width() - HILDON_BANNER_WINDOW_X - req.width;
608     y = check_fullscreen_state (get_current_app_window ()) ? 
609         HILDON_BANNER_WINDOW_FULLSCREEN_Y : HILDON_BANNER_WINDOW_Y;
610
611     gtk_window_move (GTK_WINDOW (widget), x, y);
612 }
613
614 static void
615 hildon_banner_realize                           (GtkWidget *widget)
616 {
617     /* We let the parent to init widget->window before we need it */
618     if (GTK_WIDGET_CLASS (parent_class)->realize)
619         GTK_WIDGET_CLASS (parent_class)->realize (widget);
620
621     /* We use special hint to turn the banner into information notification. */
622     gdk_window_set_type_hint (widget->window, GDK_WINDOW_TYPE_HINT_MESSAGE);
623
624     hildon_banner_check_position (widget);
625 }
626
627 static void 
628 hildon_banner_class_init                        (HildonBannerClass *klass)
629 {
630     GObjectClass *object_class;
631     GtkWidgetClass *widget_class;
632
633     object_class = G_OBJECT_CLASS (klass);
634     widget_class = GTK_WIDGET_CLASS (klass);
635     parent_class = g_type_class_peek_parent (klass);
636
637     /* Append private structure to class. This is more elegant than
638        on g_new based approach */
639     g_type_class_add_private (klass, sizeof (HildonBannerPrivate));
640
641     /* Override virtual methods */
642     object_class->constructor = hildon_banner_constructor;
643     object_class->set_property = hildon_banner_set_property;
644     object_class->get_property = hildon_banner_get_property;
645     GTK_OBJECT_CLASS (klass)->destroy = hildon_banner_destroy;
646     widget_class->map_event = hildon_banner_map_event;
647     widget_class->realize = hildon_banner_realize;
648
649     /* Install properties.
650        We need construct properties for singleton purposes */
651
652     /**
653      * HildonBanner:parent-window:
654      *
655      * The window for which the banner will be singleton. 
656      *                      
657      */
658     g_object_class_install_property (object_class, PROP_PARENT_WINDOW,
659             g_param_spec_object ("parent-window",
660                 "Parent window",
661                 "The window for which the banner will be singleton",
662                 GTK_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
663
664     /**
665      * HildonBanner:is-timed:
666      *
667      * Whether the banner is timed and goes away automatically.
668      *                      
669      */
670     g_object_class_install_property (object_class, PROP_IS_TIMED,
671             g_param_spec_boolean ("is-timed",
672                 "Is timed",
673                 "Whether or not the notification goes away automatically "
674                 "after the specified time has passed",
675                 FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
676 }
677
678 static void 
679 hildon_banner_init                              (HildonBanner *self)
680 {
681     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
682     g_assert (self);
683
684     /* Initialize the common layout inside banner */
685     priv->layout = gtk_hbox_new (FALSE, HILDON_MARGIN_DEFAULT);
686
687     priv->label = g_object_new (GTK_TYPE_LABEL, NULL);
688     gtk_label_set_line_wrap (GTK_LABEL (priv->label), TRUE);
689
690     gtk_container_set_border_width (GTK_CONTAINER (priv->layout), HILDON_MARGIN_DEFAULT);
691     gtk_container_add (GTK_CONTAINER (self), priv->layout);
692     gtk_box_pack_start (GTK_BOX (priv->layout), priv->label, TRUE, TRUE, 0);
693
694     gtk_window_set_accept_focus (GTK_WINDOW (self), FALSE);
695 }
696
697 /* Makes sure that icon/progress item contains the desired type
698    of item. If possible, tries to avoid creating a new widget but
699    reuses the existing one */
700 static void
701 hildon_banner_ensure_child                      (HildonBanner *self, 
702                                                  GtkWidget *user_widget,
703                                                  guint pos,
704                                                  GType type,
705                                                  const gchar *first_property, 
706                                                  ...)
707 {
708     GtkWidget *widget;
709     va_list args;
710     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
711
712     g_assert (HILDON_IS_BANNER (self));
713     g_assert (priv);
714
715     widget = priv->main_item;
716     va_start (args, first_property);
717
718     /* Reuse existing widget if possible */
719     if (! user_widget && G_TYPE_CHECK_INSTANCE_TYPE (widget, type))
720     {
721         g_object_set_valist (G_OBJECT (widget), first_property, args);
722     }
723     else
724     {
725         /* We have to abandon old content widget */
726         if (widget)
727             gtk_container_remove (GTK_CONTAINER (priv->layout), widget);
728
729         /* Use user provided widget or create a new one */
730         priv->main_item = widget = user_widget ? 
731             user_widget : GTK_WIDGET (g_object_new_valist(type, first_property, args));
732         gtk_box_pack_start (GTK_BOX (priv->layout), widget, TRUE, TRUE, 0);
733     }
734
735     /* We make sure that the widget exists in desired position. Different
736        banners place this child widget to different places */
737     gtk_box_reorder_child (GTK_BOX (priv->layout), widget, pos);
738     va_end (args);
739 }
740
741 /* Creates a new banner instance or uses an existing one */
742 static HildonBanner*
743 hildon_banner_get_instance_for_widget           (GtkWidget *widget, 
744                                                  gboolean timed)
745 {
746     GtkWidget *window;
747
748     g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
749     window = widget ? gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW) : NULL;
750     return g_object_new (HILDON_TYPE_BANNER, "parent-window", window, "is-timed", timed, NULL);
751 }
752
753 /**
754  * hildon_banner_show_information:
755  * @widget: the #GtkWidget that is the owner of the banner
756  * @icon_name: the name of icon to use. Can be %NULL for default icon
757  * @text: Text to display
758  *
759  * This function creates and displays an information banner that
760  * automatically goes away after certain time period. For each window
761  * in your application there can only be one timed banner, so if you
762  * spawn a new banner before the earlier one has timed out, the
763  * previous one will be replaced.
764  *
765  */
766 void 
767 hildon_banner_show_information                  (GtkWidget *widget, 
768                                                  const gchar *icon_name,
769                                                  const gchar *text)
770 {
771     HildonBanner *banner;
772
773     g_return_if_fail (icon_name == NULL || icon_name[0] != 0);
774     g_return_if_fail (text != NULL);
775
776     /* Prepare banner */
777     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
778     hildon_banner_ensure_child (banner, NULL, 0, GTK_TYPE_IMAGE, 
779             "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
780             "icon-name", icon_name ? icon_name : HILDON_BANNER_DEFAULT_ICON,
781             "yalign", 0.0, 
782             NULL);
783
784     hildon_banner_set_text (banner, text);
785     hildon_banner_bind_label_style (banner, NULL);
786
787     /* Show the banner, since caller cannot do that */
788     gtk_widget_show_all (GTK_WIDGET (banner));
789 }
790
791 /**
792  * hildon_banner_show_informationf:
793  * @widget: the #GtkWidget that is the owner of the banner
794  * @icon_name: the name of icon to use. Can be %NULL for default icon
795  * @format: a printf-like format string
796  * @Varargs: arguments for the format string
797  *
798  * A helper function for #hildon_banner_show_information with
799  * string formatting.
800  *
801  */
802 void       
803 hildon_banner_show_informationf                 (GtkWidget *widget, 
804                                                  const gchar *icon_name,
805                                                  const gchar *format, 
806                                                  ...)
807 {
808     g_return_if_fail (format != NULL);
809
810     gchar *message;
811     va_list args;
812
813     va_start (args, format);
814     message = g_strdup_vprintf (format, args);
815     va_end (args);
816
817     hildon_banner_show_information (widget, icon_name, message);
818
819     g_free (message);
820 }
821
822 /**
823  * hildon_banner_show_information_with_markup:
824  * @widget: the #GtkWidget that wants to display banner
825  * @icon_name: the name of icon to use. Can be %NULL for default icon.
826  * @markup: a markup string to display (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
827  *
828  * This function creates and displays an information banner that
829  * automatically goes away after certain time period. For each window
830  * in your application there can only be one timed banner, so if you
831  * spawn a new banner before the earlier one has timed out, the
832  * previous one will be replaced.
833  *
834  */
835 void 
836 hildon_banner_show_information_with_markup      (GtkWidget *widget, 
837                                                  const gchar *icon_name, 
838                                                  const gchar *markup)
839 {
840     HildonBanner *banner;
841
842     g_return_if_fail (icon_name == NULL || icon_name[0] != 0);
843     g_return_if_fail (markup != NULL);
844
845     /* Prepare banner */
846     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
847
848     hildon_banner_ensure_child (banner, NULL, 0, GTK_TYPE_IMAGE, 
849             "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
850             "icon-name", icon_name ? icon_name : HILDON_BANNER_DEFAULT_ICON,
851             "yalign", 0.0, 
852             NULL);
853
854     hildon_banner_set_markup (banner, markup);
855     hildon_banner_bind_label_style (banner, NULL);
856
857     /* Show the banner, since caller cannot do that */
858     gtk_widget_show_all (GTK_WIDGET (banner));
859 }
860
861 /**
862  * hildon_banner_show_animation:
863  * @widget: the #GtkWidget that wants to display banner
864  * @animation_name: The progress animation to use. You usually can just
865  *                  pass %NULL for the default animation.
866  * @text: the text to display.
867  *
868  * Shows an animated progress notification. It's recommended not to try
869  * to show more than one progress notification at a time, since
870  * they will appear on top of each other. You can use progress
871  * notifications with timed banners. In this case the banners are
872  * located so that you can somehow see both.
873  *
874  * Please note that banners are destroyed automatically once the
875  * window they are attached to is closed. The pointer that you
876  * receive with this function do not contain additional references,
877  * so it can become invalid without warning (this is true for
878  * all toplevel windows in gtk). To make sure that the banner do not disapear
879  * automatically, you can separately ref the return value (this
880  * doesn't prevent the banner from disappearing, but the object it just
881  * not finalized). In this case you have to call both #gtk_widget_destroy 
882  * followed by #g_object_unref (in this order).
883  * 
884  * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy
885  *          once you are done with the banner.
886  *
887  */
888 GtkWidget*
889 hildon_banner_show_animation                    (GtkWidget *widget, 
890                                                  const gchar *animation_name, 
891                                                  const gchar *text)
892 {
893     HildonBanner *banner;
894     GtkIconTheme *theme; 
895     GtkIconInfo *info;
896     GtkWidget *image_widget;
897     const gchar *filename;
898
899     g_return_val_if_fail (animation_name == NULL || animation_name[0] != 0, NULL);
900     g_return_val_if_fail (text != NULL, NULL);
901
902     /* Find out which animation to use */
903     theme = gtk_icon_theme_get_default ();
904     info = gtk_icon_theme_lookup_icon (theme, animation_name ?   /* FIXME: consider using: gtk_icon_theme_load_icon() */
905             animation_name : HILDON_BANNER_DEFAULT_PROGRESS_ANIMATION,
906             HILDON_ICON_SIZE_NOTE, 0);
907
908     /* Try to load animation. One could try to optimize this 
909        to avoid loading the default animation during each call */
910     if (info) {
911         filename = gtk_icon_info_get_filename (info);
912         image_widget = gtk_image_new_from_file (filename);
913         gtk_icon_info_free (info);
914     } else {
915         g_warning ("Icon theme lookup for icon failed!");
916         image_widget = NULL;
917     }
918
919     /* Prepare banner */
920     banner = hildon_banner_get_instance_for_widget (widget, FALSE);
921     hildon_banner_ensure_child (banner, image_widget, 0,
922             GTK_TYPE_IMAGE, "yalign", 0.0, NULL);
923
924     hildon_banner_set_text (banner, text);
925     hildon_banner_bind_label_style (banner, NULL);
926
927     /* And show it */
928     gtk_widget_show_all (GTK_WIDGET (banner));
929
930     return (GtkWidget *) banner;
931 }
932
933 /**
934  * hildon_banner_show_progress:
935  * @widget: the #GtkWidget that wants to display banner
936  * @bar: Progressbar to use. You usually can just pass %NULL, unless
937  *       you want somehow customized progress bar.
938  * @text: text to display.
939  *
940  * Shows progress notification. See #hildon_banner_show_animation
941  * for more information.
942  * 
943  * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy
944  *          once you are done with the banner.
945  *
946  */
947 GtkWidget*
948 hildon_banner_show_progress                     (GtkWidget *widget, 
949                                                  GtkProgressBar *bar, 
950                                                  const gchar *text)
951 {
952     HildonBanner *banner;
953     HildonBannerPrivate *priv;
954
955     g_return_val_if_fail (bar == NULL || GTK_IS_PROGRESS_BAR (bar), NULL);
956     g_return_val_if_fail (text != NULL, NULL);
957
958
959     /* Prepare banner */
960     banner = hildon_banner_get_instance_for_widget (widget, FALSE);
961     priv = HILDON_BANNER_GET_PRIVATE (banner);
962     g_assert (priv);
963     hildon_banner_ensure_child (banner, (GtkWidget *) bar, -1, GTK_TYPE_PROGRESS_BAR, NULL);
964
965     gtk_widget_set_size_request (priv->main_item,
966             HILDON_BANNER_PROGRESS_WIDTH, -1);
967
968     hildon_banner_set_text (banner, text);
969     hildon_banner_bind_label_style (banner, NULL);
970
971     /* Show the banner */
972     gtk_widget_show_all (GTK_WIDGET (banner));
973
974     return GTK_WIDGET (banner);   
975 }
976
977 /**
978  * hildon_banner_set_text:
979  * @self: a #HildonBanner widget
980  * @text: a new text to display in banner
981  *
982  * Sets the text that is displayed in the banner.
983  *
984  */
985 void 
986 hildon_banner_set_text                          (HildonBanner *self, 
987                                                  const gchar *text)
988 {
989     GtkLabel *label;
990     HildonBannerPrivate *priv;
991
992     g_return_if_fail (HILDON_IS_BANNER (self));
993
994     priv = HILDON_BANNER_GET_PRIVATE (self);
995     g_assert (priv);
996
997     label = GTK_LABEL (priv->label);
998     gtk_label_set_text (label, text);
999
1000     hildon_banner_check_position (GTK_WIDGET (self));
1001 }
1002
1003 /**
1004  * hildon_banner_set_markup:
1005  * @self: a #HildonBanner widget
1006  * @markup: a new text with Pango markup to display in the banner
1007  *
1008  * Sets the text with markup that is displayed in the banner.
1009  *
1010  */
1011 void 
1012 hildon_banner_set_markup                        (HildonBanner *self, 
1013                                                  const gchar *markup)
1014 {
1015     GtkLabel *label;
1016     HildonBannerPrivate *priv;
1017
1018     g_return_if_fail (HILDON_IS_BANNER (self));
1019
1020     priv = HILDON_BANNER_GET_PRIVATE (self);
1021     g_assert (priv);
1022
1023     label = GTK_LABEL (priv->label);
1024     gtk_label_set_markup (label, markup);
1025
1026     hildon_banner_check_position (GTK_WIDGET(self));
1027 }
1028
1029 /**
1030  * hildon_banner_set_fraction:
1031  * @self: a #HildonBanner widget
1032  * @fraction: #gdouble
1033  *
1034  * The fraction is the completion of progressbar, 
1035  * the scale is from 0.0 to 1.0.
1036  * Sets the amount of fraction the progressbar has.
1037  *
1038  */
1039 void 
1040 hildon_banner_set_fraction                      (HildonBanner *self, 
1041                                                  gdouble fraction)
1042 {
1043     HildonBannerPrivate *priv;
1044
1045     g_return_if_fail (HILDON_IS_BANNER (self));
1046     priv = HILDON_BANNER_GET_PRIVATE (self);
1047     g_assert (priv);
1048
1049     g_return_if_fail (GTK_IS_PROGRESS_BAR (priv->main_item));
1050     gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->main_item), fraction);
1051 }
1052