82e587babef7036c075f0432ce381a8781214ebe
[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 (GTK_IS_WIDGET (user_widget));
689     g_assert (priv);
690
691     widget = priv->main_item;
692     va_start (args, first_property);
693
694     /* Reuse existing widget if possible */
695     if (! user_widget && G_TYPE_CHECK_INSTANCE_TYPE (widget, type))
696     {
697         g_object_set_valist (G_OBJECT (widget), first_property, args);
698     }
699     else
700     {
701         /* We have to abandon old content widget */
702         if (widget)
703             gtk_container_remove (GTK_CONTAINER (priv->layout), widget);
704
705         /* Use user provided widget or create a new one */
706         priv->main_item = widget = user_widget ? 
707             user_widget : GTK_WIDGET (g_object_new_valist(type, first_property, args));
708         gtk_box_pack_start (GTK_BOX (priv->layout), widget, TRUE, TRUE, 0);
709     }
710
711     /* We make sure that the widget exists in desired position. Different
712        banners place this child widget to different places */
713     gtk_box_reorder_child (GTK_BOX (priv->layout), widget, pos);
714     va_end (args);
715 }
716
717 /* Creates a new banner instance or uses an existing one */
718 static HildonBanner*
719 hildon_banner_get_instance_for_widget           (GtkWidget *widget, 
720                                                  gboolean timed)
721 {
722     GtkWidget *window;
723
724     g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
725     window = widget ? gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW) : NULL;
726     return g_object_new (HILDON_TYPE_BANNER, "parent-window", window, "is-timed", timed, NULL);
727 }
728
729 /**
730  * hildon_banner_show_information:
731  * @widget: the #GtkWidget that wants to display banner
732  * @icon_name: the name of icon to use. Can be %NULL for default icon.
733  * @text: Text to display
734  *
735  * This function creates and displays an information banner that
736  * automatically goes away after certain time period. For each window
737  * in your application there can only be one timed banner, so if you
738  * spawn a new banner before the earlier one has timed out, the
739  * previous one will be replaced.
740  *
741  */
742 void 
743 hildon_banner_show_information                  (GtkWidget *widget, 
744                                                  const gchar *icon_name,
745                                                  const gchar *text)
746 {
747     HildonBanner *banner;
748
749     g_return_if_fail (GTK_IS_WIDGET(widget));
750     g_return_if_fail (icon_name == NULL || icon_name[0] != 0);
751     g_return_if_fail (text != NULL);
752
753     /* Prepare banner */
754     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
755     hildon_banner_ensure_child (banner, NULL, 0, GTK_TYPE_IMAGE, 
756             "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
757             "icon-name", icon_name ? icon_name : HILDON_BANNER_DEFAULT_ICON,
758             "yalign", 0.0, 
759             NULL);
760
761     hildon_banner_set_text (banner, text);
762     hildon_banner_bind_label_style (banner, NULL);
763
764     /* Show the banner, since caller cannot do that */
765     gtk_widget_show_all (GTK_WIDGET (banner));
766 }
767
768 void       
769 hildon_banner_show_informationf                 (GtkWidget *widget, 
770                                                  const gchar *icon_name,
771                                                  const gchar *format, 
772                                                  ...)
773 {
774     g_return_if_fail (format != NULL);
775
776     gchar *message;
777     va_list args;
778
779     va_start (args, format);
780     message = g_strdup_vprintf (format, args);
781     va_end (args);
782
783     hildon_banner_show_information (widget, icon_name, message);
784
785     g_free (message);
786 }
787
788 /**
789  * hildon_banner_show_information_with_markup:
790  * @widget: the #GtkWidget that wants to display banner
791  * @icon_name: the name of icon to use. Can be %NULL for default icon.
792  * @markup: a markup string to display (see <link linkend="PangoMarkupFormat">Pango markup format</link>)
793  *
794  * This function creates and displays an information banner that
795  * automatically goes away after certain time period. For each window
796  * in your application there can only be one timed banner, so if you
797  * spawn a new banner before the earlier one has timed out, the
798  * previous one will be replaced.
799  *
800  */
801 void 
802 hildon_banner_show_information_with_markup      (GtkWidget *widget, 
803                                                  const gchar *icon_name, 
804                                                  const gchar *markup)
805 {
806     HildonBanner *banner;
807
808     g_return_if_fail (GTK_IS_WIDGET (widget));
809     g_return_if_fail (icon_name == NULL || icon_name[0] != 0);
810     g_return_if_fail (markup != NULL);
811
812     /* Prepare banner */
813     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
814
815     hildon_banner_ensure_child (banner, NULL, 0, GTK_TYPE_IMAGE, 
816             "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
817             "icon-name", icon_name ? icon_name : HILDON_BANNER_DEFAULT_ICON,
818             "yalign", 0.0, 
819             NULL);
820
821     hildon_banner_set_markup (banner, markup);
822     hildon_banner_bind_label_style (banner, NULL);
823
824     /* Show the banner, since caller cannot do that */
825     gtk_widget_show_all (GTK_WIDGET (banner));
826 }
827
828 /**
829  * hildon_banner_show_animation:
830  * @widget: the #GtkWidget that wants to display banner
831  * @animation_name: The progress animation to use. You usually can just
832  *                  pass %NULL for the default animation.
833  * @text: the text to display.
834  *
835  * Shows an animated progress notification. It's recommended not to try
836  * to show more than one progress notification at a time, since
837  * they will appear on top of each other. You can use progress
838  * notifications with timed banners. In this case the banners are
839  * located so that you can somehow see both.
840  *
841  * Please note that banners are destroyed automatically once the
842  * window they are attached to is closed. The pointer that you
843  * receive with this function do not contain additional references,
844  * so it can become invalid without warning (this is true for
845  * all toplevel windows in gtk). To make sure that the banner do not disapear
846  * automatically, you can separately ref the return value (this
847  * doesn't prevent the banner from disappearing, but the object it just
848  * not finalized). In this case you have to call both #gtk_widget_destroy 
849  * followed by #g_object_unref (in this order).
850  * 
851  * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy
852  *          once you are ready with the banner.
853  *
854  */
855 GtkWidget*
856 hildon_banner_show_animation                    (GtkWidget *widget, 
857                                                  const gchar *animation_name, 
858                                                  const gchar *text)
859 {
860     HildonBanner *banner;
861     GtkIconTheme *theme; 
862     GtkIconInfo *info;
863     GtkWidget *image_widget;
864     const gchar *filename;
865
866     g_return_val_if_fail (GTK_IS_WIDGET(widget), NULL);
867     g_return_val_if_fail (animation_name == NULL || animation_name[0] != 0, NULL);
868     g_return_val_if_fail (text != NULL, NULL);
869
870     /* Find out which animation to use */
871     theme = gtk_icon_theme_get_default ();
872     info = gtk_icon_theme_lookup_icon (theme, animation_name ?   /* FIXME: consider using: gtk_icon_theme_load_icon() */
873             animation_name : HILDON_BANNER_DEFAULT_PROGRESS_ANIMATION,
874             HILDON_ICON_SIZE_NOTE, 0);
875
876     /* Try to load animation. One could try to optimize this 
877        to avoid loading the default animation during each call */
878     if (info) {
879         filename = gtk_icon_info_get_filename (info);
880         image_widget = gtk_image_new_from_file (filename);
881         gtk_icon_info_free (info);
882     } else {
883         g_warning ("Icon theme lookup for icon failed!");
884         image_widget = NULL;
885     }
886
887     /* Prepare banner */
888     banner = hildon_banner_get_instance_for_widget (widget, FALSE);
889     hildon_banner_ensure_child (banner, image_widget, 0,
890             GTK_TYPE_IMAGE, "yalign", 0.0, NULL);
891
892     hildon_banner_set_text (banner, text);
893     hildon_banner_bind_label_style (banner, NULL);
894
895     /* And show it */
896     gtk_widget_show_all (GTK_WIDGET (banner));
897
898     return (GtkWidget *) banner;
899 }
900
901 /**
902  * hildon_banner_show_progress:
903  * @widget: the #GtkWidget that wants to display banner
904  * @bar: Progressbar to use. You usually can just pass %NULL, unless
905  *       you want somehow customized progress bar.
906  * @text: text to display.
907  *
908  * Shows progress notification. See #hildon_banner_show_animation
909  * for more information.
910  * 
911  * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy
912  *          once you are ready with the banner.
913  *
914  */
915 GtkWidget*
916 hildon_banner_show_progress                     (GtkWidget *widget, 
917                                                  GtkProgressBar *bar, 
918                                                  const gchar *text)
919 {
920     HildonBanner *banner;
921     HildonBannerPrivate *priv;
922
923     g_return_val_if_fail (GTK_IS_WIDGET(widget), NULL);
924     g_return_val_if_fail (bar == NULL || GTK_IS_PROGRESS_BAR(bar), NULL);
925     g_return_val_if_fail (text != NULL, NULL);
926
927     priv = HILDON_BANNER_GET_PRIVATE (widget);
928     g_assert (priv);
929
930     /* Prepare banner */
931     banner = hildon_banner_get_instance_for_widget (widget, FALSE);
932     hildon_banner_ensure_child (banner, (GtkWidget *) bar, -1, GTK_TYPE_PROGRESS_BAR, NULL);
933
934     gtk_widget_set_size_request (priv->main_item,
935             HILDON_BANNER_PROGRESS_WIDTH, -1);
936
937     hildon_banner_set_text (banner, text);
938     hildon_banner_bind_label_style (banner, NULL);
939
940     /* Show the banner */
941     gtk_widget_show_all (GTK_WIDGET (banner));
942
943     return GTK_WIDGET (banner);   
944 }
945
946 /**
947  * hildon_banner_set_text:
948  * @self: a #HildonBanner widget
949  * @text: a new text to display in banner
950  *
951  * Sets the text that is displayed in the banner.
952  *
953  */
954 void 
955 hildon_banner_set_text                          (HildonBanner *self, 
956                                                  const gchar *text)
957 {
958     GtkLabel *label;
959     HildonBannerPrivate *priv;
960
961     g_return_if_fail (HILDON_IS_BANNER (self));
962
963     priv = HILDON_BANNER_GET_PRIVATE (self);
964     g_assert (priv);
965
966     label = GTK_LABEL (priv->label);
967     gtk_label_set_text (label, text);
968
969     hildon_banner_check_position (GTK_WIDGET (self));
970 }
971
972 /**
973  * hildon_banner_set_markup:
974  * @self: a #HildonBanner widget
975  * @markup: a new text with Pango markup to display in the banner
976  *
977  * Sets the text with markup that is displayed in the banner.
978  *
979  * Since: 0.12.8
980  */
981 void 
982 hildon_banner_set_markup                        (HildonBanner *self, 
983                                                  const gchar *markup)
984 {
985     GtkLabel *label;
986     HildonBannerPrivate *priv;
987
988     g_return_if_fail (HILDON_IS_BANNER (self));
989
990     priv = HILDON_BANNER_GET_PRIVATE (self);
991     g_assert (priv);
992
993     label = GTK_LABEL (priv->label);
994     gtk_label_set_markup (label, markup);
995
996     hildon_banner_check_position (GTK_WIDGET(self));
997 }
998
999 /**
1000  * hildon_banner_set_fraction:
1001  * @self: a #HildonBanner widget
1002  * @fraction: #gdouble
1003  *
1004  * The fraction is the completion of progressbar, 
1005  * the scale is from 0.0 to 1.0.
1006  * Sets the amount of fraction the progressbar has.
1007  *
1008  */
1009 void 
1010 hildon_banner_set_fraction                      (HildonBanner *self, 
1011                                                  gdouble fraction)
1012 {
1013     HildonBannerPrivate *priv;
1014
1015     g_return_if_fail (HILDON_IS_BANNER (self));
1016     priv = HILDON_BANNER_GET_PRIVATE (self);
1017     g_assert (priv);
1018
1019     g_return_if_fail (GTK_IS_PROGRESS_BAR (priv->main_item));
1020     gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->main_item), fraction);
1021 }
1022
1023 /**
1024  * Deprecated: really, do NOT use.
1025  */
1026 void 
1027 hildon_gtk_label_set_text_n_lines               (GtkLabel *label, 
1028                                                  const gchar *text, 
1029                                                  gint max_lines)
1030 {
1031     /* Forces the wrapping of text into several lines and ellipsizes the rest. 
1032        Similar to combination of gtk_label_set_wrap and pango ellipzation. 
1033        We cannot just use those directly, since ellipzation always wins wrapping.
1034
1035        This means that we have to:
1036      * First wrap the text
1037      * Insert forced linebreaks into text
1038      * Truncate the result
1039
1040      NOTE! This will not work with pango markup!
1041
1042     FIXME: luc: DO NOT TRUNCATE the text. Use as many lines as needed.
1043     Lenth of the text is under applications' responsibility.
1044     Widget does not have to enforce this. */
1045
1046     PangoLayout *layout;
1047     PangoLayoutLine *line;
1048     GtkRequisition req;
1049     GString *wrapped_text;
1050     gchar *line_data;
1051     gint lines, i;
1052
1053     g_return_if_fail (GTK_IS_LABEL (label));
1054     g_return_if_fail (max_lines >= 1);
1055
1056     /* Setup the label to contain the new data */
1057     gtk_label_set_text (label, text);
1058     gtk_label_set_line_wrap (label, TRUE);
1059     gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_NONE);
1060
1061     /* We really want to recalculate the size, not use some old values */
1062     gtk_widget_size_request (GTK_WIDGET (label), &req);
1063     layout = gtk_label_get_layout (label);
1064     lines = pango_layout_get_line_count (layout);
1065
1066     /* Now collect the wrapped text. */
1067     wrapped_text = g_string_new (NULL);
1068
1069     for (i = 0; i < lines; i++)
1070     {
1071         /* Append the next line into wrapping buffer, but 
1072            avoid adding extra whitespaces at the end, since those
1073            can cause other lines to be ellipsized as well. */
1074         line = pango_layout_get_line (layout, i);
1075         line_data = g_strndup (pango_layout_get_text(layout) + line->start_index, 
1076                 line->length);
1077         g_strchomp (line_data);
1078         g_string_append (wrapped_text, line_data);
1079
1080         /* Append forced linebreaks, until we have the desired
1081            amount of lines. After that we put the rest to the
1082            last line to make ellipzation to happen */
1083         if (i < lines - 1)
1084         {
1085             if (i < max_lines - 1)
1086                 g_string_append_c (wrapped_text, '\n');
1087             else
1088                 g_string_append_c (wrapped_text, ' ');
1089         }
1090
1091         g_free(line_data);
1092     }
1093
1094     /* Now update the label to use wrapped text. Use builtin
1095        ellipzation as well. */
1096     gtk_widget_set_size_request (GTK_WIDGET (label), req.width, -1);
1097     gtk_label_set_text (label, wrapped_text->str);
1098     gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END);
1099     gtk_label_set_line_wrap (label, FALSE);
1100
1101     g_string_free (wrapped_text, TRUE);
1102 }
1103