2008-04-15 13:21:13 <timj@imendio.com>
[hildon] / src / hildon-banner.c
index 6f596ca..b2fa6c3 100644 (file)
@@ -133,9 +133,15 @@ hildon_banner_constructor                       (GType type,
 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);
@@ -164,11 +170,6 @@ static HildonBanner*
 hildon_banner_get_instance_for_widget           (GtkWidget *widget, 
                                                  gboolean timed);
 
-static gint
-hildon_banner_delete_event                      (GtkWidget *widget,
-                                                 GdkEvent  *event);
-
-
 G_DEFINE_TYPE (HildonBanner, hildon_banner, GTK_TYPE_WINDOW)
 
 /* copy/paste from old infoprint implementation: Use matchbox 
@@ -276,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 ();
@@ -290,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);
 
@@ -520,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 */
@@ -541,6 +555,21 @@ hildon_banner_finalize                          (GObject *object)
     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, 
@@ -579,6 +608,25 @@ hildon_banner_client_event                      (GtkWidget *widget,
 }
 #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 */
 static void 
@@ -588,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;
+    }
 
-        width = PANGO_PIXELS (logical.width);
+    /* 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;
+
+        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);
 }
 
 
@@ -630,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;
 
@@ -650,7 +736,6 @@ hildon_banner_realize                           (GtkWidget *widget)
     /* We use special hint to turn the banner into information notification. */
     gdk_window_set_type_hint (widget->window, GDK_WINDOW_TYPE_HINT_NOTIFICATION);
     gtk_window_set_transient_for (GTK_WINDOW (widget), (GtkWindow *) priv->parent);
-    gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
 
     hildon_banner_check_position (widget);
 }
@@ -676,6 +761,7 @@ hildon_banner_class_init                        (HildonBannerClass *klass)
     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
@@ -738,6 +824,7 @@ hildon_banner_init                              (HildonBanner *self)
 
     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);
@@ -748,6 +835,10 @@ hildon_banner_init                              (HildonBanner *self)
 #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
@@ -842,6 +933,7 @@ 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;
 }
@@ -920,6 +1012,7 @@ 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;
 }
@@ -992,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;
 }
@@ -1036,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);   
 }
@@ -1066,8 +1161,10 @@ hildon_banner_set_text                          (HildonBanner *self,
 
     if (existing_text != NULL && 
         text != NULL          &&
-        strcmp (existing_text, text) != 0)
+        strcmp (existing_text, text) != 0) {
             gtk_label_set_text (label, text);
+            hildon_banner_reset_wrap_state (self);
+    }
 
     hildon_banner_check_position (GTK_WIDGET (self));
 }
@@ -1095,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));
 }