diff options
author | Alberto Garcia <agarcia@igalia.com> | 2009-04-30 13:10:26 +0200 |
---|---|---|
committer | Alberto Garcia <agarcia@igalia.com> | 2009-04-30 13:33:58 +0200 |
commit | 69a11338fe0cc36a868e5812b590f5437622988a (patch) | |
tree | abd1efc835e7e30f7799eac64ff45405f1624528 /hildon/hildon-banner.c | |
parent | 8a970bcc0a22925ed90b869c8f7c1ba521e189ac (diff) |
Renamed src/ to hildon/
* src/
* Makefile.am
* configure.ac
* doc/Makefile.am
* doc/hildon.types
* po/POTFILES.in:
Renamed src/ to hildon/
* examples/*.c
* debian/changelog
* debian/rules:
Include <hildon/hildon.h> directly in all examples.
Diffstat (limited to 'hildon/hildon-banner.c')
-rw-r--r-- | hildon/hildon-banner.c | 1238 |
1 files changed, 1238 insertions, 0 deletions
diff --git a/hildon/hildon-banner.c b/hildon/hildon-banner.c new file mode 100644 index 0000000..0d65edc --- /dev/null +++ b/hildon/hildon-banner.c @@ -0,0 +1,1238 @@ +/* + * This file is a part of hildon + * + * Copyright (C) 2005, 2006, 2007 Nokia Corporation, all rights reserved. + * + * Contact: Rodrigo Novo <rodrigo.novo@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, 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +/** + * SECTION:hildon-banner + * @short_description: A widget used to display timed notifications. + * + * #HildonBanner is a small, pop-up window that can be used to display + * a short, timed notification or information to the user. It can + * communicate that a task has been finished or that the application + * state has changed. + * + * Hildon provides convenient funtions to create and show banners. To + * create and show information banners you can use + * hildon_banner_show_information(), hildon_banner_show_informationf() + * or hildon_banner_show_information_with_markup(). + * + * Two more kinds of banners are maintained for backward compatibility + * but are no longer recommended in Hildon 2.2. These are the animated + * banner (created with hildon_banner_show_animation()) and the + * progress banner (created with hildon_banner_show_progress()). See + * hildon_gtk_window_set_progress_indicator() for the preferred way of + * showing progress notifications in Hildon 2.2. + * + * Information banners dissapear automatically after a certain + * period. This is stored in the #HildonBanner:timeout property (in + * miliseconds), and can be changed using hildon_banner_set_timeout(). + * + * Note that #HildonBanner<!-- -->s should only be used to display + * non-critical pieces of information. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <X11/Xatom.h> +#include <gdk/gdkx.h> + +#undef HILDON_DISABLE_DEPRECATED + +#include "hildon-banner.h" +#include "hildon-banner-private.h" +#include "hildon-defines.h" + +/* position relative to the screen */ + +#define HILDON_BANNER_WINDOW_X 0 + +#define HILDON_BANNER_WINDOW_Y HILDON_WINDOW_TITLEBAR_HEIGHT + +/* max widths */ + +#define HILDON_BANNER_PROGRESS_WIDTH 104 + +#define HILDON_BANNER_LABEL_MAX_TIMED \ + (800 - ((HILDON_MARGIN_TRIPLE) * 2)) + +#define HILDON_BANNER_LABEL_MAX_PROGRESS 375 /*265*/ + +/* default timeout */ + +#define HILDON_BANNER_DEFAULT_TIMEOUT 3000 + +/* default icons */ + +#define HILDON_BANNER_DEFAULT_PROGRESS_ANIMATION "indicator_update" + +/* animation related stuff */ + +#define HILDON_BANNER_ANIMATION_FRAMERATE ((float)1000/150) + +#define HILDON_BANNER_ANIMATION_TMPL "indicator_update%d" + +#define HILDON_BANNER_ANIMATION_NFRAMES 8 + +enum +{ + PROP_0, + PROP_PARENT_WINDOW, + PROP_IS_TIMED, + PROP_TIMEOUT +}; + +static GtkWidget* global_timed_banner = NULL; + +static GQuark +hildon_banner_timed_quark (void); + +static void +hildon_banner_bind_style (HildonBanner *self, + const gchar *name); + +static gboolean +hildon_banner_timeout (gpointer data); + +static gboolean +hildon_banner_clear_timeout (HildonBanner *self); + +static void +hildon_banner_ensure_timeout (HildonBanner *self); + +static void +hildon_banner_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); + +static void +hildon_banner_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void +hildon_banner_destroy (GtkObject *object); + +static GObject* +hildon_banner_real_get_instance (GObject *window, + gboolean timed); + +static GObject* +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); + +static void +hildon_banner_check_position (GtkWidget *widget); + +static void +hildon_banner_realize (GtkWidget *widget); + +static void +hildon_banner_class_init (HildonBannerClass *klass); + +static void +hildon_banner_init (HildonBanner *self); + +static void +hildon_banner_ensure_child (HildonBanner *self, + GtkWidget *user_widget, + guint pos, + GType type, + const gchar *first_property, + ...); + +static HildonBanner* +hildon_banner_get_instance_for_widget (GtkWidget *widget, + gboolean timed); + +G_DEFINE_TYPE (HildonBanner, hildon_banner, GTK_TYPE_WINDOW) + +static GQuark +hildon_banner_timed_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY(quark == 0)) + quark = g_quark_from_static_string ("hildon-banner-timed"); + + return quark; +} + +/* Set the widget and label name to make the correct rc-style attached into them */ +static void +hildon_banner_bind_style (HildonBanner *self, + const gchar *name_sufix) +{ + HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self); + gchar *name; + + g_assert (priv); + + name = g_strconcat ("HildonBannerLabel-", name_sufix, NULL); + gtk_widget_set_name (priv->label, name); + g_free (name); + + name = g_strconcat ("HildonBanner-", name_sufix, NULL); + gtk_widget_set_name (GTK_WIDGET (self), name); + g_free (name); +} + +/* 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; + gboolean continue_timeout = FALSE; + + GDK_THREADS_ENTER (); + + g_assert (HILDON_IS_BANNER (data)); + + widget = GTK_WIDGET (data); + g_object_ref (widget); + + continue_timeout = simulate_close (widget); + + if (! continue_timeout) { + HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (data); + priv->timeout_id = 0; + gtk_widget_destroy (widget); + } + + g_object_unref (widget); + + GDK_THREADS_LEAVE (); + + return continue_timeout; +} + +static gboolean +hildon_banner_clear_timeout (HildonBanner *self) +{ + HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self); + g_assert (priv); + + if (priv->timeout_id != 0) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + return TRUE; + } + + return FALSE; +} + +static void +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 > 0) + priv->timeout_id = g_timeout_add (priv->timeout, + hildon_banner_timeout, self); +} + +static void +hildon_banner_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkWidget *window; + HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object); + g_assert (priv); + + 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); + break; + + 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) { + gtk_window_set_destroy_with_parent (GTK_WINDOW (object), TRUE); + g_object_add_weak_pointer(G_OBJECT (window), (gpointer) &priv->parent); + } + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +hildon_banner_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object); + g_assert (priv); + + 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; + + case PROP_PARENT_WINDOW: + g_value_set_object (value, gtk_window_get_transient_for (GTK_WINDOW (object))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +hildon_banner_destroy (GtkObject *object) +{ + HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (object); + g_assert (priv); + + HildonBanner *self; + GObject *parent_window = (GObject *) priv->parent; + + g_assert (HILDON_IS_BANNER (object)); + self = HILDON_BANNER (object); + + /* Drop possible global pointer. That can hold reference to us */ + if ((gpointer) object == (gpointer) global_timed_banner) { + global_timed_banner = NULL; + g_object_unref (object); + } + + /* Remove the data from parent window for timed banners. Those hold reference */ + 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 (hildon_banner_parent_class)->destroy) + GTK_OBJECT_CLASS (hildon_banner_parent_class)->destroy (object); +} + +/* Search a previous banner instance */ +static GObject* +hildon_banner_real_get_instance (GObject *window, + gboolean timed) +{ + if (timed) { + /* If we have a parent window, the previous instance is stored there */ + 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; + } + + /* Non-timed banners are normal (non-singleton) objects */ + return NULL; +} + +/* By overriding constructor we force timed banners to be + singletons for each window */ +static GObject* +hildon_banner_constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *banner, *window = NULL; + gboolean timed = FALSE; + guint i; + + /* Search banner type information from parameters in order + to locate the possible previous banner instance. */ + for (i = 0; i < n_construct_params; i++) + { + if (strcmp(construct_params[i].pspec->name, "parent-window") == 0) + window = g_value_get_object (construct_params[i].value); + else if (strcmp(construct_params[i].pspec->name, "is-timed") == 0) + timed = g_value_get_boolean (construct_params[i].value); + } + + /* Try to get a previous instance if such exists */ + banner = hildon_banner_real_get_instance (window, timed); + if (! banner) + { + /* We have to create a new banner */ + 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. */ + if (timed) { + if (window) { + g_object_set_qdata_full (G_OBJECT (window), hildon_banner_timed_quark (), + g_object_ref (banner), g_object_unref); + } else { + g_assert (global_timed_banner == NULL); + global_timed_banner = g_object_ref (banner); + } + } + } + else { + /* FIXME: This is a hack! We have to manually freeze + notifications. This is normally done by g_object_init, but we + are not going to call that. g_object_newv will otherwise give + a critical like this: + + GLIB CRITICAL ** GLib-GObject - g_object_notify_queue_thaw: + 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 */ + if (timed && hildon_banner_clear_timeout (HILDON_BANNER (banner))) + hildon_banner_ensure_timeout (HILDON_BANNER(banner)); + + 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, but just hide the + * banner instead of destroying it, as it is already meant to + * be destroyed by hildon_banner_timeout() (if it's timed) or + * the application (if it's not). */ + gtk_widget_hide (widget); + } + + return result; +} + +#if defined(MAEMO_GTK) +static void +hildon_banner_map (GtkWidget *widget) +{ + if (GTK_WIDGET_CLASS (hildon_banner_parent_class)->map) { + /* Make the banner temporary _before_ mapping it, to avoid closing + * other temporary windows */ + gtk_window_set_is_temporary (GTK_WINDOW (widget), TRUE); + + GTK_WIDGET_CLASS (hildon_banner_parent_class)->map (widget); + + /* Make the banner non-temporary _after_ mapping it, to avoid + * being closed by other non-temporary windows */ + gtk_window_set_is_temporary (GTK_WINDOW (widget), FALSE); + + hildon_banner_check_position (widget); + } +} +#endif + +/* We start the timer for timed notifications after the window appears on screen */ +static gboolean +hildon_banner_map_event (GtkWidget *widget, + GdkEventAny *event) +{ + gboolean result = FALSE; + + 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; +} + +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 +force_to_wrap_truncated (HildonBanner *banner) +{ + GtkLabel *label; + 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_return_if_fail (priv); + + label = GTK_LABEL (priv->label); + + layout = gtk_label_get_layout (label); + + 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 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 + || pango_layout_is_wrapped (layout)) { + /* Force wrapping by setting the maximum size */ + width = width_max; + + 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; + + 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; + } + + /* Set the new width/height if applicable */ + gtk_widget_set_size_request (GTK_WIDGET (label), width, height); +} + + +static void +hildon_banner_check_position (GtkWidget *widget) +{ + gint x, y; + GtkRequisition req; + + gtk_widget_set_size_request (widget, gdk_screen_width (), -1); + + force_to_wrap_truncated (HILDON_BANNER(widget)); /* see N#27000 and G#329646 */ + + gtk_widget_size_request (widget, &req); + + if (req.width == 0) + { + return; + } + + x = HILDON_BANNER_WINDOW_X; + + y = HILDON_BANNER_WINDOW_Y; + + gtk_window_move (GTK_WINDOW (widget), x, y); +} + +static void +hildon_banner_realize (GtkWidget *widget) +{ + GdkWindow *gdkwin; + GdkAtom atom; + guint32 portrait = 1; + const gchar *notification_type = "_HILDON_NOTIFICATION_TYPE_BANNER"; + 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 (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_NOTIFICATION); + gtk_window_set_transient_for (GTK_WINDOW (widget), (GtkWindow *) priv->parent); + + hildon_banner_check_position (widget); + + gdkwin = widget->window; + + /* Set the _HILDON_NOTIFICATION_TYPE property so Matchbox places the window correctly */ + atom = gdk_atom_intern ("_HILDON_NOTIFICATION_TYPE", FALSE); + gdk_property_change (gdkwin, atom, gdk_x11_xatom_to_atom (XA_STRING), 8, GDK_PROP_MODE_REPLACE, + (gpointer) notification_type, strlen (notification_type)); + + /* HildonBanner supports portrait mode */ + atom = gdk_atom_intern ("_HILDON_PORTRAIT_MODE_SUPPORT", FALSE); + gdk_property_change (gdkwin, atom, gdk_x11_xatom_to_atom (XA_CARDINAL), 32, + GDK_PROP_MODE_REPLACE, (gpointer) &portrait, 1); +} + +static void +hildon_banner_class_init (HildonBannerClass *klass) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = G_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + + /* Append private structure to class. This is more elegant than + on g_new based approach */ + g_type_class_add_private (klass, sizeof (HildonBannerPrivate)); + + /* 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->map = hildon_banner_map; +#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 (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); + + 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 + of item. If possible, tries to avoid creating a new widget but + reuses the existing one */ +static void +hildon_banner_ensure_child (HildonBanner *self, + GtkWidget *user_widget, + guint pos, + GType type, + const gchar *first_property, + ...) +{ + GtkWidget *widget; + va_list args; + HildonBannerPrivate *priv = HILDON_BANNER_GET_PRIVATE (self); + + g_assert (priv); + + widget = priv->main_item; + va_start (args, first_property); + + /* Reuse existing widget if possible */ + if (! user_widget && G_TYPE_CHECK_INSTANCE_TYPE (widget, type)) + { + g_object_set_valist (G_OBJECT (widget), first_property, args); + } + else + { + /* 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)); + gtk_box_pack_start (GTK_BOX (priv->layout), widget, TRUE, TRUE, 0); + } + + /* We make sure that the widget exists in desired position. Different + banners place this child widget to different places */ + gtk_box_reorder_child (GTK_BOX (priv->layout), widget, pos); + va_end (args); +} + +/* Creates a new banner instance or uses an existing one */ +static HildonBanner* +hildon_banner_get_instance_for_widget (GtkWidget *widget, + gboolean timed) +{ + GtkWidget *window; + + 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); +} + +static GtkWidget * +hildon_banner_create_animation (void) +{ + GtkWidget *image; + GdkPixbufSimpleAnim *anim; + GdkPixbuf *frame; + GtkIconTheme *theme; + GError *error = NULL; + gchar *icon_name; + gint i; + + anim = gdk_pixbuf_simple_anim_new (HILDON_ICON_PIXEL_SIZE_STYLUS, + HILDON_ICON_PIXEL_SIZE_STYLUS, + HILDON_BANNER_ANIMATION_FRAMERATE); + gdk_pixbuf_simple_anim_set_loop (anim, TRUE); + theme = gtk_icon_theme_get_default (); + + for (i = 1; i <= HILDON_BANNER_ANIMATION_NFRAMES; i++) { + icon_name = g_strdup_printf (HILDON_BANNER_ANIMATION_TMPL, i); + frame = gtk_icon_theme_load_icon (theme, icon_name, HILDON_ICON_PIXEL_SIZE_STYLUS, + 0, &error); + + if (error) { + g_warning ("Icon theme lookup for icon `%s' failed: %s", + icon_name, error->message); + g_error_free (error); + error = NULL; + } else { + gdk_pixbuf_simple_anim_add_frame (anim, frame); + } + + g_object_unref (frame); + g_free (icon_name); + } + + image = gtk_image_new_from_animation (GDK_PIXBUF_ANIMATION (anim)); + g_object_unref (anim); + + return image; +} + +/** + * hildon_banner_show_information: + * @widget: the #GtkWidget that is the owner of the banner + * @icon_name: since Hildon 2.2 this parameter is not used anymore and + * any value that you pass will be ignored + * @text: Text to display + * + * This function creates and displays an information banner that + * automatically goes away after certain time period. For each window + * in your application there can only be one timed banner, so if you + * spawn a new banner before the earlier one has timed out, the + * previous one will be replaced. + * + * Returns: The newly created banner + * + */ +GtkWidget* +hildon_banner_show_information (GtkWidget *widget, + const gchar *icon_name, + const gchar *text) +{ + HildonBanner *banner; + + g_return_val_if_fail (text != NULL, NULL); + + /* Prepare banner */ + banner = hildon_banner_get_instance_for_widget (widget, TRUE); + + hildon_banner_set_text (banner, text); + hildon_banner_bind_style (banner, "information"); + + /* Show the banner, since caller cannot do that */ + gtk_widget_show_all (GTK_WIDGET (banner)); + + return (GtkWidget *) banner; +} + +/** + * hildon_banner_show_informationf: + * @widget: the #GtkWidget that is the owner of the banner + * @icon_name: since Hildon 2.2 this parameter is not used anymore and + * any value that you pass will be ignored + * @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_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); + + banner = hildon_banner_show_information (widget, icon_name, message); + + g_free (message); + + return banner; +} + +/** + * hildon_banner_show_information_with_markup: + * @widget: the #GtkWidget that wants to display banner + * @icon_name: since Hildon 2.2 this parameter is not used anymore and + * any value that you pass will be ignored + * @markup: a markup string to display (see <link linkend="PangoMarkupFormat">Pango markup format</link>) + * + * This function creates and displays an information banner that + * automatically goes away after certain time period. For each window + * in your application there can only be one timed banner, so if you + * spawn a new banner before the earlier one has timed out, the + * previous one will be replaced. + * + * Returns: the newly created banner + * + */ +GtkWidget* +hildon_banner_show_information_with_markup (GtkWidget *widget, + const gchar *icon_name, + const gchar *markup) +{ + HildonBanner *banner; + + 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); + + hildon_banner_set_markup (banner, markup); + hildon_banner_bind_style (banner, "information"); + + /* Show the banner, since caller cannot do that */ + gtk_widget_show_all (GTK_WIDGET (banner)); + + return (GtkWidget *) banner; +} + +/** + * hildon_banner_show_animation: + * @widget: the #GtkWidget that wants to display banner + * @animation_name: since Hildon 2.2 this parameter is not used + * anymore and any value that you pass will be + * ignored + * @text: the text to display. + * + * Shows an animated progress notification. It's recommended not to try + * to show more than one progress notification at a time, since + * they will appear on top of each other. You can use progress + * notifications with timed banners. In this case the banners are + * located so that you can somehow see both. + * + * Please note that banners are destroyed automatically once the + * window they are attached to is closed. The pointer that you receive + * with this function does not contain additional references, so it + * can become invalid without warning (this is true for all toplevel + * windows in gtk). To make sure that the banner does not disappear + * automatically, you can separately ref the return value (this + * doesn't prevent the banner from disappearing, just the object from + * being finalized). In this case you have to call both + * gtk_widget_destroy() followed by g_object_unref() (in this order). + * + * Returns: a #HildonBanner widget. You must call gtk_widget_destroy() + * once you are done with the banner. + * + * Deprecated: Hildon 2.2: use + * hildon_gtk_window_set_progress_indicator() instead. + */ +GtkWidget* +hildon_banner_show_animation (GtkWidget *widget, + const gchar *animation_name, + const gchar *text) +{ + HildonBanner *banner; + GtkWidget *image_widget; + + g_return_val_if_fail (text != NULL, NULL); + + image_widget = hildon_banner_create_animation (); + + /* Prepare banner */ + banner = hildon_banner_get_instance_for_widget (widget, FALSE); + hildon_banner_ensure_child (banner, image_widget, 0, + GTK_TYPE_IMAGE, "yalign", 0.0, NULL); + + hildon_banner_set_text (banner, text); + hildon_banner_bind_style (banner, "animation"); + + /* And show it */ + gtk_widget_show_all (GTK_WIDGET (banner)); + + return (GtkWidget *) banner; +} + +/** + * hildon_banner_show_progress: + * @widget: the #GtkWidget that wants to display banner + * @bar: Progressbar to use. You usually can just pass %NULL, unless + * you want somehow customized progress bar. + * @text: text to display. + * + * Shows progress notification. See #hildon_banner_show_animation + * for more information. + * + * Returns: a #HildonBanner widget. You must call #gtk_widget_destroy + * once you are done with the banner. + * + * Deprecated: Hildon 2.2: use hildon_gtk_window_set_progress_indicator() instead. + */ +GtkWidget* +hildon_banner_show_progress (GtkWidget *widget, + GtkProgressBar *bar, + const gchar *text) +{ + HildonBanner *banner; + HildonBannerPrivate *priv; + + g_return_val_if_fail (bar == NULL || GTK_IS_PROGRESS_BAR (bar), NULL); + g_return_val_if_fail (text != NULL, NULL); + + + /* Prepare banner */ + banner = hildon_banner_get_instance_for_widget (widget, FALSE); + priv = HILDON_BANNER_GET_PRIVATE (banner); + g_assert (priv); + hildon_banner_ensure_child (banner, (GtkWidget *) bar, -1, GTK_TYPE_PROGRESS_BAR, NULL); + + gtk_widget_set_size_request (priv->main_item, + HILDON_BANNER_PROGRESS_WIDTH, -1); + + hildon_banner_set_text (banner, text); + hildon_banner_bind_style (banner, "progress"); + + /* Show the banner */ + gtk_widget_show_all (GTK_WIDGET (banner)); + + return GTK_WIDGET (banner); +} + +/** + * hildon_banner_set_text: + * @self: a #HildonBanner widget + * @text: a new text to display in banner + * + * Sets the text that is displayed in the banner. + * + */ +void +hildon_banner_set_text (HildonBanner *self, + const gchar *text) +{ + GtkLabel *label; + HildonBannerPrivate *priv; + const gchar *existing_text; + + g_return_if_fail (HILDON_IS_BANNER (self)); + + priv = HILDON_BANNER_GET_PRIVATE (self); + g_assert (priv); + + label = GTK_LABEL (priv->label); + 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)); +} + +/** + * hildon_banner_set_markup: + * @self: a #HildonBanner widget + * @markup: a new text with Pango markup to display in the banner + * + * Sets the text with markup that is displayed in the banner. + * + */ +void +hildon_banner_set_markup (HildonBanner *self, + const gchar *markup) +{ + GtkLabel *label; + HildonBannerPrivate *priv; + + g_return_if_fail (HILDON_IS_BANNER (self)); + + priv = HILDON_BANNER_GET_PRIVATE (self); + g_assert (priv); + + label = GTK_LABEL (priv->label); + gtk_label_set_markup (label, markup); + + hildon_banner_reset_wrap_state (self); + + hildon_banner_check_position (GTK_WIDGET(self)); +} + +/** + * hildon_banner_set_fraction: + * @self: a #HildonBanner widget + * @fraction: #gdouble + * + * The fraction is the completion of progressbar, + * the scale is from 0.0 to 1.0. + * Sets the amount of fraction the progressbar has. + * + * Note that this method only has effect if @self was created with + * hildon_banner_show_progress() + * + */ +void +hildon_banner_set_fraction (HildonBanner *self, + gdouble fraction) +{ + HildonBannerPrivate *priv; + + g_return_if_fail (HILDON_IS_BANNER (self)); + priv = HILDON_BANNER_GET_PRIVATE (self); + g_assert (priv); + + g_return_if_fail (GTK_IS_PROGRESS_BAR (priv->main_item)); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->main_item), fraction); +} + +/** + * 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. + * + * Note that this method only has effect if @self is an information + * banner (created using hildon_banner_show_information() and + * friends). + */ +void +hildon_banner_set_timeout (HildonBanner *self, + guint timeout) +{ + HildonBannerPrivate *priv; + + g_return_if_fail (HILDON_IS_BANNER (self)); + priv = HILDON_BANNER_GET_PRIVATE (self); + g_assert (priv); + + priv->timeout = timeout; +} + +/** + * 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. + * + * Deprecated: This function does nothing. As of hildon 2.2, hildon + * banners don't allow changing their icons. + */ +void +hildon_banner_set_icon (HildonBanner *self, + const gchar *icon_name) +{ +} + +/** + * 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. + * + * Deprecated: This function does nothing. As of hildon 2.2, hildon + * banners don't allow changing their icons. + */ +void +hildon_banner_set_icon_from_file (HildonBanner *self, + const gchar *icon_file) +{ +} |