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