From e2328a4bfcd0fd595209d01914c2ee6b6fe6965a Mon Sep 17 00:00:00 2001 From: Christian Dywan Date: Sat, 5 Jan 2008 06:24:38 +0100 Subject: [PATCH] Implement a throbber widget. The throbber is used in the menubar and individual tabs. Clean infrastructure for private G* extensions is added. --- .gitignore | 5 + Makefile.am | 2 +- autogen.sh | 1 + configure.in | 4 +- katze/Makefile.am | 13 + katze/katze-throbber.c | 878 +++++++++++++++++++++++++++++++++++++++++ katze/katze-throbber.h | 119 ++++++ katze/katze-utils.c | 12 + katze/katze-utils.h | 50 +++ katze/katze.h | 18 + src/Makefile.am | 46 ++- src/browser.c | 7 +- src/helpers.c | 14 +- 13 files changed, 1142 insertions(+), 27 deletions(-) create mode 100644 katze/Makefile.am create mode 100644 katze/katze-throbber.c create mode 100644 katze/katze-throbber.h create mode 100644 katze/katze-utils.c create mode 100644 katze/katze-utils.h create mode 100644 katze/katze.h diff --git a/.gitignore b/.gitignore index 14af9aa6..9f8d6522 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,16 @@ +*.la +*.lo *.o .deps +.libs aclocal.m4 autom4te.cache config.* configure depcomp install-sh +libtool +ltmain.sh Makefile Makefile.in missing diff --git a/Makefile.am b/Makefile.am index cd36747b..95418862 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ AUTOMAKE_OPTIONS = gnu -SUBDIRS = src +SUBDIRS = katze src desktopdir = $(datadir)/applications desktop_DATA = midori.desktop diff --git a/autogen.sh b/autogen.sh index 863dee9f..739a102c 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,5 +1,6 @@ #!/bin/sh +libtoolize --copy --force aclocal autoheader autoconf diff --git a/configure.in b/configure.in index 46034966..80742357 100644 --- a/configure.in +++ b/configure.in @@ -4,6 +4,7 @@ AC_CONFIG_SRCDIR([src/main.h]) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE([AC_PACKAGE_TARNAME()], [AC_PACKAGE_VERSION()]) +AM_PROG_LIBTOOL # Checks for programs AC_PROG_CC @@ -83,7 +84,8 @@ AC_DEFINE_UNQUOTED([LIBXML_VER], "$LIBXML_VER", [libXML2 version]) # Here we tell the configure script which files to *create* AC_CONFIG_FILES([ - Makefile \ + Makefile \ + katze/Makefile \ src/Makefile ]) AC_OUTPUT diff --git a/katze/Makefile.am b/katze/Makefile.am new file mode 100644 index 00000000..f0b2a9fb --- /dev/null +++ b/katze/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES = \ + $(GTK_CFLAGS) + +noinst_LTLIBRARIES = \ + libkatze.la + +libkatze_la_LIBADD = \ + $(GTK_LIBS) + +libkatze_la_SOURCES = \ + katze.h \ + katze-throbber.c katze-throbber.h \ + katze-utils.c katze-utils.h diff --git a/katze/katze-throbber.c b/katze/katze-throbber.c new file mode 100644 index 00000000..fb31912f --- /dev/null +++ b/katze/katze-throbber.c @@ -0,0 +1,878 @@ +/* + Copyright (C) 2007 Christian Dywan + + 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 + +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; +} diff --git a/katze/katze-throbber.h b/katze/katze-throbber.h new file mode 100644 index 00000000..ebd51028 --- /dev/null +++ b/katze/katze-throbber.h @@ -0,0 +1,119 @@ +/* + Copyright (C) 2007 Christian Dywan + + 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. +*/ + +#ifndef __KATZE_THROBBER_H__ +#define __KATZE_THROBBER_H__ + +#include +#include + +#include "katze-utils.h" + +G_BEGIN_DECLS + +#define KATZE_TYPE_THROBBER \ + (katze_throbber_get_type ()) +#define KATZE_THROBBER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), KATZE_TYPE_THROBBER, KatzeThrobber)) +#define KATZE_THROBBER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), KATZE_TYPE_THROBBER, KatzeThrobberClass)) +#define KATZE_IS_THROBBER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), KATZE_TYPE_THROBBER)) +#define KATZE_IS_THROBBER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), KATZE_TYPE_THROBBER)) +#define KATZE_THROBBER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), KATZE_TYPE_THROBBER, KatzeThrobberClass)) + +typedef struct _KatzeThrobber KatzeThrobber; +typedef struct _KatzeThrobberPrivate KatzeThrobberPrivate; +typedef struct _KatzeThrobberClass KatzeThrobberClass; + +struct _KatzeThrobber +{ + KatzeThrobberPrivate* priv; + GtkWidget parent_object; + + /* Padding for future expansion */ + void (*_katze_reserved1) (void); + void (*_katze_reserved2) (void); + void (*_katze_reserved3) (void); + void (*_katze_reserved4) (void); +}; + +struct _KatzeThrobberClass +{ + GtkMiscClass parent_class; + + /* Padding for future expansion */ + void (*_katze_reserved1) (void); + void (*_katze_reserved2) (void); + void (*_katze_reserved3) (void); + void (*_katze_reserved4) (void); +}; + +GType +katze_throbber_get_type (void) G_GNUC_CONST; + +GtkWidget* +katze_throbber_new (void); + +void +katze_throbber_set_icon_size (KatzeThrobber* throbber, + GtkIconSize icon_size); + +void +katze_throbber_set_icon_name (KatzeThrobber* throbber, + const gchar* icon_size); + +void +katze_throbber_set_pixbuf (KatzeThrobber* throbber, + GdkPixbuf* pixbuf); + +void +katze_throbber_set_animated (KatzeThrobber* throbber, + gboolean animated); + +void +katze_throbber_set_static_icon_name (KatzeThrobber* throbber, + const gchar* icon_name); + +void +katze_throbber_set_static_pixbuf (KatzeThrobber* throbber, + GdkPixbuf* pixbuf); + +void +katze_throbber_set_static_stock_id (KatzeThrobber* throbber, + const gchar* stock_id); + +GtkIconSize +katze_throbber_get_icon_size (KatzeThrobber* throbber); + +const gchar* +katze_throbber_get_icon_name (KatzeThrobber* throbber); + +GdkPixbuf* +katze_throbber_get_pixbuf (KatzeThrobber* throbber); + +gboolean +katze_throbber_get_animated (KatzeThrobber* throbber); + +const gchar* +katze_throbber_get_static_icon_name (KatzeThrobber *throbber); + +GdkPixbuf* +katze_throbber_get_static_pixbuf (KatzeThrobber* throbber); + +const gchar* +katze_throbber_get_static_stock_id (KatzeThrobber* throbber); + +G_END_DECLS + +#endif /* __KATZE_THROBBER_H__ */ diff --git a/katze/katze-utils.c b/katze/katze-utils.c new file mode 100644 index 00000000..620a5d01 --- /dev/null +++ b/katze/katze-utils.c @@ -0,0 +1,12 @@ +/* + Copyright (C) 2007 Christian Dywan + + 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-utils.h" diff --git a/katze/katze-utils.h b/katze/katze-utils.h new file mode 100644 index 00000000..45dda897 --- /dev/null +++ b/katze/katze-utils.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 2007 Christian Dywan + + 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. +*/ + +#ifndef __KATZE_UTILS_H__ +#define __KATZE_UTILS_H__ + +#include + +G_BEGIN_DECLS + +/** + * katze_assign: + * @lvalue: a pointer + * @rvalue: the new value + * + * Frees @lvalue if needed and assigns it the value of @rvalue. + **/ +#define katze_assign(lvalue, rvalue) \ + if (1) \ + { \ + g_free (lvalue); \ + lvalue = rvalue; \ + } + +/** + * katze_object_assign: + * @lvalue: a gobject + * @rvalue: the new value + * + * Unrefs @lvalue if needed and assigns it the value of @rvalue. + **/ +#define katze_object_assign(lvalue, rvalue) \ + if (1) \ + { \ + if (lvalue) \ + g_object_unref (lvalue); \ + lvalue = rvalue; \ + } + +G_END_DECLS + +#endif /* __KATZE_UTILS_H__ */ diff --git a/katze/katze.h b/katze/katze.h new file mode 100644 index 00000000..f401be3c --- /dev/null +++ b/katze/katze.h @@ -0,0 +1,18 @@ +/* + Copyright (C) 2007 Christian Dywan + + 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. +*/ + +#ifndef __KATZE__ +#define __KATZE__ + +#include "katze-throbber.h" +#include "katze-utils.h" + +#endif /* __KATZE_THROBBER_H__ */ diff --git a/src/Makefile.am b/src/Makefile.am index c8db7753..6246932a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,17 +1,31 @@ -INCLUDES = $(GTK_CFLAGS) $(WEBKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBSEXY_CFLAGS) -LDADD = $(GTK_LIBS) $(WEBKIT_LIBS) $(LIBXML_LIBS) $(LIBSEXY_LIBS) +INCLUDES = \ + $(GTK_CFLAGS) \ + $(WEBKIT_CFLAGS) \ + $(LIBXML_CFLAGS) \ + $(LIBSEXY_CFLAGS) \ + -I../katze -bin_PROGRAMS = midori -midori_SOURCES = main.c main.h \ - browser.c browser.h \ - prefs.c prefs.h \ - webSearch.c webSearch.h \ - helpers.c helpers.h \ - webView.c webView.h \ - sokoke.c sokoke.h \ - conf.c conf.h \ - search.c search.h \ - xbel.c xbel.h \ - global.h \ - ui.h \ - debug.h +LDADD = \ + $(GTK_LIBS) \ + $(WEBKIT_LIBS) \ + $(LIBXML_LIBS) \ + $(LIBSEXY_LIBS) \ + ../katze/libkatze.la + +bin_PROGRAMS = \ + midori + +midori_SOURCES = \ + main.c main.h \ + browser.c browser.h \ + prefs.c prefs.h \ + webSearch.c webSearch.h \ + helpers.c helpers.h \ + webView.c webView.h \ + sokoke.c sokoke.h \ + conf.c conf.h \ + search.c search.h \ + xbel.c xbel.h \ + global.h \ + ui.h \ + debug.h diff --git a/src/browser.c b/src/browser.c index f98d4016..8dac19a5 100644 --- a/src/browser.c +++ b/src/browser.c @@ -18,6 +18,7 @@ #include "webView.h" #include "webSearch.h" #include "xbel.h" +#include "../katze/katze.h" #include #include @@ -1190,7 +1191,7 @@ CBrowser* browser_new(CBrowser* oldBrowser) browser->menubar = gtk_ui_manager_get_widget(ui_manager, "/menubar"); GtkWidget* menuitem = gtk_menu_item_new(); gtk_widget_show(menuitem); - browser->throbber = gtk_image_new_from_stock(GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU); + browser->throbber = katze_throbber_new(); gtk_widget_show(browser->throbber); gtk_container_add(GTK_CONTAINER(menuitem), browser->throbber); gtk_widget_set_sensitive(menuitem, FALSE); @@ -1561,7 +1562,9 @@ CBrowser* browser_new(CBrowser* oldBrowser) , G_CALLBACK(on_notebook_tab_mouse_up), browser); GtkWidget* hbox = gtk_hbox_new(FALSE, 1); gtk_container_add(GTK_CONTAINER(eventbox), GTK_WIDGET(hbox)); - browser->webView_icon = gtk_image_new_from_stock(GTK_STOCK_FILE, GTK_ICON_SIZE_MENU); + browser->webView_icon = katze_throbber_new(); + katze_throbber_set_static_stock_id(KATZE_THROBBER(browser->webView_icon) + , GTK_STOCK_FILE); gtk_box_pack_start(GTK_BOX(hbox), browser->webView_icon, FALSE, FALSE, 0); browser->webView_name = gtk_label_new(xbel_item_get_title(browser->sessionItem)); gtk_misc_set_alignment(GTK_MISC(browser->webView_name), 0.0, 0.5); diff --git a/src/helpers.c b/src/helpers.c index 2981931c..74ed68e6 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -13,6 +13,7 @@ #include "search.h" #include "sokoke.h" +#include "../katze/katze.h" #include #include @@ -231,14 +232,11 @@ void update_favicon(CBrowser* browser) // TODO: Retrieve mime type and load icon; don't forget ftp listings } else - gtk_image_set_from_stock(GTK_IMAGE(browser->webView_icon) - , GTK_STOCK_FILE, GTK_ICON_SIZE_MENU); - } - else - { - gtk_image_set_from_stock(GTK_IMAGE(browser->webView_icon) - , GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU); + katze_throbber_set_static_stock_id(KATZE_THROBBER(browser->webView_icon) + , GTK_STOCK_FILE); } + katze_throbber_set_animated(KATZE_THROBBER(browser->webView_icon) + , browser->loadedPercent != -1); } void update_security(CBrowser* browser) @@ -382,6 +380,8 @@ void update_gui_state(CBrowser* browser) g_object_set(action, "tooltip", "Stop loading the current page", NULL); gtk_widget_show(browser->progress); } + katze_throbber_set_animated(KATZE_THROBBER(browser->throbber) + , browser->loadedPercent != -1); gtk_image_set_from_stock(GTK_IMAGE(browser->location_icon), GTK_STOCK_FILE , GTK_ICON_SIZE_MENU);