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