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