2008-04-15 13:21:13 <timj@imendio.com>
[hildon] / src / hildon-banner.c
index 801dce4..b2fa6c3 100644 (file)
@@ -1,14 +1,14 @@
 /*
  * This file is a part of hildon
  *
- * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
+ * Copyright (C) 2005, 2006, 2007 Nokia Corporation, all rights reserved.
  *
  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * as published by the Free Software Foundation; version 2.1 of
- * the License.
+ * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  *
  */
 
+/**
+ * SECTION:hildon-banner 
+ * @short_description: A widget used to display timed notifications. 
+ *
+ * #HildonBanner can be used to display a short, timed notification 
+ * or information to the user. It can communicate that a 
+ * task has been finished or the application state has changed.
+ * Banners should be used only to display non-critical pieces of 
+ * information.
+ *
+ */
+
 #ifdef                                          HAVE_CONFIG_H
 #include                                        <config.h>
 #endif
@@ -54,7 +66,7 @@
 
 /* default timeout */
 
-#define                                         HILDON_BANNER_TIMEOUT 3000
+#define                                         HILDON_BANNER_DEFAULT_TIMEOUT 3000
 
 /* default icons */
 
@@ -66,7 +78,8 @@ enum
 {
     PROP_0,
     PROP_PARENT_WINDOW, 
-    PROP_IS_TIMED
+    PROP_IS_TIMED,
+    PROP_TIMEOUT
 };
 
 static GtkWidget*                               global_timed_banner = NULL;
@@ -117,9 +130,18 @@ hildon_banner_constructor                       (GType type,
                                                  guint n_construct_params,
                                                  GObjectConstructParam *construct_params);
 
+static void
+hildon_banner_finalize                          (GObject *object);
+
+static gboolean
+hildon_banner_button_press_event                (GtkWidget* widget,
+                                                GdkEventButton* event);
+
 static gboolean 
 hildon_banner_map_event                         (GtkWidget *widget, 
                                                  GdkEventAny *event);
+static void
+hildon_banner_reset_wrap_state                  (HildonBanner *banner);
 
 static void 
 force_to_wrap_truncated                         (HildonBanner *banner);
@@ -148,38 +170,7 @@ static HildonBanner*
 hildon_banner_get_instance_for_widget           (GtkWidget *widget, 
                                                  gboolean timed);
 
-static GtkWindowClass*                          parent_class = NULL;
-
-/**
- * hildon_banner_get_type:
- *
- * Initialises, and returns the type of a hildon banner.
- *
- * @Returns: GType of #HildonBanner
- */
-GType G_GNUC_CONST 
-hildon_banner_get_type                          (void)
-{
-    static GType banner_type = 0;
-
-    if (! banner_type)
-    {
-        static const GTypeInfo banner_info = {
-            sizeof (HildonBannerClass),
-            NULL,       /* base_init */
-            NULL,       /* base_finalize */
-            (GClassInitFunc) hildon_banner_class_init,
-            NULL,       /* class_finalize */
-            NULL,       /* class_data */
-            sizeof (HildonBanner),
-            0,  /* n_preallocs */
-            (GInstanceInitFunc) hildon_banner_init,
-        };
-        banner_type = g_type_register_static (GTK_TYPE_WINDOW,
-                "HildonBanner", &banner_info, 0 );
-    }
-    return banner_type;
-}
+G_DEFINE_TYPE (HildonBanner, hildon_banner, GTK_TYPE_WINDOW)
 
 /* copy/paste from old infoprint implementation: Use matchbox 
    properties to find the topmost application window */
@@ -286,11 +277,31 @@ hildon_banner_bind_label_style                  (HildonBanner *self,
 }
 
 /* In timeout function we automatically destroy timed banners */
+static gboolean
+simulate_close (GtkWidget* widget)
+{
+    gboolean result = FALSE;
+
+    /* If the banner is currently visible (it normally should), 
+       we simulate clicking the close button of the window.
+       This allows applications to reuse the banner by prevent
+       closing it etc */
+    if (GTK_WIDGET_DRAWABLE (widget))
+    {
+        GdkEvent *event = gdk_event_new (GDK_DELETE);
+        event->any.window = g_object_ref (widget->window);
+        event->any.send_event = FALSE;
+        result = gtk_widget_event (widget, event);
+        gdk_event_free (event);
+    }
+
+    return result;
+}
+
 static gboolean 
 hildon_banner_timeout                           (gpointer data)
 {
     GtkWidget *widget;
-    GdkEvent *event;
     gboolean continue_timeout = FALSE;
 
     GDK_THREADS_ENTER ();
@@ -300,21 +311,13 @@ hildon_banner_timeout                           (gpointer data)
     widget = GTK_WIDGET (data);
     g_object_ref (widget);
 
-    /* If the banner is currently visible (it normally should), 
-       we simulate clicking the close button of the window.
-       This allows applications to reuse the banner by prevent
-       closing it etc */
-    if (GTK_WIDGET_DRAWABLE (widget))
-    {
-        event = gdk_event_new (GDK_DELETE);
-        event->any.window = g_object_ref (widget->window);
-        event->any.send_event = FALSE;
-        continue_timeout = gtk_widget_event (widget, event);
-        gdk_event_free (event);
-    }
+    continue_timeout = simulate_close (widget);
 
-    if (! continue_timeout)
+    if (! continue_timeout) {
+        HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (data);
+        priv->timeout_id = 0;
         gtk_widget_destroy (widget);
+    }
 
     g_object_unref (widget);
 
@@ -344,8 +347,8 @@ hildon_banner_ensure_timeout                    (HildonBanner *self)
     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
     g_assert (priv);
 
-    if (priv->timeout_id == 0 && priv->is_timed)
-        priv->timeout_id = g_timeout_add (HILDON_BANNER_TIMEOUT, 
+    if (priv->timeout_id == 0 && priv->is_timed && priv->timeout > 0)
+        priv->timeout_id = g_timeout_add (priv->timeout, 
                 hildon_banner_timeout, self);
 }
 
@@ -362,6 +365,10 @@ hildon_banner_set_property                      (GObject *object,
 
     switch (prop_id) {
 
+        case PROP_TIMEOUT:
+             priv->timeout = g_value_get_uint (value);
+             break;
         case PROP_IS_TIMED:
             priv->is_timed = g_value_get_boolean (value);
 
@@ -379,11 +386,17 @@ hildon_banner_set_property                      (GObject *object,
 
         case PROP_PARENT_WINDOW:
             window = g_value_get_object (value);         
+            if (priv->parent) {
+                g_object_remove_weak_pointer(G_OBJECT (priv->parent), (gpointer) &priv->parent);
+            }
 
             gtk_window_set_transient_for (GTK_WINDOW (object), (GtkWindow *) window);
+            priv->parent = (GtkWindow *) window;
 
-            if (window)
+            if (window) {
                 gtk_window_set_destroy_with_parent (GTK_WINDOW (object), TRUE);
+                g_object_add_weak_pointer(G_OBJECT (window), (gpointer) &priv->parent);
+            }
 
             break;
 
@@ -404,6 +417,10 @@ hildon_banner_get_property                      (GObject *object,
 
     switch (prop_id)
     {
+        case PROP_TIMEOUT:
+             g_value_set_uint (value, priv->timeout);
+             break;
         case PROP_IS_TIMED:
             g_value_set_boolean (value, priv->is_timed);
             break;
@@ -425,7 +442,7 @@ hildon_banner_destroy                           (GtkObject *object)
     g_assert (priv);
 
     HildonBanner *self;
-    GObject *parent_window;
+    GObject *parent_window = (GObject *) priv->parent;
 
     g_assert (HILDON_IS_BANNER (object));
     self = HILDON_BANNER (object);
@@ -437,13 +454,14 @@ hildon_banner_destroy                           (GtkObject *object)
     }
 
     /* Remove the data from parent window for timed banners. Those hold reference */
-    if (priv->is_timed && (parent_window = (GObject *) gtk_window_get_transient_for (GTK_WINDOW (object))) != NULL)
+    if (priv->is_timed && parent_window != NULL) {
         g_object_set_qdata (parent_window, hildon_banner_timed_quark (), NULL);
+    }
 
     (void) hildon_banner_clear_timeout (self);
 
-    if (GTK_OBJECT_CLASS (parent_class)->destroy)
-        GTK_OBJECT_CLASS (parent_class)->destroy (object);
+    if (GTK_OBJECT_CLASS (hildon_banner_parent_class)->destroy)
+        GTK_OBJECT_CLASS (hildon_banner_parent_class)->destroy (object);
 }
 
 /* Search a previous banner instance */
@@ -451,12 +469,11 @@ static GObject*
 hildon_banner_real_get_instance                 (GObject *window, 
                                                  gboolean timed)
 {
-    g_assert (GTK_IS_WINDOW (window));
-
     if (timed) {
         /* If we have a parent window, the previous instance is stored there */
-        if (window) 
+        if (window) {
             return g_object_get_qdata(window, hildon_banner_timed_quark ());
+        }
 
         /* System notification instance is stored into global pointer */
         return (GObject *) global_timed_banner;
@@ -492,7 +509,7 @@ hildon_banner_constructor                       (GType type,
     if (! banner)
     {
         /* We have to create a new banner */
-        banner = G_OBJECT_CLASS (parent_class)->constructor (type, n_construct_params, construct_params);
+        banner = G_OBJECT_CLASS (hildon_banner_parent_class)->constructor (type, n_construct_params, construct_params);
 
         /* Store the newly created singleton instance either into parent 
            window data or into global variables. */
@@ -516,6 +533,7 @@ hildon_banner_constructor                       (GType type,
            assertion `nqueue->freeze_count > 0' failed */
 
         g_object_freeze_notify (banner);
+        hildon_banner_reset_wrap_state (HILDON_BANNER (banner));
     }
 
     /* We restart possible timeouts for each new timed banner request */
@@ -525,6 +543,33 @@ hildon_banner_constructor                       (GType type,
     return banner;
 }
 
+static void
+hildon_banner_finalize                          (GObject *object)
+{
+    HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object);
+
+    if (priv->parent) {
+        g_object_remove_weak_pointer(G_OBJECT (priv->parent), (gpointer) &priv->parent);
+    }
+
+    G_OBJECT_CLASS (hildon_banner_parent_class)->finalize (object);
+}
+
+static gboolean
+hildon_banner_button_press_event                (GtkWidget* widget,
+                                                GdkEventButton* event)
+{
+       gboolean result = simulate_close (widget);
+
+       if (!result) {
+               /* signal emission not stopped - basically behave like
+                * gtk_main_do_event() for a delete event */
+               gtk_widget_destroy (widget);
+       }
+
+       return result;
+}
+
 /* We start the timer for timed notifications after the window appears on screen */
 static gboolean 
 hildon_banner_map_event                         (GtkWidget *widget, 
@@ -532,14 +577,55 @@ hildon_banner_map_event                         (GtkWidget *widget,
 {
     gboolean result = FALSE;
 
-    if (GTK_WIDGET_CLASS (parent_class)->map_event)
-        result = GTK_WIDGET_CLASS (parent_class)->map_event (widget, event);
+    if (GTK_WIDGET_CLASS (hildon_banner_parent_class)->map_event)
+        result = GTK_WIDGET_CLASS (hildon_banner_parent_class)->map_event (widget, event);
 
     hildon_banner_ensure_timeout (HILDON_BANNER(widget));
 
     return result;
 }  
 
+#if defined(MAEMO_GTK)
+
+static GdkAtom atom_temporaries = GDK_NONE;
+
+/* Do nothing for _GTK_DELETE_TEMPORARIES */
+static gint
+hildon_banner_client_event                      (GtkWidget *widget,
+                                                 GdkEventClient  *event)
+{
+  gboolean handled = FALSE;
+
+  if (atom_temporaries == GDK_NONE)
+    atom_temporaries = gdk_atom_intern_static_string ("_GTK_DELETE_TEMPORARIES");
+
+  if (event->message_type == atom_temporaries)
+    {
+      handled = TRUE;
+    }
+
+  return handled;
+}
+#endif
+
+static void
+hildon_banner_reset_wrap_state (HildonBanner *banner)
+{
+    PangoLayout *layout;
+    HildonBannerPrivate *priv;
+
+    priv = HILDON_BANNER_GET_PRIVATE (banner);
+    g_assert (priv);
+
+    layout = gtk_label_get_layout (GTK_LABEL (priv->label));
+
+    pango_layout_set_width (layout, -1);
+    priv->has_been_wrapped = FALSE;
+    priv->has_been_truncated = FALSE;
+
+    gtk_widget_set_size_request (priv->label, -1, -1);
+    gtk_widget_set_size_request (GTK_WIDGET (banner), -1, -1);
+}
 
 /* force to wrap truncated label by setting explicit size request
  * see N#27000 and G#329646 */
@@ -550,30 +636,64 @@ force_to_wrap_truncated                         (HildonBanner *banner)
     PangoLayout *layout;
     int width_text, width_max;
     int width = -1;
+    int height = -1;
+    PangoRectangle logical;
+    GtkRequisition requisition;
     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (banner);
 
-    g_assert (priv);
+    g_return_if_fail (priv);
+
     label = GTK_LABEL (priv->label);
 
     layout = gtk_label_get_layout (label);
-    width_text  = PANGO_PIXELS(pango_layout_get_width (layout));
-    /* = width to which the lines of the PangoLayout should be wrapped */
+
+    pango_layout_get_extents (layout, NULL, &logical);
+    width_text = PANGO_PIXELS (logical.width);
 
     width_max = priv->is_timed ? HILDON_BANNER_LABEL_MAX_TIMED
         : HILDON_BANNER_LABEL_MAX_PROGRESS;
 
-    if (width_text >= width_max) {
-        /* explicitly request maximum size to force wrapping */
-        PangoRectangle logical;
+    /* If the width of the label is going to exceed the maximum allowed
+     * width, enforce the maximum allowed width now.
+     */
+    if (priv->has_been_wrapped
+        || width_text >= width_max) {
+        /* Force wrapping by setting the maximum size */
+        width = width_max;
 
-        pango_layout_set_width (layout, width_max * PANGO_SCALE);
-        pango_layout_get_extents (layout, NULL, &logical);
+        priv->has_been_wrapped = TRUE;
+    }
+
+    /* Make the label update its layout; and update our layout pointer
+     * because the layout will be cleared and refreshed.
+     */
+    gtk_widget_set_size_request (GTK_WIDGET (label), width, height);
+    gtk_widget_size_request (GTK_WIDGET (label), &requisition);
+
+    layout = gtk_label_get_layout (label);
+
+    /* If the layout has now been wrapped and exceeds 3 lines, we truncate
+     * the rest of the label according to spec.
+     */
+    if (priv->has_been_truncated
+        || (pango_layout_is_wrapped (layout)
+            && pango_layout_get_line_count (layout) > 3)) {
+        int lines;
 
-        width = PANGO_PIXELS (logical.width);
+        pango_layout_get_extents (layout, NULL, &logical);
+        lines = pango_layout_get_line_count (layout);
+
+        /* This calculation assumes that the same font is used
+         * throughout the banner -- this is usually the case on maemo
+         *
+         * FIXME: Pango >= 1.20 has pango_layout_set_height().
+         */
+        height = (PANGO_PIXELS (logical.height) * 3) / lines + 1;
+        priv->has_been_truncated = TRUE;
     }
 
-    /* use fixed width when wrapping or natural one otherwise */
-    gtk_widget_set_size_request (GTK_WIDGET (label), width, -1);
+    /* Set the new width/height if applicable */
+    gtk_widget_set_size_request (GTK_WIDGET (label), width, height);
 }
 
 
@@ -592,7 +712,11 @@ hildon_banner_check_position                    (GtkWidget *widget)
         return;
     }
 
-    x = gdk_screen_width() - HILDON_BANNER_WINDOW_X - req.width;
+    if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+        x = HILDON_BANNER_WINDOW_X;
+    else
+        x = gdk_screen_width() - HILDON_BANNER_WINDOW_X - req.width;
+
     y = check_fullscreen_state (get_current_app_window ()) ? 
         HILDON_BANNER_WINDOW_FULLSCREEN_Y : HILDON_BANNER_WINDOW_Y;
 
@@ -602,12 +726,16 @@ hildon_banner_check_position                    (GtkWidget *widget)
 static void
 hildon_banner_realize                           (GtkWidget *widget)
 {
+    HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (widget);
+    g_assert (priv);
+
     /* We let the parent to init widget->window before we need it */
-    if (GTK_WIDGET_CLASS (parent_class)->realize)
-        GTK_WIDGET_CLASS (parent_class)->realize (widget);
+    if (GTK_WIDGET_CLASS (hildon_banner_parent_class)->realize)
+        GTK_WIDGET_CLASS (hildon_banner_parent_class)->realize (widget);
 
     /* We use special hint to turn the banner into information notification. */
-    gdk_window_set_type_hint (widget->window, GDK_WINDOW_TYPE_HINT_MESSAGE);
+    gdk_window_set_type_hint (widget->window, GDK_WINDOW_TYPE_HINT_NOTIFICATION);
+    gtk_window_set_transient_for (GTK_WINDOW (widget), (GtkWindow *) priv->parent);
 
     hildon_banner_check_position (widget);
 }
@@ -620,7 +748,6 @@ hildon_banner_class_init                        (HildonBannerClass *klass)
 
     object_class = G_OBJECT_CLASS (klass);
     widget_class = GTK_WIDGET_CLASS (klass);
-    parent_class = g_type_class_peek_parent (klass);
 
     /* Append private structure to class. This is more elegant than
        on g_new based approach */
@@ -628,45 +755,90 @@ hildon_banner_class_init                        (HildonBannerClass *klass)
 
     /* Override virtual methods */
     object_class->constructor = hildon_banner_constructor;
+    object_class->finalize = hildon_banner_finalize;
     object_class->set_property = hildon_banner_set_property;
     object_class->get_property = hildon_banner_get_property;
     GTK_OBJECT_CLASS (klass)->destroy = hildon_banner_destroy;
     widget_class->map_event = hildon_banner_map_event;
     widget_class->realize = hildon_banner_realize;
+    widget_class->button_press_event = hildon_banner_button_press_event;
+#if defined(MAEMO_GTK)
+    widget_class->client_event = hildon_banner_client_event;
+#endif
 
     /* Install properties.
        We need construct properties for singleton purposes */
+
+    /**
+     * HildonBanner:parent-window:
+     *
+     * The window for which the banner will be singleton. 
+     *                      
+     */
     g_object_class_install_property (object_class, PROP_PARENT_WINDOW,
             g_param_spec_object ("parent-window",
                 "Parent window",
                 "The window for which the banner will be singleton",
                 GTK_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
+    /**
+     * HildonBanner:is-timed:
+     *
+     * Whether the banner is timed and goes away automatically.
+     *                      
+     */
     g_object_class_install_property (object_class, PROP_IS_TIMED,
             g_param_spec_boolean ("is-timed",
                 "Is timed",
                 "Whether or not the notification goes away automatically "
                 "after the specified time has passed",
                 FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+    /**
+     * HildonBanner:timeout:
+     *
+     * The time before making the banner banner go away. This needs 
+     * to be adjusted before the banner is mapped to the screen.
+     *                      
+     */
+    g_object_class_install_property (object_class, PROP_TIMEOUT,
+            g_param_spec_uint ("timeout",
+                "Timeout",
+                "The time before making the banner banner go away",
+                0,
+                10000,
+                HILDON_BANNER_DEFAULT_TIMEOUT,
+                G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void 
 hildon_banner_init                              (HildonBanner *self)
 {
     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
-    g_assert (self);
+    g_assert (priv);
+
+    priv->parent = NULL;
 
     /* Initialize the common layout inside banner */
     priv->layout = gtk_hbox_new (FALSE, HILDON_MARGIN_DEFAULT);
 
     priv->label = g_object_new (GTK_TYPE_LABEL, NULL);
     gtk_label_set_line_wrap (GTK_LABEL (priv->label), TRUE);
+    gtk_label_set_line_wrap_mode (GTK_LABEL (priv->label), PANGO_WRAP_WORD_CHAR);
 
     gtk_container_set_border_width (GTK_CONTAINER (priv->layout), HILDON_MARGIN_DEFAULT);
     gtk_container_add (GTK_CONTAINER (self), priv->layout);
     gtk_box_pack_start (GTK_BOX (priv->layout), priv->label, TRUE, TRUE, 0);
 
     gtk_window_set_accept_focus (GTK_WINDOW (self), FALSE);
+
+#if defined(MAEMO_GTK)
+    gtk_window_set_is_temporary (GTK_WINDOW (self), TRUE);
+#endif
+
+    hildon_banner_reset_wrap_state (self);
+
+    gtk_widget_add_events (GTK_WIDGET (self), GDK_BUTTON_PRESS_MASK);
 }
 
 /* Makes sure that icon/progress item contains the desired type
@@ -684,7 +856,6 @@ hildon_banner_ensure_child                      (HildonBanner *self,
     va_list args;
     HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self);
 
-    g_assert (HILDON_IS_BANNER (self));
     g_assert (priv);
 
     widget = priv->main_item;
@@ -700,7 +871,7 @@ hildon_banner_ensure_child                      (HildonBanner *self,
         /* We have to abandon old content widget */
         if (widget)
             gtk_container_remove (GTK_CONTAINER (priv->layout), widget);
-
+        
         /* Use user provided widget or create a new one */
         priv->main_item = widget = user_widget ? 
             user_widget : GTK_WIDGET (g_object_new_valist(type, first_property, args));
@@ -720,15 +891,14 @@ hildon_banner_get_instance_for_widget           (GtkWidget *widget,
 {
     GtkWidget *window;
 
-    g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
     window = widget ? gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW) : NULL;
     return g_object_new (HILDON_TYPE_BANNER, "parent-window", window, "is-timed", timed, NULL);
 }
 
 /**
  * hildon_banner_show_information:
- * @widget: the #GtkWidget that wants to display banner
- * @icon_name: the name of icon to use. Can be %NULL for default icon.
+ * @widget: the #GtkWidget that is the owner of the banner
+ * @icon_name: the name of icon to use. Can be %NULL for default icon
  * @text: Text to display
  *
  * This function creates and displays an information banner that
@@ -737,16 +907,18 @@ hildon_banner_get_instance_for_widget           (GtkWidget *widget,
  * spawn a new banner before the earlier one has timed out, the
  * previous one will be replaced.
  *
+ * Returns: The newly created banner
+ *
  */
-void 
+GtkWidget*
 hildon_banner_show_information                  (GtkWidget *widget, 
                                                  const gchar *icon_name,
                                                  const gchar *text)
 {
     HildonBanner *banner;
 
-    g_return_if_fail (icon_name == NULL || icon_name[0] != 0);
-    g_return_if_fail (text != NULL);
+    g_return_val_if_fail (icon_name == NULL || icon_name[0] != 0, NULL);
+    g_return_val_if_fail (text != NULL, NULL);
 
     /* Prepare banner */
     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
@@ -761,26 +933,44 @@ hildon_banner_show_information                  (GtkWidget *widget,
 
     /* Show the banner, since caller cannot do that */
     gtk_widget_show_all (GTK_WIDGET (banner));
+    gtk_window_present (GTK_WINDOW (banner));
+
+    return (GtkWidget *) banner;
 }
 
-void       
+/**
+ * hildon_banner_show_informationf:
+ * @widget: the #GtkWidget that is the owner of the banner
+ * @icon_name: the name of icon to use. Can be %NULL for default icon
+ * @format: a printf-like format string
+ * @Varargs: arguments for the format string
+ *
+ * A helper function for #hildon_banner_show_information with
+ * string formatting.
+ *
+ * Returns: the newly created banner
+ */
+GtkWidget* 
 hildon_banner_show_informationf                 (GtkWidget *widget, 
                                                  const gchar *icon_name,
                                                  const gchar *format, 
                                                  ...)
 {
-    g_return_if_fail (format != NULL);
+    g_return_val_if_fail (format != NULL, NULL);
 
     gchar *message;
     va_list args;
+    GtkWidget *banner;
 
     va_start (args, format);
     message = g_strdup_vprintf (format, args);
     va_end (args);
 
-    hildon_banner_show_information (widget, icon_name, message);
+    banner = hildon_banner_show_information (widget, icon_name, message);
 
     g_free (message);
+
+    return banner;
 }
 
 /**
@@ -795,16 +985,18 @@ hildon_banner_show_informationf                 (GtkWidget *widget,
  * spawn a new banner before the earlier one has timed out, the
  * previous one will be replaced.
  *
+ * Returns: the newly created banner
+ *
  */
-void 
+GtkWidget*
 hildon_banner_show_information_with_markup      (GtkWidget *widget, 
                                                  const gchar *icon_name, 
                                                  const gchar *markup)
 {
     HildonBanner *banner;
 
-    g_return_if_fail (icon_name == NULL || icon_name[0] != 0);
-    g_return_if_fail (markup != NULL);
+    g_return_val_if_fail (icon_name == NULL || icon_name[0] != 0, NULL);
+    g_return_val_if_fail (markup != NULL, NULL);
 
     /* Prepare banner */
     banner = hildon_banner_get_instance_for_widget (widget, TRUE);
@@ -820,6 +1012,9 @@ hildon_banner_show_information_with_markup      (GtkWidget *widget,
 
     /* Show the banner, since caller cannot do that */
     gtk_widget_show_all (GTK_WIDGET (banner));
+    gtk_window_present (GTK_WINDOW (banner));
+
+    return (GtkWidget *) banner;
 }
 
 /**
@@ -846,7 +1041,7 @@ hildon_banner_show_information_with_markup      (GtkWidget *widget,
  * followed by #g_object_unref (in this order).
  * 
  * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy
- *          once you are ready with the banner.
+ *          once you are done with the banner.
  *
  */
 GtkWidget*
@@ -890,6 +1085,7 @@ hildon_banner_show_animation                    (GtkWidget *widget,
 
     /* And show it */
     gtk_widget_show_all (GTK_WIDGET (banner));
+    gtk_window_present (GTK_WINDOW (banner));
 
     return (GtkWidget *) banner;
 }
@@ -905,7 +1101,7 @@ hildon_banner_show_animation                    (GtkWidget *widget,
  * for more information.
  * 
  * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy
- *          once you are ready with the banner.
+ *          once you are done with the banner.
  *
  */
 GtkWidget*
@@ -934,6 +1130,7 @@ hildon_banner_show_progress                     (GtkWidget *widget,
 
     /* Show the banner */
     gtk_widget_show_all (GTK_WIDGET (banner));
+    gtk_window_present (GTK_WINDOW (banner));
 
     return GTK_WIDGET (banner);   
 }
@@ -952,6 +1149,7 @@ hildon_banner_set_text                          (HildonBanner *self,
 {
     GtkLabel *label;
     HildonBannerPrivate *priv;
+    const gchar *existing_text;
 
     g_return_if_fail (HILDON_IS_BANNER (self));
 
@@ -959,7 +1157,14 @@ hildon_banner_set_text                          (HildonBanner *self,
     g_assert (priv);
 
     label = GTK_LABEL (priv->label);
-    gtk_label_set_text (label, text);
+    existing_text = gtk_label_get_text (label);
+
+    if (existing_text != NULL && 
+        text != NULL          &&
+        strcmp (existing_text, text) != 0) {
+            gtk_label_set_text (label, text);
+            hildon_banner_reset_wrap_state (self);
+    }
 
     hildon_banner_check_position (GTK_WIDGET (self));
 }
@@ -971,7 +1176,6 @@ hildon_banner_set_text                          (HildonBanner *self,
  *
  * Sets the text with markup that is displayed in the banner.
  *
- * Since: 0.12.8
  */
 void 
 hildon_banner_set_markup                        (HildonBanner *self, 
@@ -988,6 +1192,8 @@ hildon_banner_set_markup                        (HildonBanner *self,
     label = GTK_LABEL (priv->label);
     gtk_label_set_markup (label, markup);
 
+    hildon_banner_reset_wrap_state (self);
+
     hildon_banner_check_position (GTK_WIDGET(self));
 }
 
@@ -1016,83 +1222,83 @@ hildon_banner_set_fraction                      (HildonBanner *self,
 }
 
 /**
- * Deprecated: really, do NOT use.
+ * hildon_banner_set_timeout:
+ * @self: a #HildonBanner widget
+ * @timeout: timeout to set in miliseconds.
+ *
+ * Sets the timeout on the banner. After the given amount of miliseconds
+ * has elapsed the banner will go away. Note that settings this only makes
+ * sense on the banners that are timed and that have not been yet displayed
+ * on the screen.
+ *
  */
-void 
-hildon_gtk_label_set_text_n_lines               (GtkLabel *label, 
-                                                 const gchar *text, 
-                                                 gint max_lines)
+void
+hildon_banner_set_timeout                       (HildonBanner *self,
+                                                 guint timeout)
 {
-    /* Forces the wrapping of text into several lines and ellipsizes the rest. 
-       Similar to combination of gtk_label_set_wrap and pango ellipzation. 
-       We cannot just use those directly, since ellipzation always wins wrapping.
-
-       This means that we have to:
-     * First wrap the text
-     * Insert forced linebreaks into text
-     * Truncate the result
-
-     NOTE! This will not work with pango markup!
+    HildonBannerPrivate *priv;
 
-    FIXME: luc: DO NOT TRUNCATE the text. Use as many lines as needed.
-    Lenth of the text is under applications' responsibility.
-    Widget does not have to enforce this. */
+    g_return_if_fail (HILDON_IS_BANNER (self));
+    priv = HILDON_BANNER_GET_PRIVATE (self);
+    g_assert (priv);
 
-    PangoLayout *layout;
-    PangoLayoutLine *line;
-    GtkRequisition req;
-    GString *wrapped_text;
-    gchar *line_data;
-    gint lines, i;
+    priv->timeout = timeout;
+}
 
-    g_return_if_fail (GTK_IS_LABEL (label));
-    g_return_if_fail (max_lines >= 1);
+/**
+ * hildon_banner_set_icon:
+ * @self: a #HildonBanner widget
+ * @icon_name: the name of icon to use. Can be %NULL for default icon
+ *
+ * Sets the icon to be used in the banner.
+ *
+ */
+void 
+hildon_banner_set_icon                          (HildonBanner *self, 
+                                                 const gchar  *icon_name)
+{
+    HildonBannerPrivate *priv;
 
-    /* Setup the label to contain the new data */
-    gtk_label_set_text (label, text);
-    gtk_label_set_line_wrap (label, TRUE);
-    gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_NONE);
+    g_return_if_fail (HILDON_IS_BANNER (self));
+    priv = HILDON_BANNER_GET_PRIVATE (self);
+    g_assert (priv);
 
-    /* We really want to recalculate the size, not use some old values */
-    gtk_widget_size_request (GTK_WIDGET (label), &req);
-    layout = gtk_label_get_layout (label);
-    lines = pango_layout_get_line_count (layout);
+    hildon_banner_ensure_child (self, NULL, 0, GTK_TYPE_IMAGE, 
+            "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
+            "icon-name", icon_name ? icon_name : HILDON_BANNER_DEFAULT_ICON,
+            "yalign", 0.0, 
+            NULL);
+}
 
-    /* Now collect the wrapped text. */
-    wrapped_text = g_string_new (NULL);
+/**
+ * hildon_banner_set_icon_from_file:
+ * @self: a #HildonBanner widget
+ * @icon_file: the filename of icon to use. Can be %NULL for default icon
+ *
+ * Sets the icon from its filename to be used in the banner.
+ *
+ */
+void 
+hildon_banner_set_icon_from_file                (HildonBanner *self, 
+                                                 const gchar  *icon_file)
+{
+    HildonBannerPrivate *priv;
 
-    for (i = 0; i < lines; i++)
-    {
-        /* Append the next line into wrapping buffer, but 
-           avoid adding extra whitespaces at the end, since those
-           can cause other lines to be ellipsized as well. */
-        line = pango_layout_get_line (layout, i);
-        line_data = g_strndup (pango_layout_get_text(layout) + line->start_index, 
-                line->length);
-        g_strchomp (line_data);
-        g_string_append (wrapped_text, line_data);
-
-        /* Append forced linebreaks, until we have the desired
-           amount of lines. After that we put the rest to the
-           last line to make ellipzation to happen */
-        if (i < lines - 1)
-        {
-            if (i < max_lines - 1)
-                g_string_append_c (wrapped_text, '\n');
-            else
-                g_string_append_c (wrapped_text, ' ');
-        }
+    g_return_if_fail (HILDON_IS_BANNER (self));
+    priv = HILDON_BANNER_GET_PRIVATE (self);
+    g_assert (priv);
 
-        g_free(line_data);
+    if (icon_file != NULL) {
+        hildon_banner_ensure_child (self, NULL, 0, GTK_TYPE_IMAGE, 
+                "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
+                "file", icon_file,
+                "yalign", 0.0, 
+                NULL);
+    } else {
+        hildon_banner_ensure_child (self, NULL, 0, GTK_TYPE_IMAGE, 
+                "pixel-size", HILDON_ICON_PIXEL_SIZE_NOTE, 
+                "icon-name", HILDON_BANNER_DEFAULT_ICON,
+                "yalign", 0.0, 
+                NULL);
     }
-
-    /* Now update the label to use wrapped text. Use builtin
-       ellipzation as well. */
-    gtk_widget_set_size_request (GTK_WIDGET (label), req.width, -1);
-    gtk_label_set_text (label, wrapped_text->str);
-    gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END);
-    gtk_label_set_line_wrap (label, FALSE);
-
-    g_string_free (wrapped_text, TRUE);
 }
-