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