af0bd8df275330a943853ee799e14df21b97e001
[hildon] / src / hildon-banner.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@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.
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 #ifdef                                          HAVE_CONFIG_H
26 #include                                        <config.h>
27 #endif
28
29 #include                                        "hildon-banner.h"
30 #include                                        <gtk/gtkhbox.h>
31 #include                                        <gtk/gtkimage.h>
32 #include                                        <gtk/gtkicontheme.h>
33 #include                                        <string.h>
34 #include                                        <X11/X.h>
35 #include                                        <X11/Xatom.h>
36 #include                                        "hildon-defines.h"
37 #include                                        "hildon-banner-private.h"
38
39 /* position relative to the screen */
40
41 #define                                         HILDON_BANNER_WINDOW_X 30
42
43 #define                                         HILDON_BANNER_WINDOW_Y 73
44
45 #define                                         HILDON_BANNER_WINDOW_FULLSCREEN_Y 20
46
47 /* max widths */
48
49 #define                                         HILDON_BANNER_PROGRESS_WIDTH 104
50
51 #define                                         HILDON_BANNER_LABEL_MAX_TIMED 375
52
53 #define                                         HILDON_BANNER_LABEL_MAX_PROGRESS 375 /*265*/
54
55 /* default timeout */
56
57 #define                                         HILDON_BANNER_TIMEOUT 3000
58
59 /* default icons */
60
61 #define                                         HILDON_BANNER_DEFAULT_ICON "qgn_note_infoprint"
62
63 #define                                         HILDON_BANNER_DEFAULT_PROGRESS_ANIMATION "qgn_indi_pball_a"
64
65 enum 
66 {
67     PROP_0,
68     PROP_PARENT_WINDOW, 
69     PROP_IS_TIMED
70 };
71
72 static GtkWidget*                               global_timed_banner = NULL;
73
74 static Window 
75 get_current_app_window                          (void);
76
77 static gboolean 
78 check_fullscreen_state                          (Window window);
79
80 static GQuark 
81 hildon_banner_timed_quark                       (void);
82
83 static void 
84 hildon_banner_bind_label_style                  (HildonBanner *self,
85                                                  const gchar *name);
86
87 static gboolean 
88 hildon_banner_timeout                           (gpointer data);
89
90 static gboolean 
91 hildon_banner_clear_timeout                     (HildonBanner *self);
92
93 static void 
94 hildon_banner_ensure_timeout                    (HildonBanner *self);
95
96 static void 
97 hildon_banner_set_property                      (GObject *object,
98                                                  guint prop_id,
99                                                  const GValue *value,
100                                                  GParamSpec *pspec);
101     
102 static void 
103 hildon_banner_get_property                      (GObject *object,
104                                                  guint prop_id,
105                                                  GValue *value,
106                                                  GParamSpec *pspec);
107
108 static void
109 hildon_banner_destroy                           (GtkObject *object);
110         
111 static GObject*
112 hildon_banner_real_get_instance                 (GObject *window, 
113                                                  gboolean timed);
114
115 static GObject* 
116 hildon_banner_constructor                       (GType type,
117                                                  guint n_construct_params,
118                                                  GObjectConstructParam *construct_params);
119
120 static gboolean 
121 hildon_banner_map_event                         (GtkWidget *widget, 
122                                                  GdkEventAny *event);
123
124 static void 
125 force_to_wrap_truncated                         (HildonBanner *banner);
126
127 static void
128 hildon_banner_check_position                    (GtkWidget *widget);
129
130 static void
131 hildon_banner_realize                           (GtkWidget *widget);
132
133 static void 
134 hildon_banner_class_init                        (HildonBannerClass *klass);
135
136 static void 
137 hildon_banner_init                              (HildonBanner *self);
138
139 static void
140 hildon_banner_ensure_child                      (HildonBanner *self, 
141                                                  GtkWidget *user_widget,
142                                                  guint pos,
143                                                  GType type,
144                                                  const gchar *first_property, 
145                                                  ...);
146
147 static HildonBanner*
148 hildon_banner_get_instance_for_widget           (GtkWidget *widget, 
149                                                  gboolean timed);
150
151 static GtkWindowClass*                          parent_class = NULL;
152
153 /**
154  * hildon_banner_get_type:
155  *
156  * Initialises, and returns the type of a hildon banner.
157  *
158  * @Returns: GType of #HildonBanner
159  */
160 GType G_GNUC_CONST 
161 hildon_banner_get_type                          (void)
162 {
163     static GType banner_type = 0;
164
165     if (! banner_type)
166     {
167         static const GTypeInfo banner_info = {
168             sizeof (HildonBannerClass),
169             NULL,       /* base_init */
170             NULL,       /* base_finalize */
171             (GClassInitFunc) hildon_banner_class_init,
172             NULL,       /* class_finalize */
173             NULL,       /* class_data */
174             sizeof (HildonBanner),
175             0,  /* n_preallocs */
176             (GInstanceInitFunc) hildon_banner_init,
177         };
178         banner_type = g_type_register_static (GTK_TYPE_WINDOW,
179                 "HildonBanner", &banner_info, 0 );
180     }
181     return banner_type;
182 }
183
184 /* copy/paste from old infoprint implementation: Use matchbox 
185    properties to find the topmost application window */
186 static Window 
187 get_current_app_window                          (void)
188 {
189     unsigned long n;
190     unsigned long extra;
191     int format;
192     int status;
193
194     Atom atom_current_app_window = gdk_x11_get_xatom_by_name ("_MB_CURRENT_APP_WINDOW");
195     Atom realType;
196     Window win_result = None;
197     guchar *data_return = NULL;
198
199     status = XGetWindowProperty (GDK_DISPLAY(), GDK_ROOT_WINDOW (), 
200             atom_current_app_window, 0L, 16L,
201             0, XA_WINDOW, &realType, &format,
202             &n, &extra, 
203             &data_return);
204
205     if (status == Success && realType == XA_WINDOW && format == 32 && n == 1 && data_return != NULL)
206     {
207         win_result = ((Window*) data_return)[0];
208     } 
209
210     if (data_return) 
211         XFree (data_return);    
212
213     return win_result;
214 }
215
216 /* Checks if a window is in fullscreen state or not. This
217    information is needed when banners are positioned on screen.
218    copy/paste from old infoprint implementation.  */
219 static gboolean 
220 check_fullscreen_state                          (Window window)
221 {
222     unsigned long n;
223     unsigned long extra;
224     int format, status, i; 
225     guchar *data_return = NULL;
226
227     Atom realType;
228     Atom atom_window_state = gdk_x11_get_xatom_by_name ("_NET_WM_STATE");
229     Atom atom_fullscreen   = gdk_x11_get_xatom_by_name ("_NET_WM_STATE_FULLSCREEN");
230
231     if (window == None)
232         return FALSE;
233
234     /* in some cases XGetWindowProperty seems to generate BadWindow,
235        so at the moment this function does not always work perfectly */
236     gdk_error_trap_push ();
237     status = XGetWindowProperty (GDK_DISPLAY (), window,
238             atom_window_state, 0L, 1000000L,
239             0, XA_ATOM, &realType, &format,
240             &n, &extra, &data_return);
241
242     gdk_flush ();
243
244     if (gdk_error_trap_pop ())
245         return FALSE;
246
247     if (status == Success && realType == XA_ATOM && format == 32 && n > 0)
248     {
249         for (i=0; i < n; i++)
250             if  (((Atom*)data_return)[i] && ((Atom*)data_return)[i] == atom_fullscreen)
251             {
252                 if (data_return) XFree (data_return);
253                 return TRUE;
254             }
255     }
256
257     if (data_return) 
258         XFree (data_return);
259
260     return FALSE;
261 }
262
263 static GQuark 
264 hildon_banner_timed_quark                       (void)
265 {
266     static GQuark quark = 0;
267
268     if (G_UNLIKELY(quark == 0))
269         quark = g_quark_from_static_string ("hildon-banner-timed");
270
271     return quark;
272 }
273
274 /* Set the label name to make the correct rc-style attached into it */
275 static void 
276 hildon_banner_bind_label_style                  (HildonBanner *self,
277                                                  const gchar *name)
278 {
279     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
280     g_assert (priv);
281
282     GtkWidget *label = priv->label;
283
284     /* Too bad that we cannot really reset the widget name */
285     gtk_widget_set_name (label, name ? name : g_type_name (GTK_WIDGET_TYPE (label)));
286 }
287
288 /* In timeout function we automatically destroy timed banners */
289 static gboolean 
290 hildon_banner_timeout                           (gpointer data)
291 {
292     GtkWidget *widget;
293     GdkEvent *event;
294     gboolean continue_timeout = FALSE;
295
296     GDK_THREADS_ENTER ();
297
298     g_assert (HILDON_IS_BANNER (data));
299
300     widget = GTK_WIDGET (data);
301     g_object_ref (widget);
302
303     /* If the banner is currently visible (it normally should), 
304        we simulate clicking the close button of the window.
305        This allows applications to reuse the banner by prevent
306        closing it etc */
307     if (GTK_WIDGET_DRAWABLE (widget))
308     {
309         event = gdk_event_new (GDK_DELETE);
310         event->any.window = g_object_ref (widget->window);
311         event->any.send_event = FALSE;
312         continue_timeout = gtk_widget_event (widget, event);
313         gdk_event_free (event);
314     }
315
316     if (! continue_timeout)
317         gtk_widget_destroy (widget);
318
319     g_object_unref (widget);
320
321     GDK_THREADS_LEAVE ();
322
323     return continue_timeout;
324 }
325
326 static gboolean 
327 hildon_banner_clear_timeout                     (HildonBanner *self)
328 {
329     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
330     g_assert (priv);
331
332     if (priv->timeout_id != 0) {
333         g_source_remove (priv->timeout_id);
334         priv->timeout_id = 0;
335         return TRUE;
336     }
337
338     return FALSE;
339 }
340
341 static void 
342 hildon_banner_ensure_timeout                    (HildonBanner *self)
343 {
344     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
345     g_assert (priv);
346
347     if (priv->timeout_id == 0 && priv->is_timed)
348         priv->timeout_id = g_timeout_add (HILDON_BANNER_TIMEOUT, 
349                 hildon_banner_timeout, self);
350 }
351
352 static void 
353 hildon_banner_set_property                      (GObject *object,
354                                                  guint prop_id,
355                                                  const GValue *value,
356                                                  GParamSpec *pspec)
357 {
358     GtkWidget *window;
359     GdkGeometry geom;
360     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
361     g_assert (priv);
362
363     switch (prop_id) {
364
365         case PROP_IS_TIMED:
366             priv->is_timed = g_value_get_boolean (value);
367
368             /* Timed and progress notifications have different
369                pixel size values for text. 
370                We force to use requisition size for timed banners 
371                in order to avoid resize problems when reusing the
372                window (see bug #24339) */
373             geom.max_width = priv->is_timed ? -1
374                 : HILDON_BANNER_LABEL_MAX_PROGRESS;
375             geom.max_height = -1;
376             gtk_window_set_geometry_hints (GTK_WINDOW (object), 
377                     priv->label, &geom, GDK_HINT_MAX_SIZE);
378             break;
379
380         case PROP_PARENT_WINDOW:
381             window = g_value_get_object (value);         
382
383             gtk_window_set_transient_for (GTK_WINDOW (object), (GtkWindow *) window);
384
385             if (window)
386                 gtk_window_set_destroy_with_parent (GTK_WINDOW (object), TRUE);
387
388             break;
389
390         default:
391             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
392             break;
393     }
394 }
395
396 static void 
397 hildon_banner_get_property                      (GObject *object,
398                                                  guint prop_id,
399                                                  GValue *value,
400                                                  GParamSpec *pspec)
401 {
402     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
403     g_assert (priv);
404
405     switch (prop_id)
406     {
407         case PROP_IS_TIMED:
408             g_value_set_boolean (value, priv->is_timed);
409             break;
410
411         case PROP_PARENT_WINDOW:
412             g_value_set_object (value, gtk_window_get_transient_for (GTK_WINDOW (object)));
413             break;
414
415         default:
416             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417             break;
418     }
419 }
420
421 static void
422 hildon_banner_destroy                           (GtkObject *object)
423 {
424     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
425     g_assert (priv);
426
427     HildonBanner *self;
428     GObject *parent_window;
429
430     g_assert (HILDON_IS_BANNER (object));
431     self = HILDON_BANNER (object);
432
433     /* Drop possible global pointer. That can hold reference to us */
434     if ((gpointer) object == (gpointer) global_timed_banner) {
435         global_timed_banner = NULL;
436         g_object_unref (object);
437     }
438
439     /* Remove the data from parent window for timed banners. Those hold reference */
440     if (priv->is_timed && (parent_window = (GObject *) gtk_window_get_transient_for (GTK_WINDOW (object))) != NULL)
441         g_object_set_qdata (parent_window, hildon_banner_timed_quark (), NULL);
442
443     (void) hildon_banner_clear_timeout (self);
444
445     if (GTK_OBJECT_CLASS (parent_class)->destroy)
446         GTK_OBJECT_CLASS (parent_class)->destroy (object);
447 }
448
449 /* Search a previous banner instance */
450 static GObject*
451 hildon_banner_real_get_instance                 (GObject *window, 
452                                                  gboolean timed)
453 {
454     g_assert (GTK_IS_WINDOW (window));
455
456     if (timed) {
457         /* If we have a parent window, the previous instance is stored there */
458         if (window) 
459             return g_object_get_qdata(window, hildon_banner_timed_quark ());
460
461         /* System notification instance is stored into global pointer */
462         return (GObject *) global_timed_banner;
463     }
464
465     /* Non-timed banners are normal (non-singleton) objects */
466     return NULL;
467 }
468
469 /* By overriding constructor we force timed banners to be
470    singletons for each window */
471 static GObject* 
472 hildon_banner_constructor                       (GType type,
473                                                  guint n_construct_params,
474                                                  GObjectConstructParam *construct_params)
475 {
476     GObject *banner, *window = NULL;
477     gboolean timed = FALSE;
478     guint i;
479
480     /* Search banner type information from parameters in order
481        to locate the possible previous banner instance. */
482     for (i = 0; i < n_construct_params; i++)
483     {
484         if (strcmp(construct_params[i].pspec->name, "parent-window") == 0)
485             window = g_value_get_object (construct_params[i].value);       
486         else if (strcmp(construct_params[i].pspec->name, "is-timed") == 0)
487             timed = g_value_get_boolean (construct_params[i].value);
488     }
489
490     /* Try to get a previous instance if such exists */
491     banner = hildon_banner_real_get_instance (window, timed);
492     if (! banner)
493     {
494         /* We have to create a new banner */
495         banner = G_OBJECT_CLASS (parent_class)->constructor (type, n_construct_params, construct_params);
496
497         /* Store the newly created singleton instance either into parent 
498            window data or into global variables. */
499         if (timed) {
500             if (window) {
501                 g_object_set_qdata_full (G_OBJECT (window), hildon_banner_timed_quark (), 
502                         g_object_ref (banner), g_object_unref); 
503             } else {
504                 g_assert (global_timed_banner == NULL);
505                 global_timed_banner = g_object_ref (banner);
506             }
507         }
508     }
509     else {
510         /* FIXME: This is a hack! We have to manually freeze
511            notifications. This is normally done by g_object_init, but we
512            are not going to call that. g_object_newv will otherwise give
513            a critical like this:
514
515            GLIB CRITICAL ** GLib-GObject - g_object_notify_queue_thaw: 
516            assertion `nqueue->freeze_count > 0' failed */
517
518         g_object_freeze_notify (banner);
519     }
520
521     /* We restart possible timeouts for each new timed banner request */
522     if (timed && hildon_banner_clear_timeout (HILDON_BANNER (banner)))
523         hildon_banner_ensure_timeout (HILDON_BANNER(banner));
524
525     return banner;
526 }
527
528 /* We start the timer for timed notifications after the window appears on screen */
529 static gboolean 
530 hildon_banner_map_event                         (GtkWidget *widget, 
531                                                  GdkEventAny *event)
532 {
533     gboolean result = FALSE;
534
535     if (GTK_WIDGET_CLASS (parent_class)->map_event)
536         result = GTK_WIDGET_CLASS (parent_class)->map_event (widget, event);
537
538     hildon_banner_ensure_timeout (HILDON_BANNER(widget));
539
540     return result;
541 }  
542
543
544 /* force to wrap truncated label by setting explicit size request
545  * see N#27000 and G#329646 */
546 static void 
547 force_to_wrap_truncated                         (HildonBanner *banner)
548 {
549     GtkLabel *label;
550     PangoLayout *layout;
551     int width_text, width_max;
552     int width = -1;
553     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (banner);
554
555     g_assert (priv);
556     label = GTK_LABEL (priv->label);
557
558     layout = gtk_label_get_layout (label);
559     width_text  = PANGO_PIXELS(pango_layout_get_width (layout));
560     /* = width to which the lines of the PangoLayout should be wrapped */
561
562     width_max = priv->is_timed ? HILDON_BANNER_LABEL_MAX_TIMED
563         : HILDON_BANNER_LABEL_MAX_PROGRESS;
564
565     if (width_text >= width_max) {
566         /* explicitly request maximum size to force wrapping */
567         PangoRectangle logical;
568
569         pango_layout_set_width (layout, width_max * PANGO_SCALE);
570         pango_layout_get_extents (layout, NULL, &logical);
571
572         width = PANGO_PIXELS (logical.width);
573     }
574
575     /* use fixed width when wrapping or natural one otherwise */
576     gtk_widget_set_size_request (GTK_WIDGET (label), width, -1);
577 }
578
579
580 static void
581 hildon_banner_check_position                    (GtkWidget *widget)
582 {
583     gint x, y;
584     GtkRequisition req;
585
586     force_to_wrap_truncated (HILDON_BANNER(widget)); /* see N#27000 and G#329646 */
587
588     gtk_widget_size_request (widget, &req);
589
590     if (req.width == 0)
591     {
592         return;
593     }
594
595     x = gdk_screen_width() - HILDON_BANNER_WINDOW_X - req.width;
596     y = check_fullscreen_state (get_current_app_window ()) ? 
597         HILDON_BANNER_WINDOW_FULLSCREEN_Y : HILDON_BANNER_WINDOW_Y;
598
599     gtk_window_move (GTK_WINDOW (widget), x, y);
600 }
601
602 static void
603 hildon_banner_realize                           (GtkWidget *widget)
604 {
605     /* We let the parent to init widget->window before we need it */
606     if (GTK_WIDGET_CLASS (parent_class)->realize)
607         GTK_WIDGET_CLASS (parent_class)->realize (widget);
608
609     /* We use special hint to turn the banner into information notification. */
610     gdk_window_set_type_hint (widget->window, GDK_WINDOW_TYPE_HINT_MESSAGE);
611
612     hildon_banner_check_position (widget);
613 }
614
615 static void 
616 hildon_banner_class_init                        (HildonBannerClass *klass)
617 {
618     GObjectClass *object_class;
619     GtkWidgetClass *widget_class;
620
621     object_class = G_OBJECT_CLASS (klass);
622     widget_class = GTK_WIDGET_CLASS (klass);
623     parent_class = g_type_class_peek_parent (klass);
624
625     /* Append private structure to class. This is more elegant than
626        on g_new based approach */
627     g_type_class_add_private (klass, sizeof (HildonBannerPrivate));
628
629     /* Override virtual methods */
630     object_class->constructor = hildon_banner_constructor;
631     object_class->set_property = hildon_banner_set_property;
632     object_class->get_property = hildon_banner_get_property;
633     GTK_OBJECT_CLASS (klass)->destroy = hildon_banner_destroy;
634     widget_class->map_event = hildon_banner_map_event;
635     widget_class->realize = hildon_banner_realize;
636
637     /* Install properties.
638        We need construct properties for singleton purposes */
639     g_object_class_install_property (object_class, PROP_PARENT_WINDOW,
640             g_param_spec_object ("parent-window",
641                 "Parent window",
642                 "The window for which the banner will be singleton",
643                 GTK_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
644
645     g_object_class_install_property (object_class, PROP_IS_TIMED,
646             g_param_spec_boolean ("is-timed",
647                 "Is timed",
648                 "Whether or not the notification goes away automatically "
649                 "after the specified time has passed",
650                 FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
651 }
652
653 static void 
654 hildon_banner_init                              (HildonBanner *self)
655 {
656     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
657     g_assert (self);
658
659     /* Initialize the common layout inside banner */
660     priv->layout = gtk_hbox_new (FALSE, HILDON_MARGIN_DEFAULT);
661
662     priv->label = g_object_new (GTK_TYPE_LABEL, NULL);
663     gtk_label_set_line_wrap (GTK_LABEL (priv->label), TRUE);
664
665     gtk_container_set_border_width (GTK_CONTAINER (priv->layout), HILDON_MARGIN_DEFAULT);
666     gtk_container_add (GTK_CONTAINER (self), priv->layout);
667     gtk_box_pack_start (GTK_BOX (priv->layout), priv->label, TRUE, TRUE, 0);
668
669     gtk_window_set_accept_focus (GTK_WINDOW (self), FALSE);
670 }
671
672 /* Makes sure that icon/progress item contains the desired type
673    of item. If possible, tries to avoid creating a new widget but
674    reuses the existing one */
675 static void
676 hildon_banner_ensure_child                      (HildonBanner *self, 
677                                                  GtkWidget *user_widget,
678                                                  guint pos,
679                                                  GType type,
680                                                  const gchar *first_property, 
681                                                  ...)
682 {
683     GtkWidget *widget;
684     va_list args;
685     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
686
687     g_assert (HILDON_IS_BANNER (self));
688     g_assert (priv);
689
690     widget = priv->main_item;
691     va_start (args, first_property);
692
693     /* Reuse existing widget if possible */
694     if (! user_widget && G_TYPE_CHECK_INSTANCE_TYPE (widget, type))
695     {
696         g_object_set_valist (G_OBJECT (widget), first_property, args);
697     }
698     else
699     {
700         /* We have to abandon old content widget */
701         if (widget)
702             gtk_container_remove (GTK_CONTAINER (priv->layout), widget);
703
704         /* Use user provided widget or create a new one */
705         priv->main_item = widget = user_widget ? 
706             user_widget : GTK_WIDGET (g_object_new_valist(type, first_property, args));
707         gtk_box_pack_start (GTK_BOX (priv->layout), widget, TRUE, TRUE, 0);
708     }
709
710     /* We make sure that the widget exists in desired position. Different
711        banners place this child widget to different places */
712     gtk_box_reorder_child (GTK_BOX (priv->layout), widget, pos);
713     va_end (args);
714 }
715
716 /* Creates a new banner instance or uses an existing one */
717 static HildonBanner*
718 hildon_banner_get_instance_for_widget           (GtkWidget *widget, 
719                                                  gboolean timed)
720 {
721     GtkWidget *window;
722
723     g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
724     window = widget ? gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW) : NULL;
725     return g_object_new (HILDON_TYPE_BANNER, "parent-window", window, "is-timed", timed, NULL);
726 }
727
728 /**
729  * hildon_banner_show_information:
730  * @widget: the #GtkWidget that wants to display banner
731  * @icon_name: the name of icon to use. Can be %NULL for default icon.
732  * @text: Text to display
733  *
734  * This function creates and displays an information banner that
735  * automatically goes away after certain time period. For each window
736  * in your application there can only be one timed banner, so if you
737  * spawn a new banner before the earlier one has timed out, the
738  * previous one will be replaced.
739  *
740  */
741 void 
742 hildon_banner_show_information                  (GtkWidget *widget, 
743                                                  const gchar *icon_name,
744                                                  const gchar *text)
745 {
746     HildonBanner *banner;
747
748     g_return_if_fail (GTK_IS_WIDGET(widget));
749     g_return_if_fail (icon_name == NULL || icon_name[0] != 0);
750     g_return_if_fail (text != NULL);
751
752     /* Prepare banner */
753     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
754     hildon_banner_ensure_child (banner, NULL, 0, GTK_TYPE_IMAGE, 
755             "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
756             "icon-name", icon_name ? icon_name : HILDON_BANNER_DEFAULT_ICON,
757             "yalign", 0.0, 
758             NULL);
759
760     hildon_banner_set_text (banner, text);
761     hildon_banner_bind_label_style (banner, NULL);
762
763     /* Show the banner, since caller cannot do that */
764     gtk_widget_show_all (GTK_WIDGET (banner));
765 }
766
767 void       
768 hildon_banner_show_informationf                 (GtkWidget *widget, 
769                                                  const gchar *icon_name,
770                                                  const gchar *format, 
771                                                  ...)
772 {
773     g_return_if_fail (format != NULL);
774
775     gchar *message;
776     va_list args;
777
778     va_start (args, format);
779     message = g_strdup_vprintf (format, args);
780     va_end (args);
781
782     hildon_banner_show_information (widget, icon_name, message);
783
784     g_free (message);
785 }
786
787 /**
788  * hildon_banner_show_information_with_markup:
789  * @widget: the #GtkWidget that wants to display banner
790  * @icon_name: the name of icon to use. Can be %NULL for default icon.
791  * @markup: a markup string to display (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
792  *
793  * This function creates and displays an information banner that
794  * automatically goes away after certain time period. For each window
795  * in your application there can only be one timed banner, so if you
796  * spawn a new banner before the earlier one has timed out, the
797  * previous one will be replaced.
798  *
799  */
800 void 
801 hildon_banner_show_information_with_markup      (GtkWidget *widget, 
802                                                  const gchar *icon_name, 
803                                                  const gchar *markup)
804 {
805     HildonBanner *banner;
806
807     g_return_if_fail (GTK_IS_WIDGET (widget));
808     g_return_if_fail (icon_name == NULL || icon_name[0] != 0);
809     g_return_if_fail (markup != NULL);
810
811     /* Prepare banner */
812     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
813
814     hildon_banner_ensure_child (banner, NULL, 0, GTK_TYPE_IMAGE, 
815             "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
816             "icon-name", icon_name ? icon_name : HILDON_BANNER_DEFAULT_ICON,
817             "yalign", 0.0, 
818             NULL);
819
820     hildon_banner_set_markup (banner, markup);
821     hildon_banner_bind_label_style (banner, NULL);
822
823     /* Show the banner, since caller cannot do that */
824     gtk_widget_show_all (GTK_WIDGET (banner));
825 }
826
827 /**
828  * hildon_banner_show_animation:
829  * @widget: the #GtkWidget that wants to display banner
830  * @animation_name: The progress animation to use. You usually can just
831  *                  pass %NULL for the default animation.
832  * @text: the text to display.
833  *
834  * Shows an animated progress notification. It's recommended not to try
835  * to show more than one progress notification at a time, since
836  * they will appear on top of each other. You can use progress
837  * notifications with timed banners. In this case the banners are
838  * located so that you can somehow see both.
839  *
840  * Please note that banners are destroyed automatically once the
841  * window they are attached to is closed. The pointer that you
842  * receive with this function do not contain additional references,
843  * so it can become invalid without warning (this is true for
844  * all toplevel windows in gtk). To make sure that the banner do not disapear
845  * automatically, you can separately ref the return value (this
846  * doesn't prevent the banner from disappearing, but the object it just
847  * not finalized). In this case you have to call both #gtk_widget_destroy 
848  * followed by #g_object_unref (in this order).
849  * 
850  * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy
851  *          once you are ready with the banner.
852  *
853  */
854 GtkWidget*
855 hildon_banner_show_animation                    (GtkWidget *widget, 
856                                                  const gchar *animation_name, 
857                                                  const gchar *text)
858 {
859     HildonBanner *banner;
860     GtkIconTheme *theme; 
861     GtkIconInfo *info;
862     GtkWidget *image_widget;
863     const gchar *filename;
864
865     g_return_val_if_fail (GTK_IS_WIDGET(widget), NULL);
866     g_return_val_if_fail (animation_name == NULL || animation_name[0] != 0, NULL);
867     g_return_val_if_fail (text != NULL, NULL);
868
869     /* Find out which animation to use */
870     theme = gtk_icon_theme_get_default ();
871     info = gtk_icon_theme_lookup_icon (theme, animation_name ?   /* FIXME: consider using: gtk_icon_theme_load_icon() */
872             animation_name : HILDON_BANNER_DEFAULT_PROGRESS_ANIMATION,
873             HILDON_ICON_SIZE_NOTE, 0);
874
875     /* Try to load animation. One could try to optimize this 
876        to avoid loading the default animation during each call */
877     if (info) {
878         filename = gtk_icon_info_get_filename (info);
879         image_widget = gtk_image_new_from_file (filename);
880         gtk_icon_info_free (info);
881     } else {
882         g_warning ("Icon theme lookup for icon failed!");
883         image_widget = NULL;
884     }
885
886     /* Prepare banner */
887     banner = hildon_banner_get_instance_for_widget (widget, FALSE);
888     hildon_banner_ensure_child (banner, image_widget, 0,
889             GTK_TYPE_IMAGE, "yalign", 0.0, NULL);
890
891     hildon_banner_set_text (banner, text);
892     hildon_banner_bind_label_style (banner, NULL);
893
894     /* And show it */
895     gtk_widget_show_all (GTK_WIDGET (banner));
896
897     return (GtkWidget *) banner;
898 }
899
900 /**
901  * hildon_banner_show_progress:
902  * @widget: the #GtkWidget that wants to display banner
903  * @bar: Progressbar to use. You usually can just pass %NULL, unless
904  *       you want somehow customized progress bar.
905  * @text: text to display.
906  *
907  * Shows progress notification. See #hildon_banner_show_animation
908  * for more information.
909  * 
910  * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy
911  *          once you are ready with the banner.
912  *
913  */
914 GtkWidget*
915 hildon_banner_show_progress                     (GtkWidget *widget, 
916                                                  GtkProgressBar *bar, 
917                                                  const gchar *text)
918 {
919     HildonBanner *banner;
920     HildonBannerPrivate *priv;
921
922     g_return_val_if_fail (GTK_IS_WIDGET(widget), NULL);
923     g_return_val_if_fail (bar == NULL || GTK_IS_PROGRESS_BAR (bar), NULL);
924     g_return_val_if_fail (text != NULL, NULL);
925
926
927     /* Prepare banner */
928     banner = hildon_banner_get_instance_for_widget (widget, FALSE);
929     priv = HILDON_BANNER_GET_PRIVATE (banner);
930     g_assert (priv);
931     hildon_banner_ensure_child (banner, (GtkWidget *) bar, -1, GTK_TYPE_PROGRESS_BAR, NULL);
932
933     gtk_widget_set_size_request (priv->main_item,
934             HILDON_BANNER_PROGRESS_WIDTH, -1);
935
936     hildon_banner_set_text (banner, text);
937     hildon_banner_bind_label_style (banner, NULL);
938
939     /* Show the banner */
940     gtk_widget_show_all (GTK_WIDGET (banner));
941
942     return GTK_WIDGET (banner);   
943 }
944
945 /**
946  * hildon_banner_set_text:
947  * @self: a #HildonBanner widget
948  * @text: a new text to display in banner
949  *
950  * Sets the text that is displayed in the banner.
951  *
952  */
953 void 
954 hildon_banner_set_text                          (HildonBanner *self, 
955                                                  const gchar *text)
956 {
957     GtkLabel *label;
958     HildonBannerPrivate *priv;
959
960     g_return_if_fail (HILDON_IS_BANNER (self));
961
962     priv = HILDON_BANNER_GET_PRIVATE (self);
963     g_assert (priv);
964
965     label = GTK_LABEL (priv->label);
966     gtk_label_set_text (label, text);
967
968     hildon_banner_check_position (GTK_WIDGET (self));
969 }
970
971 /**
972  * hildon_banner_set_markup:
973  * @self: a #HildonBanner widget
974  * @markup: a new text with Pango markup to display in the banner
975  *
976  * Sets the text with markup that is displayed in the banner.
977  *
978  * Since: 0.12.8
979  */
980 void 
981 hildon_banner_set_markup                        (HildonBanner *self, 
982                                                  const gchar *markup)
983 {
984     GtkLabel *label;
985     HildonBannerPrivate *priv;
986
987     g_return_if_fail (HILDON_IS_BANNER (self));
988
989     priv = HILDON_BANNER_GET_PRIVATE (self);
990     g_assert (priv);
991
992     label = GTK_LABEL (priv->label);
993     gtk_label_set_markup (label, markup);
994
995     hildon_banner_check_position (GTK_WIDGET(self));
996 }
997
998 /**
999  * hildon_banner_set_fraction:
1000  * @self: a #HildonBanner widget
1001  * @fraction: #gdouble
1002  *
1003  * The fraction is the completion of progressbar, 
1004  * the scale is from 0.0 to 1.0.
1005  * Sets the amount of fraction the progressbar has.
1006  *
1007  */
1008 void 
1009 hildon_banner_set_fraction                      (HildonBanner *self, 
1010                                                  gdouble fraction)
1011 {
1012     HildonBannerPrivate *priv;
1013
1014     g_return_if_fail (HILDON_IS_BANNER (self));
1015     priv = HILDON_BANNER_GET_PRIVATE (self);
1016     g_assert (priv);
1017
1018     g_return_if_fail (GTK_IS_PROGRESS_BAR (priv->main_item));
1019     gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->main_item), fraction);
1020 }
1021
1022 /**
1023  * Deprecated: really, do NOT use.
1024  */
1025 void 
1026 hildon_gtk_label_set_text_n_lines               (GtkLabel *label, 
1027                                                  const gchar *text, 
1028                                                  gint max_lines)
1029 {
1030     /* Forces the wrapping of text into several lines and ellipsizes the rest. 
1031        Similar to combination of gtk_label_set_wrap and pango ellipzation. 
1032        We cannot just use those directly, since ellipzation always wins wrapping.
1033
1034        This means that we have to:
1035      * First wrap the text
1036      * Insert forced linebreaks into text
1037      * Truncate the result
1038
1039      NOTE! This will not work with pango markup!
1040
1041     FIXME: luc: DO NOT TRUNCATE the text. Use as many lines as needed.
1042     Lenth of the text is under applications' responsibility.
1043     Widget does not have to enforce this. */
1044
1045     PangoLayout *layout;
1046     PangoLayoutLine *line;
1047     GtkRequisition req;
1048     GString *wrapped_text;
1049     gchar *line_data;
1050     gint lines, i;
1051
1052     g_return_if_fail (GTK_IS_LABEL (label));
1053     g_return_if_fail (max_lines >= 1);
1054
1055     /* Setup the label to contain the new data */
1056     gtk_label_set_text (label, text);
1057     gtk_label_set_line_wrap (label, TRUE);
1058     gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_NONE);
1059
1060     /* We really want to recalculate the size, not use some old values */
1061     gtk_widget_size_request (GTK_WIDGET (label), &req);
1062     layout = gtk_label_get_layout (label);
1063     lines = pango_layout_get_line_count (layout);
1064
1065     /* Now collect the wrapped text. */
1066     wrapped_text = g_string_new (NULL);
1067
1068     for (i = 0; i < lines; i++)
1069     {
1070         /* Append the next line into wrapping buffer, but 
1071            avoid adding extra whitespaces at the end, since those
1072            can cause other lines to be ellipsized as well. */
1073         line = pango_layout_get_line (layout, i);
1074         line_data = g_strndup (pango_layout_get_text(layout) + line->start_index, 
1075                 line->length);
1076         g_strchomp (line_data);
1077         g_string_append (wrapped_text, line_data);
1078
1079         /* Append forced linebreaks, until we have the desired
1080            amount of lines. After that we put the rest to the
1081            last line to make ellipzation to happen */
1082         if (i < lines - 1)
1083         {
1084             if (i < max_lines - 1)
1085                 g_string_append_c (wrapped_text, '\n');
1086             else
1087                 g_string_append_c (wrapped_text, ' ');
1088         }
1089
1090         g_free(line_data);
1091     }
1092
1093     /* Now update the label to use wrapped text. Use builtin
1094        ellipzation as well. */
1095     gtk_widget_set_size_request (GTK_WIDGET (label), req.width, -1);
1096     gtk_label_set_text (label, wrapped_text->str);
1097     gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END);
1098     gtk_label_set_line_wrap (label, FALSE);
1099
1100     g_string_free (wrapped_text, TRUE);
1101 }
1102