cbfb2634dd1a396ed9b73c9580c31a65574e95af
[hildon] / hildon / 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: Rodrigo Novo <rodrigo.novo@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 is a small, pop-up window that can be used to display
30  * a short, timed notification or information to the user. It can
31  * communicate that a task has been finished or that the application
32  * state has changed.
33  *
34  * Hildon provides convenient funtions to create and show banners. To
35  * create and show information banners you can use
36  * hildon_banner_show_information(), hildon_banner_show_informationf()
37  * or hildon_banner_show_information_with_markup().
38  *
39  * If the application window has set the _HILDON_DO_NOT_DISTURB flag (using
40  * hildon_gtk_window_set_do_not_disturb() for example), the banner will not
41  * be shown. If you need to override this flag for important information,
42  * you can use the method hildon_banner_show_information_override_dnd().
43  * Please, take into account that this is only for important information.
44  *
45  *
46  * Two more kinds of banners are maintained for backward compatibility
47  * but are no longer recommended in Hildon 2.2. These are the animated
48  * banner (created with hildon_banner_show_animation()) and the
49  * progress banner (created with hildon_banner_show_progress()). See
50  * hildon_gtk_window_set_progress_indicator() for the preferred way of
51  * showing progress notifications in Hildon 2.2.
52  *
53  * Information banners are automatically destroyed after a certain
54  * period. This is stored in the #HildonBanner:timeout property (in
55  * miliseconds), and can be changed using hildon_banner_set_timeout().
56  *
57  * Note that #HildonBanner<!-- -->s should only be used to display
58  * non-critical pieces of information.
59  *
60  * <example>
61  * <title>Using the HildonBanner widget</title>
62  * <programlisting>
63  * void show_info_banner (GtkWidget *parent)
64  * {
65  *   GtkWidget *banner;
66  * <!-- -->
67  *   banner = hildon_banner_show_information (widget, NULL, "Information banner");
68  *   hildon_banner_set_timeout (HILDON_BANNER (banner), 9000);
69  * <!-- -->
70  *   return;
71  * }
72  * </programlisting>
73  * </example>
74  */
75
76 #ifdef                                          HAVE_CONFIG_H
77 #include                                        <config.h>
78 #endif
79
80 #include                                        <string.h>
81 #include                                        <X11/Xatom.h>
82 #include                                        <gdk/gdkx.h>
83
84 #undef                                          HILDON_DISABLE_DEPRECATED
85
86 #include                                        "hildon-banner.h"
87 #include                                        "hildon-private.h"
88 #include                                        "hildon-defines.h"
89 #include                                        "hildon-gtk.h"
90
91 /* max widths */
92
93 #define                                         HILDON_BANNER_LABEL_MAX_TIMED \
94                                                 (gdk_screen_width() - ((HILDON_MARGIN_TRIPLE) * 2))
95
96 #define                                         HILDON_BANNER_LABEL_MAX_PROGRESS 375 /*265*/
97
98 /* default timeout */
99
100 #define                                         HILDON_BANNER_DEFAULT_TIMEOUT 3000
101
102 /* default icons */
103
104 #define                                         HILDON_BANNER_DEFAULT_PROGRESS_ANIMATION "indicator_update"
105
106 /* animation related stuff */
107
108 #define                                         HILDON_BANNER_ANIMATION_FRAMERATE ((float)1000/150)
109
110 #define                                         HILDON_BANNER_ANIMATION_TMPL "indicator_update%d"
111
112 #define                                         HILDON_BANNER_ANIMATION_NFRAMES 8
113
114 enum 
115 {
116     PROP_0,
117     PROP_PARENT_WINDOW, 
118     PROP_IS_TIMED,
119     PROP_TIMEOUT
120 };
121
122 static GtkWidget*                               global_timed_banner = NULL;
123
124 static GQuark 
125 hildon_banner_timed_quark                       (void);
126
127 static void 
128 hildon_banner_bind_style                        (HildonBanner *self);
129
130 static gboolean 
131 hildon_banner_timeout                           (gpointer data);
132
133 static gboolean 
134 hildon_banner_clear_timeout                     (HildonBanner *self);
135
136 static void 
137 hildon_banner_ensure_timeout                    (HildonBanner *self);
138
139 static void 
140 hildon_banner_set_property                      (GObject *object,
141                                                  guint prop_id,
142                                                  const GValue *value,
143                                                  GParamSpec *pspec);
144     
145 static void 
146 hildon_banner_get_property                      (GObject *object,
147                                                  guint prop_id,
148                                                  GValue *value,
149                                                  GParamSpec *pspec);
150
151 static void
152 hildon_banner_destroy                           (GtkObject *object);
153         
154 static GObject*
155 hildon_banner_real_get_instance                 (GObject *window, 
156                                                  gboolean timed);
157
158 static GObject* 
159 hildon_banner_constructor                       (GType type,
160                                                  guint n_construct_params,
161                                                  GObjectConstructParam *construct_params);
162
163 static void
164 hildon_banner_finalize                          (GObject *object);
165
166 static gboolean
167 hildon_banner_button_press_event                (GtkWidget* widget,
168                                                  GdkEventButton* event);
169
170 static gboolean 
171 hildon_banner_map_event                         (GtkWidget *widget, 
172                                                  GdkEventAny *event);
173
174 static void 
175 force_to_wrap_truncated                         (HildonBanner *banner);
176
177 static void
178 hildon_banner_realize                           (GtkWidget *widget);
179
180 static void 
181 hildon_banner_class_init                        (HildonBannerClass *klass);
182
183 static void 
184 hildon_banner_init                              (HildonBanner *self);
185
186 static void
187 hildon_banner_ensure_child                      (HildonBanner *self, 
188                                                  GtkWidget *user_widget,
189                                                  guint pos,
190                                                  GType type,
191                                                  const gchar *first_property, 
192                                                  ...);
193
194 static HildonBanner*
195 hildon_banner_get_instance_for_widget           (GtkWidget *widget, 
196                                                  gboolean timed);
197
198 static void
199 hildon_banner_set_override_flag                 (HildonBanner *banner);
200
201 static void
202 reshow_banner                                   (HildonBanner *banner);
203
204 static GtkWidget*
205 hildon_banner_real_show_information             (GtkWidget *widget,
206                                                  const gchar *text,
207                                                  gboolean override_dnd);
208
209 G_DEFINE_TYPE (HildonBanner, hildon_banner, GTK_TYPE_WINDOW)
210
211 typedef struct                                  _HildonBannerPrivate HildonBannerPrivate;
212
213 #define                                         HILDON_BANNER_GET_PRIVATE(obj) \
214                                                 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
215                                                 HILDON_TYPE_BANNER, HildonBannerPrivate));
216
217 struct                                          _HildonBannerPrivate
218 {
219     GtkWidget   *main_item;
220     GtkWidget   *alignment;
221     GtkWidget   *label;
222     GtkWidget   *layout;
223     GtkWindow   *parent;
224     const gchar *name_suffix;
225     guint        timeout;
226     guint        timeout_id;
227     guint        is_timed             : 1;
228     guint        require_override_dnd : 1;
229     guint        overrides_dnd        : 1;
230 };
231
232 static GQuark 
233 hildon_banner_timed_quark                       (void)
234 {
235     static GQuark quark = 0;
236
237     if (G_UNLIKELY(quark == 0))
238         quark = g_quark_from_static_string ("hildon-banner-timed");
239
240     return quark;
241 }
242
243 /* Set the widget and label name to make the correct rc-style attached into them */
244 static void 
245 hildon_banner_bind_style                  (HildonBanner *self)
246 {
247     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
248     GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (self));
249     gboolean portrait = gdk_screen_get_width (screen) < gdk_screen_get_height (screen);
250     const gchar *portrait_suffix = portrait ? "-portrait" : NULL;
251     gchar *name;
252
253     g_assert (priv);
254
255     name = g_strconcat ("HildonBannerLabel-", priv->name_suffix, NULL);
256     gtk_widget_set_name (priv->label, name);
257     g_free (name);
258
259     name = g_strconcat ("HildonBanner-", priv->name_suffix, portrait_suffix, NULL);
260     gtk_widget_set_name (GTK_WIDGET (self), name);
261     g_free (name);
262 }
263
264 /* In timeout function we automatically destroy timed banners */
265 static gboolean
266 simulate_close (GtkWidget* widget)
267 {
268     gboolean result = FALSE;
269
270     /* If the banner is currently visible (it normally should), 
271        we simulate clicking the close button of the window.
272        This allows applications to reuse the banner by prevent
273        closing it etc */
274     if (GTK_WIDGET_DRAWABLE (widget))
275     {
276         GdkEvent *event = gdk_event_new (GDK_DELETE);
277         event->any.window = g_object_ref (widget->window);
278         event->any.send_event = FALSE;
279         result = gtk_widget_event (widget, event);
280         gdk_event_free (event);
281     }
282
283     return result;
284 }
285
286 static void
287 hildon_banner_size_request                      (GtkWidget      *self,
288                                                  GtkRequisition *req)
289 {
290     GTK_WIDGET_CLASS (hildon_banner_parent_class)->size_request (self, req);
291     req->width = gdk_screen_get_width (gtk_widget_get_screen (self));
292 }
293
294 static gboolean 
295 hildon_banner_timeout                           (gpointer data)
296 {
297     GtkWidget *widget;
298     gboolean continue_timeout = FALSE;
299
300     g_assert (HILDON_IS_BANNER (data));
301
302     widget = GTK_WIDGET (data);
303     g_object_ref (widget);
304
305     continue_timeout = simulate_close (widget);
306
307     if (! continue_timeout) {
308         HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (data);
309         priv->timeout_id = 0;
310         gtk_widget_destroy (widget);
311     }
312
313     g_object_unref (widget);
314
315     return continue_timeout;
316 }
317
318 static gboolean 
319 hildon_banner_clear_timeout                     (HildonBanner *self)
320 {
321     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
322     g_assert (priv);
323
324     if (priv->timeout_id != 0) {
325         g_source_remove (priv->timeout_id);
326         priv->timeout_id = 0;
327         return TRUE;
328     }
329
330     return FALSE;
331 }
332
333 static void 
334 hildon_banner_ensure_timeout                    (HildonBanner *self)
335 {
336     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
337     g_assert (priv);
338
339     if (priv->timeout_id == 0 && priv->is_timed && priv->timeout > 0)
340         priv->timeout_id = gdk_threads_add_timeout (priv->timeout,
341                 hildon_banner_timeout, self);
342 }
343
344 static void 
345 hildon_banner_set_property                      (GObject *object,
346                                                  guint prop_id,
347                                                  const GValue *value,
348                                                  GParamSpec *pspec)
349 {
350     GtkWidget *window;
351     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
352     g_assert (priv);
353
354     switch (prop_id) {
355
356         case PROP_TIMEOUT:
357              priv->timeout = g_value_get_uint (value);
358              break;
359  
360         case PROP_IS_TIMED:
361             priv->is_timed = g_value_get_boolean (value);
362             break;
363
364         case PROP_PARENT_WINDOW:
365             window = g_value_get_object (value);         
366             if (priv->parent) {
367                 g_object_remove_weak_pointer(G_OBJECT (priv->parent), (gpointer) &priv->parent);
368             }
369
370             gtk_window_set_transient_for (GTK_WINDOW (object), (GtkWindow *) window);
371             priv->parent = (GtkWindow *) window;
372
373             if (window) {
374                 gtk_window_set_destroy_with_parent (GTK_WINDOW (object), TRUE);
375                 g_object_add_weak_pointer(G_OBJECT (window), (gpointer) &priv->parent);
376             }
377
378             break;
379
380         default:
381             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
382             break;
383     }
384 }
385
386 static void 
387 hildon_banner_get_property                      (GObject *object,
388                                                  guint prop_id,
389                                                  GValue *value,
390                                                  GParamSpec *pspec)
391 {
392     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
393     g_assert (priv);
394
395     switch (prop_id)
396     {
397         case PROP_TIMEOUT:
398              g_value_set_uint (value, priv->timeout);
399              break;
400  
401         case PROP_IS_TIMED:
402             g_value_set_boolean (value, priv->is_timed);
403             break;
404
405         case PROP_PARENT_WINDOW:
406             g_value_set_object (value, gtk_window_get_transient_for (GTK_WINDOW (object)));
407             break;
408
409         default:
410             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
411             break;
412     }
413 }
414
415 static void
416 hildon_banner_destroy                           (GtkObject *object)
417 {
418     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
419     g_assert (priv);
420
421     HildonBanner *self;
422     GObject *parent_window = (GObject *) priv->parent;
423
424     g_assert (HILDON_IS_BANNER (object));
425     self = HILDON_BANNER (object);
426
427     /* Drop possible global pointer. That can hold reference to us */
428     if ((gpointer) object == (gpointer) global_timed_banner) {
429         global_timed_banner = NULL;
430         g_object_unref (object);
431     }
432
433     /* Remove the data from parent window for timed banners. Those hold reference */
434     if (priv->is_timed && parent_window != NULL) {
435         g_object_set_qdata (parent_window, hildon_banner_timed_quark (), NULL);
436     }
437
438     if (!priv->is_timed && priv->parent) {
439         hildon_gtk_window_set_progress_indicator (priv->parent, 0);
440     }
441
442     (void) hildon_banner_clear_timeout (self);
443
444     if (GTK_OBJECT_CLASS (hildon_banner_parent_class)->destroy)
445         GTK_OBJECT_CLASS (hildon_banner_parent_class)->destroy (object);
446 }
447
448 /* Search a previous banner instance */
449 static GObject*
450 hildon_banner_real_get_instance                 (GObject *window, 
451                                                  gboolean timed)
452 {
453     if (timed) {
454         /* If we have a parent window, the previous instance is stored there */
455         if (window) {
456             return g_object_get_qdata(window, hildon_banner_timed_quark ());
457         }
458
459         /* System notification instance is stored into global pointer */
460         return (GObject *) global_timed_banner;
461     }
462
463     /* Non-timed banners are normal (non-singleton) objects */
464     return NULL;
465 }
466
467 /* By overriding constructor we force timed banners to be
468    singletons for each window */
469 static GObject* 
470 hildon_banner_constructor                       (GType type,
471                                                  guint n_construct_params,
472                                                  GObjectConstructParam *construct_params)
473 {
474     GObject *banner, *window = NULL;
475     gboolean timed = FALSE;
476     guint i;
477
478     /* Search banner type information from parameters in order
479        to locate the possible previous banner instance. */
480     for (i = 0; i < n_construct_params; i++)
481     {
482         if (strcmp(construct_params[i].pspec->name, "parent-window") == 0)
483             window = g_value_get_object (construct_params[i].value);       
484         else if (strcmp(construct_params[i].pspec->name, "is-timed") == 0)
485             timed = g_value_get_boolean (construct_params[i].value);
486     }
487
488     /* Try to get a previous instance if such exists */
489     banner = hildon_banner_real_get_instance (window, timed);
490     if (! banner)
491     {
492         /* We have to create a new banner */
493         banner = G_OBJECT_CLASS (hildon_banner_parent_class)->constructor (type, n_construct_params, construct_params);
494
495         /* Store the newly created singleton instance either into parent 
496            window data or into global variables. */
497         if (timed) {
498             if (window) {
499                 g_object_set_qdata_full (G_OBJECT (window), hildon_banner_timed_quark (), 
500                         g_object_ref (banner), g_object_unref); 
501             } else {
502                 g_assert (global_timed_banner == NULL);
503                 global_timed_banner = g_object_ref (banner);
504             }
505         }
506     }
507     else {
508         /* FIXME: This is a hack! We have to manually freeze
509            notifications. This is normally done by g_object_init, but we
510            are not going to call that. g_object_newv will otherwise give
511            a critical like this:
512
513            GLIB CRITICAL ** GLib-GObject - g_object_notify_queue_thaw: 
514            assertion `nqueue->freeze_count > 0' failed */
515
516         g_object_freeze_notify (banner);
517     }
518
519     /* We restart possible timeouts for each new timed banner request */
520     if (timed && hildon_banner_clear_timeout (HILDON_BANNER (banner)))
521         hildon_banner_ensure_timeout (HILDON_BANNER(banner));
522
523     return banner;
524 }
525
526 static void
527 hildon_banner_finalize                          (GObject *object)
528 {
529     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
530
531     if (priv->parent) {
532         g_object_remove_weak_pointer(G_OBJECT (priv->parent), (gpointer) &priv->parent);
533     }
534
535     G_OBJECT_CLASS (hildon_banner_parent_class)->finalize (object);
536 }
537
538 static gboolean
539 hildon_banner_button_press_event                (GtkWidget* widget,
540                                                  GdkEventButton* event)
541 {
542     gboolean result = simulate_close (widget);
543
544     if (!result) {
545         /* signal emission not stopped - basically behave like
546          * gtk_main_do_event() for a delete event, but just hide the
547          * banner instead of destroying it, as it is already meant to
548          * be destroyed by hildon_banner_timeout() (if it's timed) or
549          * the application (if it's not). */
550         gtk_widget_hide (widget);
551     }
552
553     return result;
554 }
555
556 #if defined(MAEMO_GTK)
557 static void
558 hildon_banner_map                               (GtkWidget *widget)
559 {
560     if (GTK_WIDGET_CLASS (hildon_banner_parent_class)->map) {
561         /* Make the banner temporary _before_ mapping it, to avoid closing
562          * other temporary windows */
563         gtk_window_set_is_temporary (GTK_WINDOW (widget), TRUE);
564
565         GTK_WIDGET_CLASS (hildon_banner_parent_class)->map (widget);
566
567         /* Make the banner non-temporary _after_ mapping it, to avoid
568          * being closed by other non-temporary windows */
569         gtk_window_set_is_temporary (GTK_WINDOW (widget), FALSE);
570
571         gtk_window_move (GTK_WINDOW (widget), 0, HILDON_WINDOW_TITLEBAR_HEIGHT);
572     }
573 }
574 #endif
575
576 /* We start the timer for timed notifications after the window appears on screen */
577 static gboolean 
578 hildon_banner_map_event                         (GtkWidget *widget, 
579                                                  GdkEventAny *event)
580 {
581     gboolean result = FALSE;
582
583     if (GTK_WIDGET_CLASS (hildon_banner_parent_class)->map_event)
584         result = GTK_WIDGET_CLASS (hildon_banner_parent_class)->map_event (widget, event);
585
586     hildon_banner_ensure_timeout (HILDON_BANNER(widget));
587
588     return result;
589 }  
590
591 static void
592 banner_do_set_text                              (HildonBanner *banner,
593                                                  const gchar  *text,
594                                                  gboolean      is_markup)
595 {
596     HildonBannerPrivate *priv;
597     GtkRequisition req;
598
599     priv = HILDON_BANNER_GET_PRIVATE (banner);
600
601     if (is_markup) {
602         gtk_label_set_markup (GTK_LABEL (priv->label), text);
603     } else {
604         gtk_label_set_text (GTK_LABEL (priv->label), text);
605     }
606     gtk_widget_set_size_request (priv->label, -1, -1);
607     gtk_widget_size_request (priv->label, &req);
608
609     force_to_wrap_truncated (banner);
610 }
611
612 /* force to wrap truncated label by setting explicit size request
613  * see N#27000 and G#329646 */
614 static void 
615 force_to_wrap_truncated                         (HildonBanner *banner)
616 {
617     GtkLabel *label;
618     PangoLayout *layout;
619     int width_max;
620     int width = -1;
621     int height = -1;
622     PangoRectangle logical;
623     GtkRequisition requisition;
624     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (banner);
625
626     g_return_if_fail (priv);
627
628     label = GTK_LABEL (priv->label);
629
630     layout = gtk_label_get_layout (label);
631
632     pango_layout_get_extents (layout, NULL, &logical);
633     width = PANGO_PIXELS (logical.width);
634
635     width_max = priv->is_timed ? HILDON_BANNER_LABEL_MAX_TIMED
636         : HILDON_BANNER_LABEL_MAX_PROGRESS;
637
638     /* If the width of the label is going to exceed the maximum allowed
639      * width, enforce the maximum allowed width now.
640      */
641     if (width >= width_max || pango_layout_is_wrapped (layout)) {
642         width = width_max;
643     }
644
645     /* Make the label update its layout; and update our layout pointer
646      * because the layout will be cleared and refreshed.
647      */
648     gtk_widget_set_size_request (GTK_WIDGET (label), width, height);
649     gtk_widget_size_request (GTK_WIDGET (label), &requisition);
650
651     layout = gtk_label_get_layout (label);
652
653     /* If the layout has now been wrapped and exceeds 3 lines, we truncate
654      * the rest of the label according to spec.
655      */
656     if (pango_layout_is_wrapped (layout) && pango_layout_get_line_count (layout) > 3) {
657         int lines;
658
659         pango_layout_get_extents (layout, NULL, &logical);
660         lines = pango_layout_get_line_count (layout);
661
662         /* This calculation assumes that the same font is used
663          * throughout the banner -- this is usually the case on maemo
664          *
665          * FIXME: Pango >= 1.20 has pango_layout_set_height().
666          */
667         height = (PANGO_PIXELS (logical.height) * 3) / lines + 1;
668     }
669
670     /* Set the new width/height if applicable */
671     gtk_widget_set_size_request (GTK_WIDGET (label), width, height);
672 }
673
674 static void
675 screen_size_changed                            (GdkScreen *screen,
676                                                 GtkWindow *banner)
677
678 {
679     HildonBanner *hbanner = HILDON_BANNER (banner);
680     hildon_banner_bind_style (hbanner);
681     reshow_banner (hbanner);
682 }
683
684 static void
685 hildon_banner_realize                           (GtkWidget *widget)
686 {
687     GdkWindow *gdkwin;
688     GdkScreen *screen;
689     GdkAtom atom;
690     guint32 portrait = 1;
691     const gchar *notification_type = "_HILDON_NOTIFICATION_TYPE_BANNER";
692     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (widget);
693     g_assert (priv);
694
695     /* We let the parent to init widget->window before we need it */
696     if (GTK_WIDGET_CLASS (hildon_banner_parent_class)->realize)
697         GTK_WIDGET_CLASS (hildon_banner_parent_class)->realize (widget);
698
699     /* We use special hint to turn the banner into information notification. */
700     gdk_window_set_type_hint (widget->window, GDK_WINDOW_TYPE_HINT_NOTIFICATION);
701     gtk_window_set_transient_for (GTK_WINDOW (widget), (GtkWindow *) priv->parent);
702
703     gdkwin = widget->window;
704
705     /* Set the _HILDON_NOTIFICATION_TYPE property so Matchbox places the window correctly */
706     atom = gdk_atom_intern ("_HILDON_NOTIFICATION_TYPE", FALSE);
707     gdk_property_change (gdkwin, atom, gdk_x11_xatom_to_atom (XA_STRING), 8, GDK_PROP_MODE_REPLACE,
708                          (gpointer) notification_type, strlen (notification_type));
709
710     /* HildonBanner supports portrait mode */
711     atom = gdk_atom_intern ("_HILDON_PORTRAIT_MODE_SUPPORT", FALSE);
712     gdk_property_change (gdkwin, atom, gdk_x11_xatom_to_atom (XA_CARDINAL), 32,
713                          GDK_PROP_MODE_REPLACE, (gpointer) &portrait, 1);
714
715     /* Manage override flag */
716     if ((priv->require_override_dnd)&&(!priv->overrides_dnd)) {
717       hildon_banner_set_override_flag (HILDON_BANNER (widget));
718         priv->overrides_dnd = TRUE;
719     }
720
721     screen = gtk_widget_get_screen (widget);
722     g_signal_connect (screen, "size-changed", G_CALLBACK (screen_size_changed), widget);
723 }
724
725 static void
726 hildon_banner_unrealize                         (GtkWidget *widget)
727 {
728     GdkScreen *screen = gtk_widget_get_screen (widget);
729     g_signal_handlers_disconnect_by_func (screen, G_CALLBACK (screen_size_changed), widget);
730
731     GTK_WIDGET_CLASS (hildon_banner_parent_class)->unrealize (widget);
732 }
733
734 static void 
735 hildon_banner_class_init                        (HildonBannerClass *klass)
736 {
737     GObjectClass *object_class;
738     GtkWidgetClass *widget_class;
739
740     object_class = G_OBJECT_CLASS (klass);
741     widget_class = GTK_WIDGET_CLASS (klass);
742
743     /* Append private structure to class. This is more elegant than
744        on g_new based approach */
745     g_type_class_add_private (klass, sizeof (HildonBannerPrivate));
746
747     /* Override virtual methods */
748     object_class->constructor = hildon_banner_constructor;
749     object_class->finalize = hildon_banner_finalize;
750     object_class->set_property = hildon_banner_set_property;
751     object_class->get_property = hildon_banner_get_property;
752     GTK_OBJECT_CLASS (klass)->destroy = hildon_banner_destroy;
753     widget_class->size_request = hildon_banner_size_request;
754     widget_class->map_event = hildon_banner_map_event;
755     widget_class->realize = hildon_banner_realize;
756     widget_class->unrealize = hildon_banner_unrealize;
757     widget_class->button_press_event = hildon_banner_button_press_event;
758 #if defined(MAEMO_GTK)
759     widget_class->map = hildon_banner_map;
760 #endif
761
762     /* Install properties.
763        We need construct properties for singleton purposes */
764
765     /**
766      * HildonBanner:parent-window:
767      *
768      * The window for which the banner will be singleton. 
769      *                      
770      */
771     g_object_class_install_property (object_class, PROP_PARENT_WINDOW,
772             g_param_spec_object ("parent-window",
773                 "Parent window",
774                 "The window for which the banner will be singleton",
775                 GTK_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
776
777     /**
778      * HildonBanner:is-timed:
779      *
780      * Whether the banner is timed and goes away automatically.
781      *                      
782      */
783     g_object_class_install_property (object_class, PROP_IS_TIMED,
784             g_param_spec_boolean ("is-timed",
785                 "Is timed",
786                 "Whether or not the notification goes away automatically "
787                 "after the specified time has passed",
788                 FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
789
790     /**
791      * HildonBanner:timeout:
792      *
793      * The time before destroying the banner. This needs
794      * to be adjusted before the banner is mapped to the screen.
795      *                      
796      */
797     g_object_class_install_property (object_class, PROP_TIMEOUT,
798             g_param_spec_uint ("timeout",
799                 "Timeout",
800                 "The time before making the banner banner go away",
801                 0,
802                 10000,
803                 HILDON_BANNER_DEFAULT_TIMEOUT,
804                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
805 }
806
807 static void 
808 hildon_banner_init                              (HildonBanner *self)
809 {
810     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
811     g_assert (priv);
812
813     priv->parent = NULL;
814     priv->overrides_dnd = FALSE;
815     priv->require_override_dnd = FALSE;
816     priv->name_suffix = NULL;
817
818     /* Initialize the common layout inside banner */
819     priv->alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
820     priv->layout = gtk_hbox_new (FALSE, HILDON_MARGIN_DEFAULT);
821
822     priv->label = g_object_new (GTK_TYPE_LABEL, NULL);
823     gtk_label_set_line_wrap (GTK_LABEL (priv->label), TRUE);
824     gtk_label_set_line_wrap_mode (GTK_LABEL (priv->label), PANGO_WRAP_WORD_CHAR);
825     gtk_label_set_justify (GTK_LABEL (priv->label), GTK_JUSTIFY_CENTER);
826
827     gtk_container_set_border_width (GTK_CONTAINER (priv->layout), HILDON_MARGIN_DEFAULT);
828     gtk_container_add (GTK_CONTAINER (self), priv->alignment);
829     gtk_container_add (GTK_CONTAINER (priv->alignment), priv->layout);
830     gtk_box_pack_start (GTK_BOX (priv->layout), priv->label, FALSE, FALSE, 0);
831
832     gtk_window_set_accept_focus (GTK_WINDOW (self), FALSE);
833
834     gtk_widget_add_events (GTK_WIDGET (self), GDK_BUTTON_PRESS_MASK);
835 }
836
837 /* Makes sure that icon/progress item contains the desired type
838    of item. If possible, tries to avoid creating a new widget but
839    reuses the existing one */
840 static void
841 hildon_banner_ensure_child                      (HildonBanner *self, 
842                                                  GtkWidget *user_widget,
843                                                  guint pos,
844                                                  GType type,
845                                                  const gchar *first_property, 
846                                                  ...)
847 {
848     GtkWidget *widget;
849     va_list args;
850     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
851
852     g_assert (priv);
853
854     widget = priv->main_item;
855     va_start (args, first_property);
856
857     /* Reuse existing widget if possible */
858     if (! user_widget && G_TYPE_CHECK_INSTANCE_TYPE (widget, type))
859     {
860         g_object_set_valist (G_OBJECT (widget), first_property, args);
861     }
862     else
863     {
864         /* We have to abandon old content widget */
865         if (widget)
866             gtk_container_remove (GTK_CONTAINER (priv->layout), widget);
867         
868         /* Use user provided widget or create a new one */
869         priv->main_item = widget = user_widget ? 
870             user_widget : GTK_WIDGET (g_object_new_valist(type, first_property, args));
871         gtk_box_pack_start (GTK_BOX (priv->layout), widget, FALSE, FALSE, 0);
872     }
873
874     /* We make sure that the widget exists in desired position. Different
875        banners place this child widget to different places */
876     gtk_box_reorder_child (GTK_BOX (priv->layout), widget, pos);
877     va_end (args);
878 }
879
880 /* Creates a new banner instance or uses an existing one */
881 static HildonBanner*
882 hildon_banner_get_instance_for_widget           (GtkWidget *widget, 
883                                                  gboolean timed)
884 {
885     GtkWidget *window;
886
887     window = widget ? gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW) : NULL;
888     return g_object_new (HILDON_TYPE_BANNER, "parent-window", window, "is-timed", timed, NULL);
889 }
890
891 /**
892  * hildon_banner_show_information:
893  * @widget: the #GtkWidget that is the owner of the banner
894  * @icon_name: since Hildon 2.2 this parameter is not used anymore and
895  * any value that you pass will be ignored
896  * @text: Text to display
897  *
898  * This function creates and displays an information banner that is
899  * automatically destroyed after a certain time period (see
900  * hildon_banner_set_timeout()). For each window in your application
901  * there can only be one timed banner, so if you spawn a new banner
902  * before the earlier one has timed out, the previous one will be
903  * replaced.
904  *
905  * Returns: The newly created banner
906  *
907  */
908 GtkWidget*
909 hildon_banner_show_information                  (GtkWidget *widget, 
910                                                  const gchar *icon_name,
911                                                  const gchar *text)
912 {
913     return hildon_banner_real_show_information (widget, text, FALSE);
914 }
915
916 /**
917  * hildon_banner_show_information_override_dnd:
918  * @widget: the #GtkWidget that is the owner of the banner
919  * @text: Text to display
920  *
921  * Equivalent to hildon_banner_show_information(), but overriding the
922  * "do not disturb" flag.
923  *
924  * Returns: The newly created banner
925  *
926  * Since: 2.2
927  *
928  */
929 GtkWidget*
930 hildon_banner_show_information_override_dnd     (GtkWidget *widget,
931                                                  const gchar *text)
932 {
933     return hildon_banner_real_show_information (widget, text, TRUE);
934 }
935
936 static void
937 hildon_banner_set_override_flag                 (HildonBanner *banner)
938 {
939     guint32 state = 1;
940
941     gdk_property_change (GTK_WIDGET (banner)->window,
942                          gdk_atom_intern_static_string ("_HILDON_DO_NOT_DISTURB_OVERRIDE"),
943                          gdk_x11_xatom_to_atom (XA_INTEGER),
944                          32,
945                          GDK_PROP_MODE_REPLACE,
946                          (const guchar*) &state,
947                          1);
948 }
949
950 static void
951 reshow_banner                                   (HildonBanner *banner)
952 {
953     if (GTK_WIDGET_VISIBLE (banner)) {
954         gint width = gdk_screen_get_width (gtk_widget_get_screen (GTK_WIDGET (banner)));
955         gtk_window_resize (GTK_WINDOW (banner), width, 1);
956     }
957     force_to_wrap_truncated (banner);
958     gtk_widget_show_all (GTK_WIDGET (banner));
959 }
960
961 static GtkWidget*
962 hildon_banner_real_show_information             (GtkWidget *widget,
963                                                  const gchar *text,
964                                                  gboolean override_dnd)
965 {
966     HildonBanner *banner;
967     HildonBannerPrivate *priv = NULL;
968
969     g_return_val_if_fail (text != NULL, NULL);
970
971     /* Prepare banner */
972     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
973     priv = HILDON_BANNER_GET_PRIVATE (banner);
974
975     priv->name_suffix = "information";
976     banner_do_set_text (banner, text, FALSE);
977     hildon_banner_bind_style (banner);
978
979     if (override_dnd) {
980       /* so on the realize it will set the property */
981       priv->require_override_dnd = TRUE;
982     }
983
984     /* Show the banner, since caller cannot do that */
985     reshow_banner (banner);
986
987     return GTK_WIDGET (banner);
988 }
989
990 /**
991  * hildon_banner_show_informationf:
992  * @widget: the #GtkWidget that is the owner of the banner
993  * @icon_name: since Hildon 2.2 this parameter is not used anymore and
994  * any value that you pass will be ignored
995  * @format: a printf-like format string
996  * @Varargs: arguments for the format string
997  *
998  * A helper function for hildon_banner_show_information() with
999  * string formatting.
1000  *
1001  * Returns: the newly created banner
1002  */
1003 GtkWidget* 
1004 hildon_banner_show_informationf                 (GtkWidget *widget, 
1005                                                  const gchar *icon_name,
1006                                                  const gchar *format, 
1007                                                  ...)
1008 {
1009     g_return_val_if_fail (format != NULL, NULL);
1010
1011     gchar *message;
1012     va_list args;
1013     GtkWidget *banner;
1014
1015     va_start (args, format);
1016     message = g_strdup_vprintf (format, args);
1017     va_end (args);
1018
1019     banner = hildon_banner_show_information (widget, icon_name, message);
1020
1021     g_free (message);
1022
1023     return banner;
1024 }
1025
1026 /**
1027  * hildon_banner_show_information_with_markup:
1028  * @widget: the #GtkWidget that wants to display banner
1029  * @icon_name: since Hildon 2.2 this parameter is not used anymore and
1030  * any value that you pass will be ignored
1031  * @markup: a markup string to display (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
1032  *
1033  * This function creates and displays an information banner that is
1034  * automatically destroyed after certain time period (see
1035  * hildon_banner_set_timeout()). For each window in your application
1036  * there can only be one timed banner, so if you spawn a new banner
1037  * before the earlier one has timed out, the previous one will be
1038  * replaced.
1039  *
1040  * Returns: the newly created banner
1041  *
1042  */
1043 GtkWidget*
1044 hildon_banner_show_information_with_markup      (GtkWidget *widget, 
1045                                                  const gchar *icon_name, 
1046                                                  const gchar *markup)
1047 {
1048     HildonBanner *banner;
1049     HildonBannerPrivate *priv;
1050
1051     g_return_val_if_fail (icon_name == NULL || icon_name[0] != 0, NULL);
1052     g_return_val_if_fail (markup != NULL, NULL);
1053
1054     /* Prepare banner */
1055     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
1056     priv = HILDON_BANNER_GET_PRIVATE (banner);
1057
1058     priv->name_suffix = "information";
1059     banner_do_set_text (banner, markup, TRUE);
1060     hildon_banner_bind_style (banner);
1061
1062     /* Show the banner, since caller cannot do that */
1063     reshow_banner (banner);
1064
1065     return (GtkWidget *) banner;
1066 }
1067
1068 /**
1069  * hildon_banner_show_animation:
1070  * @widget: the #GtkWidget that wants to display banner
1071  * @animation_name: since Hildon 2.2 this parameter is not used
1072  *                  anymore and any value that you pass will be
1073  *                  ignored
1074  * @text: the text to display.
1075  *
1076  * Shows an animated progress notification. It's recommended not to try
1077  * to show more than one progress notification at a time, since
1078  * they will appear on top of each other. You can use progress
1079  * notifications with timed banners. In this case the banners are
1080  * located so that you can somehow see both.
1081  *
1082  * Unlike information banners (created with
1083  * hildon_banner_show_information()), animation banners are not
1084  * destroyed automatically using a timeout. You have to destroy them
1085  * yourself.
1086  *
1087  * Please note also that these banners are destroyed automatically if the
1088  * window they are attached to is closed. The pointer that you receive
1089  * with this function does not contain additional references, so it
1090  * can become invalid without warning (this is true for all toplevel
1091  * windows in gtk). To make sure that the banner does not disappear
1092  * automatically, you can separately ref the return value (this
1093  * doesn't prevent the banner from disappearing, just the object from
1094  * being finalized). In this case you have to call
1095  * gtk_widget_destroy() followed by g_object_unref().
1096  *
1097  * Returns: a #HildonBanner widget. You must call gtk_widget_destroy()
1098  *          once you are done with the banner.
1099  *
1100  * Deprecated: Hildon 2.2: use
1101  * hildon_gtk_window_set_progress_indicator() instead.
1102  */
1103 GtkWidget*
1104 hildon_banner_show_animation                    (GtkWidget *widget, 
1105                                                  const gchar *animation_name, 
1106                                                  const gchar *text)
1107 {
1108     HildonBanner *banner;
1109     GtkWidget *image_widget;
1110     HildonBannerPrivate *priv;
1111
1112     g_return_val_if_fail (text != NULL, NULL);
1113
1114     image_widget = hildon_private_create_animation (
1115         HILDON_BANNER_ANIMATION_FRAMERATE,
1116         HILDON_BANNER_ANIMATION_TMPL,
1117         HILDON_BANNER_ANIMATION_NFRAMES);
1118
1119     /* Prepare banner */
1120     banner = hildon_banner_get_instance_for_widget (widget, FALSE);
1121     hildon_banner_ensure_child (banner, image_widget, 0,
1122             GTK_TYPE_IMAGE, "yalign", 0.0, NULL);
1123
1124     priv = HILDON_BANNER_GET_PRIVATE (banner);
1125     priv->name_suffix = "animation";
1126     banner_do_set_text (banner, text, FALSE);
1127     hildon_banner_bind_style (banner);
1128
1129     /* And show it */
1130     reshow_banner (banner);
1131
1132     return (GtkWidget *) banner;
1133 }
1134
1135 /**
1136  * hildon_banner_show_progress:
1137  * @widget: the #GtkWidget that wants to display banner
1138  * @bar: since Hildon 2.2 this parameter is not used anymore and
1139  * any value that you pass will be ignored
1140  * @text: text to display.
1141  *
1142  * Shows progress notification. See hildon_banner_show_animation()
1143  * for more information.
1144  * 
1145  * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy
1146  *          once you are done with the banner.
1147  *
1148  * Deprecated: Hildon 2.2: use hildon_gtk_window_set_progress_indicator() instead.
1149  */
1150 GtkWidget*
1151 hildon_banner_show_progress                     (GtkWidget *widget, 
1152                                                  GtkProgressBar *bar, 
1153                                                  const gchar *text)
1154 {
1155     HildonBanner *banner;
1156     HildonBannerPrivate *priv;
1157
1158     g_return_val_if_fail (text != NULL, NULL);
1159
1160     /* Prepare banner */
1161     banner = hildon_banner_get_instance_for_widget (widget, FALSE);
1162     priv = HILDON_BANNER_GET_PRIVATE (banner);
1163     g_assert (priv);
1164
1165     priv->name_suffix = "progress";
1166     banner_do_set_text (banner, text, FALSE);
1167     hildon_banner_bind_style (banner);
1168
1169     if (priv->parent)
1170         hildon_gtk_window_set_progress_indicator (priv->parent, 1);
1171
1172     /* Show the banner */
1173     reshow_banner (banner);
1174
1175     return GTK_WIDGET (banner);   
1176 }
1177
1178 /**
1179  * hildon_banner_set_text:
1180  * @self: a #HildonBanner widget
1181  * @text: a new text to display in banner
1182  *
1183  * Sets the text that is displayed in the banner.
1184  *
1185  */
1186 void 
1187 hildon_banner_set_text                          (HildonBanner *self, 
1188                                                  const gchar *text)
1189 {
1190     g_return_if_fail (HILDON_IS_BANNER (self));
1191
1192     banner_do_set_text (self, text, FALSE);
1193
1194     if (GTK_WIDGET_VISIBLE (self))
1195         reshow_banner (self);
1196 }
1197
1198 /**
1199  * hildon_banner_set_markup:
1200  * @self: a #HildonBanner widget
1201  * @markup: a new text with Pango markup to display in the banner
1202  *
1203  * Sets the text with markup that is displayed in the banner.
1204  *
1205  */
1206 void 
1207 hildon_banner_set_markup                        (HildonBanner *self, 
1208                                                  const gchar *markup)
1209 {
1210     g_return_if_fail (HILDON_IS_BANNER (self));
1211
1212     banner_do_set_text (self, markup, TRUE);
1213
1214     if (GTK_WIDGET_VISIBLE (self))
1215         reshow_banner (self);
1216 }
1217
1218 /**
1219  * hildon_banner_set_fraction:
1220  * @self: a #HildonBanner widget
1221  * @fraction: #gdouble
1222  *
1223  * The fraction is the completion of progressbar, 
1224  * the scale is from 0.0 to 1.0.
1225  * Sets the amount of fraction the progressbar has.
1226  *
1227  * Note that this method only has effect if @self was created with
1228  * hildon_banner_show_progress()
1229  *
1230  * Deprecated: This function does nothing. As of Hildon 2.2, hildon
1231  * banners don't have progress bars.
1232  */
1233 void 
1234 hildon_banner_set_fraction                      (HildonBanner *self, 
1235                                                  gdouble fraction)
1236 {
1237 }
1238
1239 /**
1240  * hildon_banner_set_timeout:
1241  * @self: a #HildonBanner widget
1242  * @timeout: timeout to set in miliseconds.
1243  *
1244  * Sets the timeout on the banner. After the given amount of miliseconds
1245  * has elapsed the banner will be destroyed. Setting this only makes
1246  * sense on banners that are timed and that have not been yet displayed
1247  * on the screen.
1248  *
1249  * Note that this method only has effect if @self is an information
1250  * banner (created using hildon_banner_show_information() and
1251  * friends).
1252  */
1253 void
1254 hildon_banner_set_timeout                       (HildonBanner *self,
1255                                                  guint timeout)
1256 {
1257     HildonBannerPrivate *priv;
1258
1259     g_return_if_fail (HILDON_IS_BANNER (self));
1260     priv = HILDON_BANNER_GET_PRIVATE (self);
1261     g_assert (priv);
1262
1263     priv->timeout = timeout;
1264 }
1265
1266 /**
1267  * hildon_banner_set_icon:
1268  * @self: a #HildonBanner widget
1269  * @icon_name: the name of icon to use. Can be %NULL for default icon
1270  *
1271  * Sets the icon to be used in the banner.
1272  *
1273  * Deprecated: This function does nothing. As of hildon 2.2, hildon
1274  * banners don't allow changing their icons.
1275  */
1276 void 
1277 hildon_banner_set_icon                          (HildonBanner *self, 
1278                                                  const gchar  *icon_name)
1279 {
1280 }
1281
1282 /**
1283  * hildon_banner_set_icon_from_file:
1284  * @self: a #HildonBanner widget
1285  * @icon_file: the filename of icon to use. Can be %NULL for default icon
1286  *
1287  * Sets the icon from its filename to be used in the banner.
1288  *
1289  * Deprecated: This function does nothing. As of hildon 2.2, hildon
1290  * banners don't allow changing their icons.
1291  */
1292 void 
1293 hildon_banner_set_icon_from_file                (HildonBanner *self, 
1294                                                  const gchar  *icon_file)
1295 {
1296 }