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