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