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