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