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