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