Importing several usefull fixes from the ubuntu-mobile branch.
[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     /* We let the parent to init widget->window before we need it */
648     if (GTK_WIDGET_CLASS (parent_class)->realize)
649         GTK_WIDGET_CLASS (parent_class)->realize (widget);
650
651     /* We use special hint to turn the banner into information notification. */
652     gdk_window_set_type_hint (widget->window, GDK_WINDOW_TYPE_HINT_NOTIFICATION);
653
654     hildon_banner_check_position (widget);
655 }
656
657 static void 
658 hildon_banner_class_init                        (HildonBannerClass *klass)
659 {
660     GObjectClass *object_class;
661     GtkWidgetClass *widget_class;
662
663     object_class = G_OBJECT_CLASS (klass);
664     widget_class = GTK_WIDGET_CLASS (klass);
665     parent_class = g_type_class_peek_parent (klass);
666
667     /* Append private structure to class. This is more elegant than
668        on g_new based approach */
669     g_type_class_add_private (klass, sizeof (HildonBannerPrivate));
670
671     /* Override virtual methods */
672     object_class->constructor = hildon_banner_constructor;
673     object_class->finalize = hildon_banner_finalize;
674     object_class->set_property = hildon_banner_set_property;
675     object_class->get_property = hildon_banner_get_property;
676     GTK_OBJECT_CLASS (klass)->destroy = hildon_banner_destroy;
677     widget_class->map_event = hildon_banner_map_event;
678     widget_class->realize = hildon_banner_realize;
679
680     /* Install properties.
681        We need construct properties for singleton purposes */
682
683     /**
684      * HildonBanner:parent-window:
685      *
686      * The window for which the banner will be singleton. 
687      *                      
688      */
689     g_object_class_install_property (object_class, PROP_PARENT_WINDOW,
690             g_param_spec_object ("parent-window",
691                 "Parent window",
692                 "The window for which the banner will be singleton",
693                 GTK_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
694
695     /**
696      * HildonBanner:is-timed:
697      *
698      * Whether the banner is timed and goes away automatically.
699      *                      
700      */
701     g_object_class_install_property (object_class, PROP_IS_TIMED,
702             g_param_spec_boolean ("is-timed",
703                 "Is timed",
704                 "Whether or not the notification goes away automatically "
705                 "after the specified time has passed",
706                 FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
707
708     /**
709      * HildonBanner:timeout:
710      *
711      * The time before making the banner banner go away. This needs 
712      * to be adjusted before the banner is mapped to the screen.
713      *                      
714      */
715     g_object_class_install_property (object_class, PROP_TIMEOUT,
716             g_param_spec_uint ("timeout",
717                 "Timeout",
718                 "The time before making the banner banner go away",
719                 0,
720                 10000,
721                 HILDON_BANNER_DEFAULT_TIMEOUT,
722                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
723 }
724
725 static void 
726 hildon_banner_init                              (HildonBanner *self)
727 {
728     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
729     g_assert (priv);
730
731     priv->parent = NULL;
732
733     /* Initialize the common layout inside banner */
734     priv->layout = gtk_hbox_new (FALSE, HILDON_MARGIN_DEFAULT);
735
736     priv->label = g_object_new (GTK_TYPE_LABEL, NULL);
737     gtk_label_set_line_wrap (GTK_LABEL (priv->label), TRUE);
738
739     gtk_container_set_border_width (GTK_CONTAINER (priv->layout), HILDON_MARGIN_DEFAULT);
740     gtk_container_add (GTK_CONTAINER (self), priv->layout);
741     gtk_box_pack_start (GTK_BOX (priv->layout), priv->label, TRUE, TRUE, 0);
742
743     gtk_window_set_accept_focus (GTK_WINDOW (self), FALSE);
744 }
745
746 /* Makes sure that icon/progress item contains the desired type
747    of item. If possible, tries to avoid creating a new widget but
748    reuses the existing one */
749 static void
750 hildon_banner_ensure_child                      (HildonBanner *self, 
751                                                  GtkWidget *user_widget,
752                                                  guint pos,
753                                                  GType type,
754                                                  const gchar *first_property, 
755                                                  ...)
756 {
757     GtkWidget *widget;
758     va_list args;
759     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
760
761     g_assert (priv);
762
763     widget = priv->main_item;
764     va_start (args, first_property);
765
766     /* Reuse existing widget if possible */
767     if (! user_widget && G_TYPE_CHECK_INSTANCE_TYPE (widget, type))
768     {
769         g_object_set_valist (G_OBJECT (widget), first_property, args);
770     }
771     else
772     {
773         /* We have to abandon old content widget */
774         if (widget)
775             gtk_container_remove (GTK_CONTAINER (priv->layout), widget);
776         
777         /* Use user provided widget or create a new one */
778         priv->main_item = widget = user_widget ? 
779             user_widget : GTK_WIDGET (g_object_new_valist(type, first_property, args));
780         gtk_box_pack_start (GTK_BOX (priv->layout), widget, TRUE, TRUE, 0);
781     }
782
783     /* We make sure that the widget exists in desired position. Different
784        banners place this child widget to different places */
785     gtk_box_reorder_child (GTK_BOX (priv->layout), widget, pos);
786     va_end (args);
787 }
788
789 /* Creates a new banner instance or uses an existing one */
790 static HildonBanner*
791 hildon_banner_get_instance_for_widget           (GtkWidget *widget, 
792                                                  gboolean timed)
793 {
794     GtkWidget *window;
795
796     window = widget ? gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW) : NULL;
797     return g_object_new (HILDON_TYPE_BANNER, "parent-window", window, "is-timed", timed, NULL);
798 }
799
800 /**
801  * hildon_banner_show_information:
802  * @widget: the #GtkWidget that is the owner of the banner
803  * @icon_name: the name of icon to use. Can be %NULL for default icon
804  * @text: Text to display
805  *
806  * This function creates and displays an information banner that
807  * automatically goes away after certain time period. For each window
808  * in your application there can only be one timed banner, so if you
809  * spawn a new banner before the earlier one has timed out, the
810  * previous one will be replaced.
811  *
812  * Returns: The newly created banner
813  *
814  */
815 GtkWidget*
816 hildon_banner_show_information                  (GtkWidget *widget, 
817                                                  const gchar *icon_name,
818                                                  const gchar *text)
819 {
820     HildonBanner *banner;
821
822     g_return_val_if_fail (icon_name == NULL || icon_name[0] != 0, NULL);
823     g_return_val_if_fail (text != NULL, NULL);
824
825     /* Prepare banner */
826     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
827     hildon_banner_ensure_child (banner, NULL, 0, GTK_TYPE_IMAGE, 
828             "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
829             "icon-name", icon_name ? icon_name : HILDON_BANNER_DEFAULT_ICON,
830             "yalign", 0.0, 
831             NULL);
832
833     hildon_banner_set_text (banner, text);
834     hildon_banner_bind_label_style (banner, NULL);
835
836     /* Show the banner, since caller cannot do that */
837     gtk_widget_show_all (GTK_WIDGET (banner));
838
839     return (GtkWidget *) banner;
840 }
841
842 /**
843  * hildon_banner_show_informationf:
844  * @widget: the #GtkWidget that is the owner of the banner
845  * @icon_name: the name of icon to use. Can be %NULL for default icon
846  * @format: a printf-like format string
847  * @Varargs: arguments for the format string
848  *
849  * A helper function for #hildon_banner_show_information with
850  * string formatting.
851  *
852  * Returns: the newly created banner
853  */
854 GtkWidget* 
855 hildon_banner_show_informationf                 (GtkWidget *widget, 
856                                                  const gchar *icon_name,
857                                                  const gchar *format, 
858                                                  ...)
859 {
860     g_return_val_if_fail (format != NULL, NULL);
861
862     gchar *message;
863     va_list args;
864     GtkWidget *banner;
865
866     va_start (args, format);
867     message = g_strdup_vprintf (format, args);
868     va_end (args);
869
870     banner = hildon_banner_show_information (widget, icon_name, message);
871
872     g_free (message);
873
874     return banner;
875 }
876
877 /**
878  * hildon_banner_show_information_with_markup:
879  * @widget: the #GtkWidget that wants to display banner
880  * @icon_name: the name of icon to use. Can be %NULL for default icon.
881  * @markup: a markup string to display (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
882  *
883  * This function creates and displays an information banner that
884  * automatically goes away after certain time period. For each window
885  * in your application there can only be one timed banner, so if you
886  * spawn a new banner before the earlier one has timed out, the
887  * previous one will be replaced.
888  *
889  * Returns: the newly created banner
890  *
891  */
892 GtkWidget*
893 hildon_banner_show_information_with_markup      (GtkWidget *widget, 
894                                                  const gchar *icon_name, 
895                                                  const gchar *markup)
896 {
897     HildonBanner *banner;
898
899     g_return_val_if_fail (icon_name == NULL || icon_name[0] != 0, NULL);
900     g_return_val_if_fail (markup != NULL, NULL);
901
902     /* Prepare banner */
903     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
904
905     hildon_banner_ensure_child (banner, NULL, 0, GTK_TYPE_IMAGE, 
906             "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
907             "icon-name", icon_name ? icon_name : HILDON_BANNER_DEFAULT_ICON,
908             "yalign", 0.0, 
909             NULL);
910
911     hildon_banner_set_markup (banner, markup);
912     hildon_banner_bind_label_style (banner, NULL);
913
914     /* Show the banner, since caller cannot do that */
915     gtk_widget_show_all (GTK_WIDGET (banner));
916
917     return (GtkWidget *) banner;
918 }
919
920 /**
921  * hildon_banner_show_animation:
922  * @widget: the #GtkWidget that wants to display banner
923  * @animation_name: The progress animation to use. You usually can just
924  *                  pass %NULL for the default animation.
925  * @text: the text to display.
926  *
927  * Shows an animated progress notification. It's recommended not to try
928  * to show more than one progress notification at a time, since
929  * they will appear on top of each other. You can use progress
930  * notifications with timed banners. In this case the banners are
931  * located so that you can somehow see both.
932  *
933  * Please note that banners are destroyed automatically once the
934  * window they are attached to is closed. The pointer that you
935  * receive with this function do not contain additional references,
936  * so it can become invalid without warning (this is true for
937  * all toplevel windows in gtk). To make sure that the banner do not disapear
938  * automatically, you can separately ref the return value (this
939  * doesn't prevent the banner from disappearing, but the object it just
940  * not finalized). In this case you have to call both #gtk_widget_destroy 
941  * followed by #g_object_unref (in this order).
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_animation                    (GtkWidget *widget, 
949                                                  const gchar *animation_name, 
950                                                  const gchar *text)
951 {
952     HildonBanner *banner;
953     GtkIconTheme *theme; 
954     GtkIconInfo *info;
955     GtkWidget *image_widget;
956     const gchar *filename;
957
958     g_return_val_if_fail (animation_name == NULL || animation_name[0] != 0, NULL);
959     g_return_val_if_fail (text != NULL, NULL);
960
961     /* Find out which animation to use */
962     theme = gtk_icon_theme_get_default ();
963     info = gtk_icon_theme_lookup_icon (theme, animation_name ?   /* FIXME: consider using: gtk_icon_theme_load_icon() */
964             animation_name : HILDON_BANNER_DEFAULT_PROGRESS_ANIMATION,
965             HILDON_ICON_SIZE_NOTE, 0);
966
967     /* Try to load animation. One could try to optimize this 
968        to avoid loading the default animation during each call */
969     if (info) {
970         filename = gtk_icon_info_get_filename (info);
971         image_widget = gtk_image_new_from_file (filename);
972         gtk_icon_info_free (info);
973     } else {
974         g_warning ("Icon theme lookup for icon failed!");
975         image_widget = NULL;
976     }
977
978     /* Prepare banner */
979     banner = hildon_banner_get_instance_for_widget (widget, FALSE);
980     hildon_banner_ensure_child (banner, image_widget, 0,
981             GTK_TYPE_IMAGE, "yalign", 0.0, NULL);
982
983     hildon_banner_set_text (banner, text);
984     hildon_banner_bind_label_style (banner, NULL);
985
986     /* And show it */
987     gtk_widget_show_all (GTK_WIDGET (banner));
988
989     return (GtkWidget *) banner;
990 }
991
992 /**
993  * hildon_banner_show_progress:
994  * @widget: the #GtkWidget that wants to display banner
995  * @bar: Progressbar to use. You usually can just pass %NULL, unless
996  *       you want somehow customized progress bar.
997  * @text: text to display.
998  *
999  * Shows progress notification. See #hildon_banner_show_animation
1000  * for more information.
1001  * 
1002  * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy
1003  *          once you are done with the banner.
1004  *
1005  */
1006 GtkWidget*
1007 hildon_banner_show_progress                     (GtkWidget *widget, 
1008                                                  GtkProgressBar *bar, 
1009                                                  const gchar *text)
1010 {
1011     HildonBanner *banner;
1012     HildonBannerPrivate *priv;
1013
1014     g_return_val_if_fail (bar == NULL || GTK_IS_PROGRESS_BAR (bar), NULL);
1015     g_return_val_if_fail (text != NULL, NULL);
1016
1017
1018     /* Prepare banner */
1019     banner = hildon_banner_get_instance_for_widget (widget, FALSE);
1020     priv = HILDON_BANNER_GET_PRIVATE (banner);
1021     g_assert (priv);
1022     hildon_banner_ensure_child (banner, (GtkWidget *) bar, -1, GTK_TYPE_PROGRESS_BAR, NULL);
1023
1024     gtk_widget_set_size_request (priv->main_item,
1025             HILDON_BANNER_PROGRESS_WIDTH, -1);
1026
1027     hildon_banner_set_text (banner, text);
1028     hildon_banner_bind_label_style (banner, NULL);
1029
1030     /* Show the banner */
1031     gtk_widget_show_all (GTK_WIDGET (banner));
1032
1033     return GTK_WIDGET (banner);   
1034 }
1035
1036 /**
1037  * hildon_banner_set_text:
1038  * @self: a #HildonBanner widget
1039  * @text: a new text to display in banner
1040  *
1041  * Sets the text that is displayed in the banner.
1042  *
1043  */
1044 void 
1045 hildon_banner_set_text                          (HildonBanner *self, 
1046                                                  const gchar *text)
1047 {
1048     GtkLabel *label;
1049     HildonBannerPrivate *priv;
1050     const gchar *existing_text;
1051
1052     g_return_if_fail (HILDON_IS_BANNER (self));
1053
1054     priv = HILDON_BANNER_GET_PRIVATE (self);
1055     g_assert (priv);
1056
1057     label = GTK_LABEL (priv->label);
1058     existing_text = gtk_label_get_text (label);
1059
1060     if (existing_text != NULL && 
1061         text != NULL          &&
1062         strcmp (existing_text, text) != 0)
1063             gtk_label_set_text (label, text);
1064
1065     hildon_banner_check_position (GTK_WIDGET (self));
1066 }
1067
1068 /**
1069  * hildon_banner_set_markup:
1070  * @self: a #HildonBanner widget
1071  * @markup: a new text with Pango markup to display in the banner
1072  *
1073  * Sets the text with markup that is displayed in the banner.
1074  *
1075  */
1076 void 
1077 hildon_banner_set_markup                        (HildonBanner *self, 
1078                                                  const gchar *markup)
1079 {
1080     GtkLabel *label;
1081     HildonBannerPrivate *priv;
1082
1083     g_return_if_fail (HILDON_IS_BANNER (self));
1084
1085     priv = HILDON_BANNER_GET_PRIVATE (self);
1086     g_assert (priv);
1087
1088     label = GTK_LABEL (priv->label);
1089     gtk_label_set_markup (label, markup);
1090
1091     hildon_banner_check_position (GTK_WIDGET(self));
1092 }
1093
1094 /**
1095  * hildon_banner_set_fraction:
1096  * @self: a #HildonBanner widget
1097  * @fraction: #gdouble
1098  *
1099  * The fraction is the completion of progressbar, 
1100  * the scale is from 0.0 to 1.0.
1101  * Sets the amount of fraction the progressbar has.
1102  *
1103  */
1104 void 
1105 hildon_banner_set_fraction                      (HildonBanner *self, 
1106                                                  gdouble fraction)
1107 {
1108     HildonBannerPrivate *priv;
1109
1110     g_return_if_fail (HILDON_IS_BANNER (self));
1111     priv = HILDON_BANNER_GET_PRIVATE (self);
1112     g_assert (priv);
1113
1114     g_return_if_fail (GTK_IS_PROGRESS_BAR (priv->main_item));
1115     gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->main_item), fraction);
1116 }
1117
1118 /**
1119  * hildon_banner_set_timeout:
1120  * @self: a #HildonBanner widget
1121  * @timeout: timeout to set in miliseconds.
1122  *
1123  * Sets the timeout on the banner. After the given amount of miliseconds
1124  * has elapsed the banner will go away. Note that settings this only makes
1125  * sense on the banners that are timed and that have not been yet displayed
1126  * on the screen.
1127  *
1128  */
1129 void
1130 hildon_banner_set_timeout                       (HildonBanner *self,
1131                                                  guint timeout)
1132 {
1133     HildonBannerPrivate *priv;
1134
1135     g_return_if_fail (HILDON_IS_BANNER (self));
1136     priv = HILDON_BANNER_GET_PRIVATE (self);
1137     g_assert (priv);
1138
1139     priv->timeout = timeout;
1140 }
1141
1142 /**
1143  * hildon_banner_set_icon:
1144  * @self: a #HildonBanner widget
1145  * @icon_name: the name of icon to use. Can be %NULL for default icon
1146  *
1147  * Sets the icon to be used in the banner.
1148  *
1149  */
1150 void 
1151 hildon_banner_set_icon                          (HildonBanner *self, 
1152                                                  const gchar  *icon_name)
1153 {
1154     HildonBannerPrivate *priv;
1155
1156     g_return_if_fail (HILDON_IS_BANNER (self));
1157     priv = HILDON_BANNER_GET_PRIVATE (self);
1158     g_assert (priv);
1159
1160     hildon_banner_ensure_child (self, NULL, 0, GTK_TYPE_IMAGE, 
1161             "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
1162             "icon-name", icon_name ? icon_name : HILDON_BANNER_DEFAULT_ICON,
1163             "yalign", 0.0, 
1164             NULL);
1165 }
1166
1167 /**
1168  * hildon_banner_set_icon_from_file:
1169  * @self: a #HildonBanner widget
1170  * @icon_file: the filename of icon to use. Can be %NULL for default icon
1171  *
1172  * Sets the icon from its filename to be used in the banner.
1173  *
1174  */
1175 void 
1176 hildon_banner_set_icon_from_file                (HildonBanner *self, 
1177                                                  const gchar  *icon_file)
1178 {
1179     HildonBannerPrivate *priv;
1180
1181     g_return_if_fail (HILDON_IS_BANNER (self));
1182     priv = HILDON_BANNER_GET_PRIVATE (self);
1183     g_assert (priv);
1184
1185     if (icon_file != NULL) {
1186         hildon_banner_ensure_child (self, NULL, 0, GTK_TYPE_IMAGE, 
1187                 "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
1188                 "file", icon_file,
1189                 "yalign", 0.0, 
1190                 NULL);
1191     } else {
1192         hildon_banner_ensure_child (self, NULL, 0, GTK_TYPE_IMAGE, 
1193                 "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
1194                 "icon-name", HILDON_BANNER_DEFAULT_ICON,
1195                 "yalign", 0.0, 
1196                 NULL);
1197     }
1198 }