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