--- /dev/null
+/*
+ Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
+
+ 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; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ See the file COPYING for the full license text.
+*/
+
+#include "katze-throbber.h"
+
+#include <gtk/gtk.h>
+
+G_DEFINE_TYPE (KatzeThrobber, katze_throbber, GTK_TYPE_MISC)
+
+struct _KatzeThrobberPrivate
+{
+ GtkIconSize icon_size;
+ gchar* icon_name;
+ GdkPixbuf* pixbuf;
+ gchar* stock_id;
+ gboolean animated;
+ gchar* static_icon_name;
+ GdkPixbuf* static_pixbuf;
+ gchar* static_stock_id;
+
+ gint index;
+ gint timer_id;
+ gint width;
+ gint height;
+};
+
+#define KATZE_THROBBER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KATZE_TYPE_THROBBER, KatzeThrobberPrivate))
+
+enum
+{
+ PROP_0,
+
+ PROP_ICON_SIZE,
+ PROP_ICON_NAME,
+ PROP_PIXBUF,
+ PROP_ANIMATED,
+ PROP_STATIC_ICON_NAME,
+ PROP_STATIC_PIXBUF,
+ PROP_STATIC_STOCK_ID
+};
+
+static void
+katze_throbber_dispose(GObject* object);
+
+static void
+katze_throbber_set_property (GObject* object,
+ guint prop_id,
+ const GValue* value,
+ GParamSpec* pspec);
+
+static void
+katze_throbber_get_property (GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec);
+
+static void
+katze_throbber_destroy (GtkObject* object);
+
+static void
+katze_throbber_realize (GtkWidget* widget);
+
+static void
+katze_throbber_unrealize (GtkWidget* widget);
+
+static void
+katze_throbber_map (GtkWidget* widget);
+
+static void
+katze_throbber_unmap (GtkWidget* widget);
+
+static void
+katze_throbber_style_set (GtkWidget* widget,
+ GtkStyle* style);
+
+static void
+katze_throbber_screen_changed (GtkWidget* widget,
+ GdkScreen* screen_prev);
+
+static void
+katze_throbber_size_request (GtkWidget* widget,
+ GtkRequisition* requisition);
+
+static gboolean
+katze_throbber_expose_event (GtkWidget* widget,
+ GdkEventExpose* event);
+
+static void
+icon_theme_changed (KatzeThrobber* throbber);
+
+static gboolean
+katze_throbber_timeout (KatzeThrobber* throbber);
+
+static void
+katze_throbber_timeout_destroy (KatzeThrobber* throbber);
+
+static void
+katze_throbber_class_init (KatzeThrobberClass* class)
+{
+ GObjectClass* gobject_class = G_OBJECT_CLASS (class);
+ gobject_class->dispose = katze_throbber_dispose;
+ gobject_class->set_property = katze_throbber_set_property;
+ gobject_class->get_property = katze_throbber_get_property;
+
+ GtkObjectClass* object_class = GTK_OBJECT_CLASS (class);
+ object_class->destroy = katze_throbber_destroy;
+
+ GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (class);
+ widget_class->realize = katze_throbber_realize;
+ widget_class->unrealize = katze_throbber_unrealize;
+ widget_class->map = katze_throbber_map;
+ widget_class->unmap = katze_throbber_unmap;
+ widget_class->style_set = katze_throbber_style_set;
+ widget_class->screen_changed = katze_throbber_screen_changed;
+ widget_class->size_request = katze_throbber_size_request;
+ widget_class->expose_event = katze_throbber_expose_event;
+
+ GParamFlags flags = G_PARAM_READWRITE | G_PARAM_CONSTRUCT;
+
+ g_object_class_install_property (gobject_class,
+ PROP_ICON_SIZE,
+ g_param_spec_int (
+ "icon-size",
+ "Icon size",
+ "Symbolic size to use for the animation",
+ 0, G_MAXINT, GTK_ICON_SIZE_MENU,
+ flags));
+
+ g_object_class_install_property (gobject_class,
+ PROP_ICON_NAME,
+ g_param_spec_string (
+ "icon-name",
+ "Icon Name",
+ "The name of an icon containing animation frames",
+ "process-working",
+ flags));
+
+ g_object_class_install_property (gobject_class,
+ PROP_PIXBUF,
+ g_param_spec_object (
+ "pixbuf",
+ "Pixbuf",
+ "A GdkPixbuf containing animation frames",
+ GDK_TYPE_PIXBUF,
+ flags));
+
+ g_object_class_install_property (gobject_class,
+ PROP_ANIMATED,
+ g_param_spec_boolean (
+ "animated",
+ "Animated",
+ "Whether the throbber should be animated",
+ FALSE,
+ flags));
+
+ g_object_class_install_property (gobject_class,
+ PROP_STATIC_ICON_NAME,
+ g_param_spec_string (
+ "static-icon-name",
+ "Static Icon Name",
+ "The name of an icon to be used as the static image",
+ NULL,
+ flags));
+
+ g_object_class_install_property (gobject_class,
+ PROP_PIXBUF,
+ g_param_spec_object (
+ "static-pixbuf",
+ "Static Pixbuf",
+ "A GdkPixbuf to be used as the static image",
+ GDK_TYPE_PIXBUF,
+ flags));
+
+ g_object_class_install_property (gobject_class,
+ PROP_STATIC_STOCK_ID,
+ g_param_spec_string (
+ "static-stock-id",
+ "Static Stock ID",
+ "The stock ID of an icon to be used as the static image",
+ NULL,
+ flags));
+
+ g_type_class_add_private (object_class, sizeof (KatzeThrobberPrivate));
+}
+
+static void
+katze_throbber_init (KatzeThrobber *throbber)
+{
+ GTK_WIDGET_SET_FLAGS (throbber, GTK_NO_WINDOW);
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ priv->timer_id = -1;
+}
+
+static void
+katze_throbber_dispose (GObject *object)
+{
+ KatzeThrobber* throbber = KATZE_THROBBER (object);
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+
+ if (G_UNLIKELY (priv->timer_id >= 0))
+ g_source_remove (priv->timer_id);
+
+ (*G_OBJECT_CLASS (katze_throbber_parent_class)->dispose) (object);
+}
+
+static void
+katze_throbber_destroy (GtkObject *object)
+{
+ KatzeThrobber* throbber = KATZE_THROBBER (object);
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+
+ katze_assign (priv->icon_name, NULL);
+ if (priv->pixbuf)
+ katze_object_assign (priv->pixbuf, NULL);
+ katze_assign (priv->static_icon_name, NULL);
+ if (priv->static_pixbuf)
+ katze_object_assign (priv->static_pixbuf, NULL);
+ katze_assign (priv->static_stock_id, NULL);
+
+ GTK_OBJECT_CLASS (katze_throbber_parent_class)->destroy (object);
+}
+
+static void
+katze_throbber_set_property (GObject* object,
+ guint prop_id,
+ const GValue* value,
+ GParamSpec* pspec)
+{
+ KatzeThrobber* throbber = KATZE_THROBBER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ICON_SIZE:
+ katze_throbber_set_icon_size (throbber, g_value_get_int (value));
+ break;
+ case PROP_ICON_NAME:
+ katze_throbber_set_icon_name (throbber, g_value_get_string (value));
+ break;
+ case PROP_PIXBUF:
+ katze_throbber_set_pixbuf (throbber, g_value_get_object (value));
+ break;
+ case PROP_ANIMATED:
+ katze_throbber_set_animated (throbber, g_value_get_boolean (value));
+ break;
+ case PROP_STATIC_ICON_NAME:
+ katze_throbber_set_static_icon_name (throbber, g_value_get_string (value));
+ break;
+ case PROP_STATIC_PIXBUF:
+ katze_throbber_set_static_pixbuf (throbber, g_value_get_object (value));
+ break;
+ case PROP_STATIC_STOCK_ID:
+ katze_throbber_set_static_stock_id (throbber, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+katze_throbber_get_property (GObject* object,
+ guint prop_id,
+ GValue* value,
+ GParamSpec* pspec)
+{
+ KatzeThrobber* throbber = KATZE_THROBBER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ICON_SIZE:
+ g_value_set_int (value, katze_throbber_get_icon_size (throbber));
+ break;
+ case PROP_ICON_NAME:
+ g_value_set_string (value, katze_throbber_get_icon_name (throbber));
+ break;
+ case PROP_PIXBUF:
+ g_value_set_object (value, katze_throbber_get_pixbuf (throbber));
+ break;
+ case PROP_ANIMATED:
+ g_value_set_boolean (value, katze_throbber_get_animated (throbber));
+ break;
+ case PROP_STATIC_ICON_NAME:
+ g_value_set_string (value, katze_throbber_get_static_icon_name (throbber));
+ break;
+ case PROP_STATIC_PIXBUF:
+ g_value_set_object (value, katze_throbber_get_static_pixbuf (throbber));
+ break;
+ case PROP_STATIC_STOCK_ID:
+ g_value_set_string (value, katze_throbber_get_static_stock_id (throbber));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * katze_throbber_new:
+ *
+ * Creates a new throbber widget.
+ *
+ * Return value: a new #KatzeThrobber
+ **/
+GtkWidget*
+katze_throbber_new (void)
+{
+ KatzeThrobber* throbber = g_object_new (KATZE_TYPE_THROBBER,
+ NULL);
+
+ return GTK_WIDGET (throbber);
+}
+
+/**
+ * katze_throbber_set_icon_size:
+ * @throbber: a #KatzeThrobber
+ * @icon_size: the new icon size
+ *
+ * Sets the desired size of the throbber image. The animation and static image
+ * will be displayed in this size. If a pixbuf is used for the animation every
+ * single frame is assumed to have this size.
+ **/
+void
+katze_throbber_set_icon_size (KatzeThrobber* throbber,
+ GtkIconSize icon_size)
+{
+ g_return_if_fail (KATZE_IS_THROBBER (throbber));
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ g_return_if_fail (gtk_icon_size_lookup (icon_size,
+ &priv->width,
+ &priv->height));
+
+ priv->icon_size = icon_size;
+
+ g_object_notify (G_OBJECT (throbber), "icon-size");
+}
+
+/**
+ * katze_throbber_set_icon_name:
+ * @throbber: a #KatzeThrobber
+ * @icon_name: an icon name or %NULL
+ *
+ * Sets the name of an icon that should provide the animation frames.
+ *
+ * The pixbuf is automatically invalidated.
+ **/
+void
+katze_throbber_set_icon_name (KatzeThrobber* throbber,
+ const gchar* icon_name)
+{
+ g_return_if_fail (KATZE_IS_THROBBER (throbber));
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ katze_assign (priv->icon_name, g_strdup (icon_name));
+
+ if (icon_name)
+ icon_theme_changed (throbber);
+
+ g_object_notify (G_OBJECT (throbber), "icon-name");
+}
+
+/**
+ * katze_throbber_set_pixbuf:
+ * @throbber: a #KatzeThrobber
+ * @pixbuf: a #GdkPixbuf or %NULL
+ *
+ * Sets the pixbuf that should provide the animation frames. Every frame
+ * is assumed to have the icon size of the throbber, which can be specified
+ * with katze_throbber_set_icon_size ().
+ *
+ * The icon name is automatically invalidated.
+ **/
+void
+katze_throbber_set_pixbuf (KatzeThrobber* throbber,
+ GdkPixbuf *pixbuf)
+{
+ g_return_if_fail (KATZE_IS_THROBBER (throbber));
+ g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ katze_object_assign (priv->pixbuf, pixbuf);
+
+ if (pixbuf)
+ {
+ g_object_ref (pixbuf);
+
+ katze_assign (priv->icon_name, NULL);
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (throbber));
+
+ g_object_notify (G_OBJECT (throbber), "pixbuf");
+}
+
+/**
+ * katze_throbber_set_animated:
+ * @throbber: a #KatzeThrobber
+ * @animated: %TRUE to animate the throbber
+ *
+ * Sets the animation state of the throbber.
+ **/
+void
+katze_throbber_set_animated (KatzeThrobber* throbber,
+ gboolean animated)
+{
+ g_return_if_fail (KATZE_IS_THROBBER (throbber));
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ if (G_UNLIKELY (priv->animated == animated))
+ return;
+
+ priv->animated = animated;
+
+ if (animated && (priv->timer_id < 0))
+ priv->timer_id = g_timeout_add_full (
+ G_PRIORITY_LOW, 50,
+ (GSourceFunc)katze_throbber_timeout,
+ throbber,
+ (GDestroyNotify)katze_throbber_timeout_destroy);
+
+ gtk_widget_queue_draw (GTK_WIDGET (throbber));
+
+ g_object_notify (G_OBJECT (throbber), "animated");
+}
+
+/**
+ * katze_throbber_set_static_icon_name:
+ * @throbber: a #KatzeThrobber
+ * @icon_name: an icon name or %NULL
+ *
+ * Sets the name of an icon that should provide the static image.
+ *
+ * The static pixbuf and stock ID are automatically invalidated.
+ **/
+void
+katze_throbber_set_static_icon_name (KatzeThrobber* throbber,
+ const gchar* icon_name)
+{
+ g_return_if_fail (KATZE_IS_THROBBER (throbber));
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ katze_assign (priv->static_icon_name, g_strdup (icon_name));
+
+ if (icon_name)
+ {
+ katze_assign (priv->static_stock_id, NULL);
+
+ icon_theme_changed (throbber);
+ }
+
+ g_object_notify (G_OBJECT (throbber), "static-icon-name");
+}
+
+/**
+ * katze_throbber_set_static_pixbuf:
+ * @throbber: a #KatzeThrobber
+ * @pixbuf: a #GdkPixbuf or %NULL
+ *
+ * Sets the pixbuf that should provide the static image. The pixbuf is
+ * assumed to have the icon size of the throbber, which can be specified
+ * with katze_throbber_set_icon_size ().
+ *
+ * The static icon name and stock ID are automatically invalidated.
+ **/
+void
+katze_throbber_set_static_pixbuf (KatzeThrobber* throbber,
+ GdkPixbuf *pixbuf)
+{
+ g_return_if_fail (KATZE_IS_THROBBER (throbber));
+ g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ katze_object_assign (priv->static_pixbuf, pixbuf);
+
+ if (pixbuf)
+ {
+ g_object_ref (pixbuf);
+
+ katze_assign (priv->static_icon_name, NULL);
+ katze_assign (priv->static_stock_id, NULL);
+ }
+
+ g_object_notify (G_OBJECT (throbber), "static-pixbuf");
+}
+
+/**
+ * katze_throbber_set_static_stock_id:
+ * @throbber: a #KatzeThrobber
+ * @stock_id: a stock ID or %NULL
+ *
+ * Sets the stock ID of an icon that should provide the static image.
+ *
+ * The statc icon name and pixbuf are automatically invalidated.
+ **/
+void
+katze_throbber_set_static_stock_id (KatzeThrobber* throbber,
+ const gchar* stock_id)
+{
+ g_return_if_fail (KATZE_IS_THROBBER (throbber));
+
+ if (stock_id)
+ {
+ GtkStockItem stock_item;
+ g_return_if_fail (gtk_stock_lookup (stock_id, &stock_item));
+ }
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ katze_assign (priv->static_stock_id, g_strdup (stock_id));
+
+ if (stock_id)
+ icon_theme_changed (throbber);
+
+ g_object_notify (G_OBJECT (throbber), "static-stock-id");
+}
+
+/**
+ * katze_throbber_get_icon_size:
+ * @throbber: a #KatzeThrobber
+ *
+ * Retrieves the size of the throbber.
+ *
+ * Return value: the size of the throbber
+ **/
+GtkIconSize
+katze_throbber_get_icon_size (KatzeThrobber* throbber)
+{
+ g_return_val_if_fail (KATZE_IS_THROBBER (throbber), GTK_ICON_SIZE_INVALID);
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ return priv->icon_size;
+}
+
+/**
+ * katze_throbber_get_icon_name:
+ * @throbber: a #KatzeThrobber
+ *
+ * Retrieves the name of the icon providing the animation frames.
+ *
+ * Return value: the name of the icon providing the animation frames, or %NULL
+ **/
+const gchar*
+katze_throbber_get_icon_name (KatzeThrobber* throbber)
+{
+ g_return_val_if_fail (KATZE_IS_THROBBER (throbber), NULL);
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ return priv->icon_name;
+}
+
+/**
+ * katze_throbber_get_pixbuf:
+ * @throbber: a #KatzeThrobber
+ *
+ * Retrieves the #GdkPixbuf providing the animation frames if an icon name
+ * or pixbuf is available. The caller of this function does not own a
+ * reference to the returned pixbuf.
+ *
+ * Return value: the pixbuf providing the animation frames, or %NULL
+ **/
+GdkPixbuf*
+katze_throbber_get_pixbuf (KatzeThrobber* throbber)
+{
+ g_return_val_if_fail (KATZE_IS_THROBBER (throbber), NULL);
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ return priv->pixbuf;
+}
+
+/**
+ * katze_throbber_get_animated:
+ * @throbber: a #KatzeThrobber
+ *
+ * Retrieves the status of the animation, whcih can be animated or static.
+ *
+ * Return value: %TRUE if the throbber is animated
+ **/
+gboolean
+katze_throbber_get_animated (KatzeThrobber* throbber)
+{
+ g_return_val_if_fail (KATZE_IS_THROBBER (throbber), FALSE);
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ return priv->animated;
+}
+
+/**
+ * katze_throbber_get_static_icon_name:
+ * @throbber: a #KatzeThrobber
+ *
+ * Retrieves the name of the icon providing the static image, if an icon name
+ * for the static image was specified.
+ *
+ * Return value: the name of the icon providing the static image, or %NULL
+ **/
+const gchar*
+katze_throbber_get_static_icon_name (KatzeThrobber* throbber)
+{
+ g_return_val_if_fail (KATZE_IS_THROBBER (throbber), NULL);
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ return priv->static_icon_name;
+}
+
+/**
+ * katze_throbber_get_static pixbuf:
+ * @throbber: a #KatzeThrobber
+ *
+ * Retrieves the #GdkPixbuf providing the static image, if an icon name, a
+ * pixbuf or a stock ID for the static image was specified. The caller of this
+ * function does not own a reference to the returned pixbuf.
+ *
+ * Return value: the pixbuf providing the static image, or %NULL
+ **/
+GdkPixbuf*
+katze_throbber_get_static_pixbuf (KatzeThrobber* throbber)
+{
+ g_return_val_if_fail (KATZE_IS_THROBBER (throbber), NULL);
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ return priv->static_pixbuf;
+}
+
+/**
+ * katze_throbber_get_static_stock_id:
+ * @throbber: a #KatzeThrobber
+ *
+ * Retrieves the stock ID of the icon providing the static image, if a
+ * stock ID for the static image was specified.
+ *
+ * Return value: the stock ID of the icon providing the static image, or %NULL
+ **/
+const gchar*
+katze_throbber_get_static_stock_id (KatzeThrobber* throbber)
+{
+ g_return_val_if_fail (KATZE_IS_THROBBER (throbber), NULL);
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+ return priv->static_stock_id;
+}
+
+static void
+katze_throbber_realize (GtkWidget* widget)
+{
+ (*GTK_WIDGET_CLASS (katze_throbber_parent_class)->realize) (widget);
+
+ icon_theme_changed (KATZE_THROBBER (widget));
+}
+
+static void
+katze_throbber_unrealize (GtkWidget* widget)
+{
+ if (GTK_WIDGET_CLASS (katze_throbber_parent_class)->unrealize)
+ GTK_WIDGET_CLASS (katze_throbber_parent_class)->unrealize (widget);
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (KATZE_THROBBER (widget));
+ katze_object_assign (priv->pixbuf, NULL);
+ katze_object_assign (priv->static_pixbuf, NULL);
+}
+
+static void
+pixbuf_assign_icon (GdkPixbuf** pixbuf,
+ const gchar* icon_name,
+ KatzeThrobber* throbber)
+{
+ if (*pixbuf)
+ g_object_unref (*pixbuf);
+
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+
+ GdkScreen* screen = gtk_widget_get_screen (GTK_WIDGET (throbber));
+ GtkIconTheme* icon_theme = gtk_icon_theme_get_for_screen (screen);
+ *pixbuf = gtk_icon_theme_load_icon (icon_theme,
+ icon_name,
+ MAX (priv->width, priv->height),
+ (GtkIconLookupFlags) 0,
+ NULL);
+}
+
+static void
+icon_theme_changed (KatzeThrobber* throbber)
+{
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+
+ if (priv->icon_name)
+ pixbuf_assign_icon (&priv->pixbuf, priv->icon_name,
+ throbber);
+
+ if (priv->static_icon_name)
+ pixbuf_assign_icon (&priv->static_pixbuf, priv->static_icon_name,
+ throbber);
+ else if (priv->static_stock_id)
+ {
+ if (priv->static_pixbuf)
+ g_object_unref (priv->static_pixbuf);
+
+ priv->static_pixbuf = gtk_widget_render_icon (GTK_WIDGET (throbber),
+ priv->static_stock_id,
+ priv->icon_size,
+ NULL);
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (throbber));
+}
+
+static void
+katze_throbber_map (GtkWidget* widget)
+{
+ (*GTK_WIDGET_CLASS (katze_throbber_parent_class)->map) (widget);
+}
+
+static void
+katze_throbber_unmap (GtkWidget* widget)
+{
+ if (GTK_WIDGET_CLASS (katze_throbber_parent_class)->unmap)
+ GTK_WIDGET_CLASS (katze_throbber_parent_class)->unmap (widget);
+}
+
+static gboolean
+katze_throbber_timeout (KatzeThrobber* throbber)
+{
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+
+ priv->index++;
+ gtk_widget_queue_draw (GTK_WIDGET (throbber));
+
+ return priv->animated;
+}
+
+static void
+katze_throbber_timeout_destroy (KatzeThrobber* throbber)
+{
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (throbber);
+
+ priv->index = 0;
+ priv->timer_id = -1;
+}
+
+static void
+katze_throbber_style_set (GtkWidget* widget,
+ GtkStyle* prev_style)
+{
+ if (GTK_WIDGET_CLASS (katze_throbber_parent_class)->style_set)
+ GTK_WIDGET_CLASS (katze_throbber_parent_class)->style_set (widget,
+ prev_style);
+
+ icon_theme_changed (KATZE_THROBBER (widget));
+}
+
+static void
+katze_throbber_screen_changed (GtkWidget* widget,
+ GdkScreen* prev_screen)
+{
+ if (GTK_WIDGET_CLASS (katze_throbber_parent_class)->screen_changed)
+ GTK_WIDGET_CLASS (katze_throbber_parent_class)->screen_changed (
+ widget,
+ prev_screen);
+
+ icon_theme_changed (KATZE_THROBBER (widget));
+}
+
+static void
+katze_throbber_size_request (GtkWidget* widget,
+ GtkRequisition* requisition)
+{
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (KATZE_THROBBER (widget));
+
+ requisition->width = priv->width;
+ requisition->height = priv->height;
+
+ GTK_WIDGET_CLASS (katze_throbber_parent_class)->size_request (widget,
+ requisition);
+}
+
+static gboolean
+katze_throbber_expose_event (GtkWidget* widget,
+ GdkEventExpose* event)
+{
+ KatzeThrobberPrivate* priv = KATZE_THROBBER_GET_PRIVATE (KATZE_THROBBER (widget));
+
+ if (G_UNLIKELY (!priv->width || !priv->height))
+ return TRUE;
+
+ if (G_UNLIKELY (!priv->pixbuf && !priv->static_pixbuf))
+ if (priv->animated && !priv->pixbuf && !priv->icon_name)
+ return TRUE;
+
+ if (!priv->animated &&
+ (priv->static_pixbuf || priv->static_icon_name || priv->static_stock_id))
+ {
+ if (G_UNLIKELY (!priv->static_pixbuf && priv->static_icon_name))
+ {
+ icon_theme_changed (KATZE_THROBBER (widget));
+
+ if (!priv->static_pixbuf)
+ {
+ g_warning ("Named icon '%s' couldn't be loaded",
+ priv->static_icon_name);
+ katze_assign (priv->static_icon_name, NULL);
+ return TRUE;
+ }
+ }
+ else if (G_UNLIKELY (!priv->static_pixbuf && priv->static_stock_id))
+ {
+ icon_theme_changed (KATZE_THROBBER (widget));
+
+ if (!priv->static_pixbuf)
+ {
+ g_warning ("Stock icon '%s' couldn't be loaded",
+ priv->static_stock_id);
+ katze_assign (priv->static_stock_id, NULL);
+ return TRUE;
+ }
+ }
+
+ gdk_draw_pixbuf (event->window, NULL, priv->static_pixbuf,
+ 0, 0,
+ widget->allocation.x,
+ widget->allocation.y,
+ priv->width, priv->height,
+ GDK_RGB_DITHER_NONE, 0, 0);
+ }
+ else
+ {
+ if (G_UNLIKELY (priv->icon_name && !priv->pixbuf))
+ {
+ icon_theme_changed (KATZE_THROBBER (widget));
+
+ if (!priv->pixbuf)
+ {
+ g_warning ("Icon '%s' couldn't be loaded", priv->icon_name);
+ katze_assign (priv->icon_name, NULL);
+ return TRUE;
+ }
+ }
+
+ if (G_UNLIKELY (!priv->pixbuf))
+ return TRUE;
+
+ gint cols = gdk_pixbuf_get_width (priv->pixbuf) / priv->width;
+ gint rows = gdk_pixbuf_get_height (priv->pixbuf) / priv->height;
+
+ if (G_LIKELY (cols > 0 && rows > 0))
+ {
+ gint index = priv->index % (cols * rows);
+
+ if (G_LIKELY (priv->timer_id >= 0))
+ index = MAX (index, 1);
+
+ guint x = (index % cols) * priv->width;
+ guint y = (index / cols) * priv->height;
+
+ gdk_draw_pixbuf (event->window, NULL, priv->pixbuf,
+ x, y,
+ widget->allocation.x,
+ widget->allocation.y,
+ priv->width, priv->height,
+ GDK_RGB_DITHER_NONE, 0, 0);
+ }
+ else
+ {
+ g_warning ("Animation frames are broken");
+ katze_assign (priv->icon_name, NULL);
+ katze_object_assign (priv->pixbuf, NULL);
+ }
+ }
+
+ return TRUE;
+}