[gtk+] Bug 319607 – Add a throbber (activity widget) to GTK+



commit d21700f5105c2e4b215aa2b4d4f8c90121748cda
Author: Bastien Nocera <hadess hadess net>
Date:   Tue Jul 21 02:16:56 2009 +0100

    Bug 319607 â?? Add a throbber (activity widget) to GTK+
    
    Add GtkSpinner activity throbber, as well as a cell renderer.

 demos/gtk-demo/Makefile.am          |    1 +
 demos/gtk-demo/list_store.c         |  144 +++++++---
 demos/gtk-demo/spinner.c            |   90 ++++++
 docs/reference/gtk/gtk-sections.txt |   36 +++
 gtk/Makefile.am                     |    4 +
 gtk/gtk.h                           |    2 +
 gtk/gtk.symbols                     |   17 ++
 gtk/gtkcellrendererspinner.c        |  362 ++++++++++++++++++++++++
 gtk/gtkcellrendererspinner.h        |   60 ++++
 gtk/gtkspinner.c                    |  525 +++++++++++++++++++++++++++++++++++
 gtk/gtkspinner.h                    |   65 +++++
 gtk/gtkstyle.c                      |  118 ++++++++-
 gtk/gtkstyle.h                      |   18 +-
 13 files changed, 1403 insertions(+), 39 deletions(-)
---
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am
index b2d8568..45d3ef3 100644
--- a/demos/gtk-demo/Makefile.am
+++ b/demos/gtk-demo/Makefile.am
@@ -37,6 +37,7 @@ demos =						\
 	rotated_text.c				\
 	search_entry.c				\
 	sizegroup.c				\
+	spinner.c				\
 	stock_browser.c				\
 	textview.c				\
 	textscroll.c				\
diff --git a/demos/gtk-demo/list_store.c b/demos/gtk-demo/list_store.c
index 990e489..33a7189 100644
--- a/demos/gtk-demo/list_store.c
+++ b/demos/gtk-demo/list_store.c
@@ -10,6 +10,8 @@
 #include <gtk/gtk.h>
 
 static GtkWidget *window = NULL;
+static GtkTreeModel *model = NULL;
+static guint timeout = 0;
 
 typedef struct
 {
@@ -26,6 +28,8 @@ enum
   COLUMN_NUMBER,
   COLUMN_SEVERITY,
   COLUMN_DESCRIPTION,
+  COLUMN_PULSE,
+  COLUMN_ACTIVE,
   NUM_COLUMNS
 };
 
@@ -47,6 +51,33 @@ static Bug data[] =
   { FALSE, 1,     "Normal",     "First bug :=)" },
 };
 
+static gboolean
+spinner_timeout (gpointer data)
+{
+  GtkTreeIter iter;
+  guint pulse;
+
+  if (model == NULL)
+    return FALSE;
+
+  gtk_tree_model_get_iter_first (model, &iter);
+  gtk_tree_model_get (model, &iter,
+                      COLUMN_PULSE, &pulse,
+                      -1);
+  if (pulse == G_MAXUINT)
+    pulse = 0;
+  else
+    pulse++;
+
+  gtk_list_store_set (GTK_LIST_STORE (model),
+                      &iter,
+                      COLUMN_PULSE, pulse,
+                      COLUMN_ACTIVE, TRUE,
+                      -1);
+
+  return TRUE;
+}
+
 static GtkTreeModel *
 create_model (void)
 {
@@ -56,21 +87,25 @@ create_model (void)
 
   /* create list store */
   store = gtk_list_store_new (NUM_COLUMNS,
-			      G_TYPE_BOOLEAN,
-			      G_TYPE_UINT,
-			      G_TYPE_STRING,
-			      G_TYPE_STRING);
+                              G_TYPE_BOOLEAN,
+                              G_TYPE_UINT,
+                              G_TYPE_STRING,
+                              G_TYPE_STRING,
+                              G_TYPE_UINT,
+                              G_TYPE_BOOLEAN);
 
   /* add data to the list store */
   for (i = 0; i < G_N_ELEMENTS (data); i++)
     {
       gtk_list_store_append (store, &iter);
       gtk_list_store_set (store, &iter,
-			  COLUMN_FIXED, data[i].fixed,
-			  COLUMN_NUMBER, data[i].number,
-			  COLUMN_SEVERITY, data[i].severity,
-			  COLUMN_DESCRIPTION, data[i].description,
-			  -1);
+                          COLUMN_FIXED, data[i].fixed,
+                          COLUMN_NUMBER, data[i].number,
+                          COLUMN_SEVERITY, data[i].severity,
+                          COLUMN_DESCRIPTION, data[i].description,
+                          COLUMN_PULSE, 0,
+                          COLUMN_ACTIVE, FALSE,
+                          -1);
     }
 
   return GTK_TREE_MODEL (store);
@@ -78,8 +113,8 @@ create_model (void)
 
 static void
 fixed_toggled (GtkCellRendererToggle *cell,
-	       gchar                 *path_str,
-	       gpointer               data)
+               gchar                 *path_str,
+               gpointer               data)
 {
   GtkTreeModel *model = (GtkTreeModel *)data;
   GtkTreeIter  iter;
@@ -110,48 +145,75 @@ add_columns (GtkTreeView *treeview)
   /* column for fixed toggles */
   renderer = gtk_cell_renderer_toggle_new ();
   g_signal_connect (renderer, "toggled",
-		    G_CALLBACK (fixed_toggled), model);
+                    G_CALLBACK (fixed_toggled), model);
 
   column = gtk_tree_view_column_new_with_attributes ("Fixed?",
-						     renderer,
-						     "active", COLUMN_FIXED,
-						     NULL);
+                                                     renderer,
+                                                     "active", COLUMN_FIXED,
+                                                     NULL);
 
   /* set this column to a fixed sizing (of 50 pixels) */
   gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column),
-				   GTK_TREE_VIEW_COLUMN_FIXED);
+                                   GTK_TREE_VIEW_COLUMN_FIXED);
   gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 50);
   gtk_tree_view_append_column (treeview, column);
 
   /* column for bug numbers */
   renderer = gtk_cell_renderer_text_new ();
   column = gtk_tree_view_column_new_with_attributes ("Bug number",
-						     renderer,
-						     "text",
-						     COLUMN_NUMBER,
-						     NULL);
+                                                     renderer,
+                                                     "text",
+                                                     COLUMN_NUMBER,
+                                                     NULL);
   gtk_tree_view_column_set_sort_column_id (column, COLUMN_NUMBER);
   gtk_tree_view_append_column (treeview, column);
 
   /* column for severities */
   renderer = gtk_cell_renderer_text_new ();
   column = gtk_tree_view_column_new_with_attributes ("Severity",
-						     renderer,
-						     "text",
-						     COLUMN_SEVERITY,
-						     NULL);
+                                                     renderer,
+                                                     "text",
+                                                     COLUMN_SEVERITY,
+                                                     NULL);
   gtk_tree_view_column_set_sort_column_id (column, COLUMN_SEVERITY);
   gtk_tree_view_append_column (treeview, column);
 
   /* column for description */
   renderer = gtk_cell_renderer_text_new ();
   column = gtk_tree_view_column_new_with_attributes ("Description",
-						     renderer,
-						     "text",
-						     COLUMN_DESCRIPTION,
-						     NULL);
+                                                     renderer,
+                                                     "text",
+                                                     COLUMN_DESCRIPTION,
+                                                     NULL);
   gtk_tree_view_column_set_sort_column_id (column, COLUMN_DESCRIPTION);
   gtk_tree_view_append_column (treeview, column);
+
+  /* column for spinner */
+  renderer = gtk_cell_renderer_spinner_new ();
+  column = gtk_tree_view_column_new_with_attributes ("Spinning",
+                                                     renderer,
+                                                     "pulse",
+                                                     COLUMN_PULSE,
+                                                     "active",
+                                                     COLUMN_ACTIVE,
+                                                     NULL);
+  gtk_tree_view_column_set_sort_column_id (column, COLUMN_PULSE);
+  gtk_tree_view_append_column (treeview, column);
+}
+
+static gboolean
+window_closed (GtkWidget *widget,
+               GdkEvent  *event,
+               gpointer   user_data)
+{
+  model = NULL;
+  window = NULL;
+  if (timeout != 0)
+    {
+      g_source_remove (timeout);
+      timeout = 0;
+    }
+  return FALSE;
 }
 
 GtkWidget *
@@ -162,17 +224,16 @@ do_list_store (GtkWidget *do_widget)
       GtkWidget *vbox;
       GtkWidget *label;
       GtkWidget *sw;
-      GtkTreeModel *model;
       GtkWidget *treeview;
 
       /* create window, etc */
       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
       gtk_window_set_screen (GTK_WINDOW (window),
-			     gtk_widget_get_screen (do_widget));
+                             gtk_widget_get_screen (do_widget));
       gtk_window_set_title (GTK_WINDOW (window), "GtkListStore demo");
 
       g_signal_connect (window, "destroy",
-			G_CALLBACK (gtk_widget_destroyed), &window);
+                        G_CALLBACK (gtk_widget_destroyed), &window);
       gtk_container_set_border_width (GTK_CONTAINER (window), 8);
 
       vbox = gtk_vbox_new (FALSE, 8);
@@ -183,10 +244,10 @@ do_list_store (GtkWidget *do_widget)
 
       sw = gtk_scrolled_window_new (NULL, NULL);
       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
-					   GTK_SHADOW_ETCHED_IN);
+                                           GTK_SHADOW_ETCHED_IN);
       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
-				      GTK_POLICY_NEVER,
-				      GTK_POLICY_AUTOMATIC);
+                                      GTK_POLICY_NEVER,
+                                      GTK_POLICY_AUTOMATIC);
       gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
 
       /* create tree model */
@@ -196,7 +257,7 @@ do_list_store (GtkWidget *do_widget)
       treeview = gtk_tree_view_new_with_model (model);
       gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
       gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview),
-				       COLUMN_DESCRIPTION);
+                                       COLUMN_DESCRIPTION);
 
       g_object_unref (model);
 
@@ -207,14 +268,25 @@ do_list_store (GtkWidget *do_widget)
 
       /* finish & show */
       gtk_window_set_default_size (GTK_WINDOW (window), 280, 250);
+      g_signal_connect (window, "delete-event",
+                        G_CALLBACK (window_closed), NULL);
     }
 
   if (!GTK_WIDGET_VISIBLE (window))
-    gtk_widget_show_all (window);
+    {
+      gtk_widget_show_all (window);
+      if (timeout == 0)
+        timeout = g_timeout_add (80, spinner_timeout, NULL);
+    }
   else
     {
       gtk_widget_destroy (window);
       window = NULL;
+      if (timeout != 0)
+        {
+          g_source_remove (timeout);
+          timeout = 0;
+        }
     }
 
   return window;
diff --git a/demos/gtk-demo/spinner.c b/demos/gtk-demo/spinner.c
new file mode 100644
index 0000000..f18e1a1
--- /dev/null
+++ b/demos/gtk-demo/spinner.c
@@ -0,0 +1,90 @@
+/* Spinner
+ *
+ * GtkSpinner allows to show that background activity is on-going.
+ *
+ */
+
+#include <gtk/gtk.h>
+
+static GtkWidget *window = NULL;
+static GtkWidget *spinner_sensitive = NULL;
+static GtkWidget *spinner_unsensitive = NULL;
+
+static void
+on_play_clicked (GtkButton *button, gpointer user_data)
+{
+  gtk_spinner_start (GTK_SPINNER (spinner_sensitive));
+  gtk_spinner_start (GTK_SPINNER (spinner_unsensitive));
+}
+
+static void
+on_stop_clicked (GtkButton *button, gpointer user_data)
+{
+  gtk_spinner_stop (GTK_SPINNER (spinner_sensitive));
+  gtk_spinner_stop (GTK_SPINNER (spinner_unsensitive));
+}
+
+GtkWidget *
+do_spinner (GtkWidget *do_widget)
+{
+  GtkWidget *vbox;
+  GtkWidget *hbox;
+  GtkWidget *button;
+  GtkWidget *spinner;
+
+  if (!window)
+  {
+    window = gtk_dialog_new_with_buttons ("GtkSpinner",
+                                          GTK_WINDOW (do_widget),
+                                          0,
+                                          GTK_STOCK_CLOSE,
+                                          GTK_RESPONSE_NONE,
+                                          NULL);
+    gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+
+    g_signal_connect (window, "response",
+                      G_CALLBACK (gtk_widget_destroy), NULL);
+    g_signal_connect (window, "destroy",
+                      G_CALLBACK (gtk_widget_destroyed), &window);
+
+    vbox = gtk_vbox_new (FALSE, 5);
+    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), vbox, TRUE, TRUE, 0);
+    gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+
+    /* Sensitive */
+    hbox = gtk_hbox_new (FALSE, 5);
+    spinner = gtk_spinner_new ();
+    gtk_container_add (GTK_CONTAINER (hbox), spinner);
+    gtk_container_add (GTK_CONTAINER (hbox), gtk_entry_new ());
+    gtk_container_add (GTK_CONTAINER (vbox), hbox);
+    spinner_sensitive = spinner;
+
+    /* Disabled */
+    hbox = gtk_hbox_new (FALSE, 5);
+    spinner = gtk_spinner_new ();
+    gtk_container_add (GTK_CONTAINER (hbox), spinner);
+    gtk_container_add (GTK_CONTAINER (hbox), gtk_entry_new ());
+    gtk_container_add (GTK_CONTAINER (vbox), hbox);
+    spinner_unsensitive = spinner;
+    gtk_widget_set_sensitive (hbox, FALSE);
+
+    button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY);
+    g_signal_connect (G_OBJECT (button), "clicked",
+                      G_CALLBACK (on_play_clicked), spinner);
+    gtk_container_add (GTK_CONTAINER (vbox), button);
+
+    button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_STOP);
+    g_signal_connect (G_OBJECT (button), "clicked",
+                      G_CALLBACK (on_stop_clicked), spinner);
+    gtk_container_add (GTK_CONTAINER (vbox), button);
+  }
+
+  if (!GTK_WIDGET_VISIBLE (window))
+    gtk_widget_show_all (window);
+  else
+    gtk_widget_destroy (window);
+
+  return window;
+}
+
+
diff --git a/docs/reference/gtk/gtk-sections.txt b/docs/reference/gtk/gtk-sections.txt
index ed91762..a06e5b5 100644
--- a/docs/reference/gtk/gtk-sections.txt
+++ b/docs/reference/gtk/gtk-sections.txt
@@ -3585,6 +3585,24 @@ gtk_spin_button_get_type
 </SECTION>
 
 <SECTION>
+<FILE>gtkspinner</FILE>
+<TITLE>GtkSpinner</TITLE>
+GtkSpinner
+gtk_spinner_new
+gtk_spinner_start
+gtk_spinner_stop
+<SUBSECTION Standard>
+GTK_SPINNER
+GTK_IS_SPINNER
+GTK_TYPE_SPINNER
+GTK_SPINNER_CLASS
+GTK_IS_SPINER_CLASS
+GTK_SPINNER_GET_CLASS
+<SUBSECTION Private>
+gtk_spinner_get_type
+</SECTION>
+
+<SECTION>
 <FILE>gtkstatusbar</FILE>
 <TITLE>GtkStatusbar</TITLE>
 GtkStatusbar
@@ -5107,6 +5125,23 @@ gtk_cell_renderer_spin_get_type
 </SECTION>
 
 <SECTION>
+<FILE>gtkcellrendererspinner</FILE>
+<TITLE>GtkCellRendererSpinner</TITLE>
+GtkCellRendererSpinner
+gtk_cell_renderer_spinner_new
+<SUBSECTION Standard>
+GTK_TYPE_CELL_RENDERER_SPINNER
+GTK_CELL_RENDERER_SPINNER
+GTK_CELL_RENDERER_SPINNER_CLASS
+GTK_IS_CELL_RENDERER_SPINNER
+GTK_IS_CELL_RENDERER_SPINNER_CLASS
+GTK_CELL_RENDERER_SPINNER_GET_CLASS
+<SUBSECTION Private>
+GtkCellRendererSpinnerPrivate
+gtk_cell_renderer_spinner_get_type
+</SECTION>
+
+<SECTION>
 <FILE>gtkcellrendererpixbuf</FILE>
 <TITLE>GtkCellRendererPixbuf</TITLE>
 GtkCellRendererPixbuf
@@ -5929,6 +5964,7 @@ gtk_paint_polygon
 gtk_paint_shadow
 gtk_paint_shadow_gap
 gtk_paint_slider
+gtk_paint_spinner
 gtk_paint_string
 gtk_paint_tab
 gtk_paint_vline
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index b6e32cf..94f27ae 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -178,6 +178,7 @@ gtk_public_h_sources =          \
 	gtkcellrendererpixbuf.h	\
 	gtkcellrendererprogress.h \
 	gtkcellrendererspin.h   \
+	gtkcellrendererspinner.h\
 	gtkcellrenderertext.h	\
 	gtkcellrenderertoggle.h	\
 	gtkcellview.h		\
@@ -289,6 +290,7 @@ gtk_public_h_sources =          \
 	gtksizegroup.h		\
 	gtksocket.h		\
 	gtkspinbutton.h		\
+	gtkspinner.h		\
 	gtkstatusbar.h		\
 	gtkstatusicon.h		\
 	gtkstock.h		\
@@ -433,6 +435,7 @@ gtk_base_c_sources =            \
 	gtkcellrendererpixbuf.c	\
 	gtkcellrendererprogress.c \
 	gtkcellrendererspin.c   \
+	gtkcellrendererspinner.c\
 	gtkcellrenderertext.c	\
 	gtkcellrenderertoggle.c	\
 	gtkcellview.c		\
@@ -557,6 +560,7 @@ gtk_base_c_sources =            \
 	gtkshow.c		\
 	gtksocket.c		\
 	gtkspinbutton.c		\
+	gtkspinner.c		\
 	gtkstatusbar.c		\
 	gtkstatusicon.c		\
 	gtkstock.c		\
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 211c2d2..6cf26a3 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -59,6 +59,7 @@
 #include <gtk/gtkcellrendererpixbuf.h>
 #include <gtk/gtkcellrendererprogress.h>
 #include <gtk/gtkcellrendererspin.h>
+#include <gtk/gtkcellrendererspinner.h>
 #include <gtk/gtkcellrenderertext.h>
 #include <gtk/gtkcellrenderertoggle.h>
 #include <gtk/gtkcellview.h>
@@ -168,6 +169,7 @@
 #include <gtk/gtksizegroup.h>
 #include <gtk/gtksocket.h>
 #include <gtk/gtkspinbutton.h>
+#include <gtk/gtkspinner.h>
 #include <gtk/gtkstatusbar.h>
 #include <gtk/gtkstatusicon.h>
 #include <gtk/gtkstock.h>
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 721239f..c3952a4 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -670,6 +670,13 @@ gtk_cell_renderer_spin_new
 #endif
 #endif
 
+#if IN_HEADER(__GTK_CELL_RENDERER_SPINNER_H__)
+#if IN_FILE(__GTK_CELL_RENDERER_SPINNER_C__)
+gtk_cell_renderer_spinner_get_type G_GNUC_CONST
+gtk_cell_renderer_spinner_new
+#endif
+#endif
+
 #if IN_HEADER(__GTK_CELL_RENDERER_PROGRESS_H__)
 #if IN_FILE(__GTK_CELL_RENDERER_PROGRESS_C__)
 gtk_cell_renderer_progress_get_type G_GNUC_CONST
@@ -1277,6 +1284,7 @@ gtk_paint_resize_grip
 gtk_paint_shadow
 gtk_paint_shadow_gap
 gtk_paint_slider
+gtk_paint_spinner
 gtk_paint_tab
 gtk_paint_vline
 gtk_border_new G_GNUC_MALLOC
@@ -3802,6 +3810,15 @@ gtk_spin_button_update
 #endif
 #endif
 
+#if IN_HEADER(__GTK_SPINNER_H__)
+#if IN_FILE(__GTK_SPINNER_C__)
+gtk_spinner_get_type G_GNUC_CONST
+gtk_spinner_new
+gtk_spinner_start
+gtk_spinner_stop
+#endif
+#endif
+
 #if IN_HEADER(__GTK_STATUSBAR_H__)
 #if IN_FILE(__GTK_STATUSBAR_C__)
 gtk_statusbar_get_context_id
diff --git a/gtk/gtkcellrendererspinner.c b/gtk/gtkcellrendererspinner.c
new file mode 100644
index 0000000..6501b26
--- /dev/null
+++ b/gtk/gtkcellrendererspinner.c
@@ -0,0 +1,362 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2009 Matthias Clasen <mclasen redhat com>
+ * Copyright (C) 2008 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2009 Bastien Nocera <hadess hadess net>
+ *
+ * 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 2007.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gtkintl.h"
+#include <gtk/gtk.h>
+#include "gtkcellrendererspinner.h"
+#include "gtkalias.h"
+
+enum {
+  PROP_0,
+  PROP_ACTIVE,
+  PROP_PULSE,
+  PROP_SIZE
+};
+
+struct _GtkCellRendererSpinnerPrivate
+{
+  gboolean active;
+  guint pulse;
+  GtkIconSize icon_size, old_icon_size;
+  gint size;
+};
+
+#define GTK_CELL_RENDERER_SPINNER_GET_PRIVATE(object)        \
+                (G_TYPE_INSTANCE_GET_PRIVATE ((object),        \
+                        GTK_TYPE_CELL_RENDERER_SPINNER, \
+                        GtkCellRendererSpinnerPrivate))
+
+static void gtk_cell_renderer_spinner_get_property        (GObject                *object,
+                                                         guint                         param_id,
+                                                         GValue                        *value,
+                                                         GParamSpec                *pspec);
+static void gtk_cell_renderer_spinner_set_property        (GObject                *object,
+                                                         guint                         param_id,
+                                                         const GValue                *value,
+                                                         GParamSpec                *pspec);
+static void gtk_cell_renderer_spinner_get_size                (GtkCellRenderer        *cell,
+                                                         GtkWidget                *widget,
+                                                         GdkRectangle                *cell_area,
+                                                         gint                        *x_offset,
+                                                         gint                        *y_offset,
+                                                         gint                        *width,
+                                                         gint                        *height);
+static void gtk_cell_renderer_spinner_render                (GtkCellRenderer        *cell,
+                                                         GdkWindow                *window,
+                                                         GtkWidget                *widget,
+                                                         GdkRectangle                *background_area,
+                                                         GdkRectangle                *cell_area,
+                                                         GdkRectangle                *expose_area,
+                                                         guint                         flags);
+
+G_DEFINE_TYPE (GtkCellRendererSpinner, gtk_cell_renderer_spinner, GTK_TYPE_CELL_RENDERER)
+
+static void
+gtk_cell_renderer_spinner_class_init (GtkCellRendererSpinnerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
+
+  object_class->get_property = gtk_cell_renderer_spinner_get_property;
+  object_class->set_property = gtk_cell_renderer_spinner_set_property;
+
+  cell_class->get_size = gtk_cell_renderer_spinner_get_size;
+  cell_class->render = gtk_cell_renderer_spinner_render;
+
+  /* GtkCellRendererSpinner::active:
+   *
+   * Whether the spinner is active (ie. shown) in the cell
+   *
+   * Since 2.20
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_ACTIVE,
+                                   g_param_spec_boolean ("active",
+                                                         P_("Active"),
+                                                         P_("Whether the spinner is active (ie. shown) in the cell"),
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+  /* GtkCellRendererSpinner::pulse:
+   *
+   * Pulse of the spinner. Increment this value to draw the next frame of the spinner animation.
+   * Usually, you would update this value in a timeout, every 80 milliseconds to show a full
+   * animation within one second.
+   *
+   * Since 2.20
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_PULSE,
+                                   g_param_spec_uint ("pulse",
+                                                      P_("Pulse"),
+                                                      P_("Pulse of the spinner"),
+                                                      0, G_MAXUINT, 0,
+                                                      G_PARAM_READWRITE));
+  /* GtkCellRendererSpinner::size:
+   *
+   * The #GtkIconSize value that specifies the size of the rendered spinner.
+   *
+   * Since 2.20
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_SIZE,
+                                   g_param_spec_enum ("size",
+                                                      P_("Size"),
+                                                      P_("The #GtkIconSize value that specifies the size of the rendered spinner"),
+                                                      GTK_TYPE_ICON_SIZE, GTK_ICON_SIZE_MENU,
+                                                      G_PARAM_READWRITE));
+
+
+  g_type_class_add_private (object_class, sizeof (GtkCellRendererSpinnerPrivate));
+}
+
+static void
+gtk_cell_renderer_spinner_init (GtkCellRendererSpinner *cell)
+{
+  cell->priv = GTK_CELL_RENDERER_SPINNER_GET_PRIVATE (cell);
+  cell->priv->pulse = 0;
+  cell->priv->old_icon_size = GTK_ICON_SIZE_INVALID;
+  cell->priv->icon_size = GTK_ICON_SIZE_MENU;
+}
+
+/**
+ * gtk_cell_renderer_spinner_new
+ *
+ * Returns a new cell renderer which will show a spinner to indicate
+ * activity.
+ *
+ * Return value: a new #GtkCellRenderer
+ *
+ * Since: 2.20
+ */
+GtkCellRenderer *
+gtk_cell_renderer_spinner_new (void)
+{
+  return g_object_new (GTK_TYPE_CELL_RENDERER_SPINNER, NULL);
+}
+
+static void
+gtk_cell_renderer_spinner_update_size (GtkCellRendererSpinner *cell,
+                                       GtkWidget *widget)
+{
+  GtkCellRendererSpinnerPrivate *priv = cell->priv;
+  GdkScreen *screen;
+  GtkIconTheme *icon_theme;
+  GtkSettings *settings;
+
+  if (cell->priv->old_icon_size == cell->priv->icon_size)
+    return;
+
+  screen = gtk_widget_get_screen (GTK_WIDGET (widget));
+  icon_theme = gtk_icon_theme_get_for_screen (screen);
+  settings = gtk_settings_get_for_screen (screen);
+
+  if (!gtk_icon_size_lookup_for_settings (settings, priv->icon_size, &priv->size, NULL))
+    {
+      g_warning ("Invalid icon size %u\n", priv->icon_size);
+      priv->size = 24;
+    }
+}
+
+static void
+gtk_cell_renderer_spinner_get_property (GObject    *object,
+                                        guint       param_id,
+                                        GValue     *value,
+                                        GParamSpec *pspec)
+{
+  GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object);
+  GtkCellRendererSpinnerPrivate *priv = cell->priv;
+
+  switch (param_id)
+    {
+      case PROP_ACTIVE:
+        g_value_set_boolean (value, priv->active);
+        break;
+      case PROP_PULSE:
+        g_value_set_uint (value, priv->pulse);
+        break;
+      case PROP_SIZE:
+        g_value_set_enum (value, priv->icon_size);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+    }
+}
+
+static void
+gtk_cell_renderer_spinner_set_property (GObject      *object,
+                                        guint         param_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec)
+{
+  GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object);
+  GtkCellRendererSpinnerPrivate *priv = cell->priv;
+
+  switch (param_id)
+    {
+      case PROP_ACTIVE:
+        priv->active = g_value_get_boolean (value);
+        break;
+      case PROP_PULSE:
+        priv->pulse = g_value_get_uint (value);
+        break;
+      case PROP_SIZE:
+        priv->old_icon_size = priv->icon_size;
+        priv->icon_size = g_value_get_enum (value);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+    }
+}
+
+static void
+gtk_cell_renderer_spinner_get_size (GtkCellRenderer *cellr,
+                                    GtkWidget *widget,
+                                    GdkRectangle *cell_area,
+                                    gint *x_offset,
+                                    gint *y_offset,
+                                    gint *width,
+                                    gint *height)
+{
+  GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (cellr);
+  GtkCellRendererSpinnerPrivate *priv = cell->priv;
+  gdouble align;
+  gint w, h;
+  gint xpad, ypad;
+  gfloat xalign, yalign;
+  gboolean rtl;
+
+  rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
+
+  gtk_cell_renderer_spinner_update_size (cell, widget);
+
+  g_object_get (cellr,
+                "xpad", &xpad,
+                "ypad", &ypad,
+                "xalign", &xalign,
+                "yalign", &yalign,
+                NULL);
+  w = h = priv->size;
+
+  if (cell_area)
+    {
+      if (x_offset)
+        {
+          align = rtl ? 1.0 - xalign : xalign;
+          *x_offset = align * (cell_area->width - w);
+          *x_offset = MAX (*x_offset, 0);
+        }
+      if (y_offset)
+        {
+          align = rtl ? 1.0 - yalign : yalign;
+          *y_offset = align * (cell_area->height - h);
+          *y_offset = MAX (*y_offset, 0);
+        }
+    }
+  else
+    {
+      if (x_offset)
+        *x_offset = 0;
+      if (y_offset)
+        *y_offset = 0;
+    }
+
+  if (width)
+    *width = w;
+  if (height)
+    *height = h;
+}
+
+static void
+gtk_cell_renderer_spinner_render (GtkCellRenderer *cellr,
+                                  GdkWindow *window,
+                                  GtkWidget *widget,
+                                  GdkRectangle *background_area,
+                                  GdkRectangle *cell_area,
+                                  GdkRectangle *expose_area,
+                                  guint flags)
+{
+  GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (cellr);
+  GtkCellRendererSpinnerPrivate *priv = cell->priv;
+  GtkStateType state;
+  GdkRectangle pix_rect;
+  GdkRectangle draw_rect;
+  gint xpad, ypad;
+
+  if (!priv->active)
+    return;
+
+  gtk_cell_renderer_spinner_get_size (cellr, widget, cell_area,
+                                      &pix_rect.x, &pix_rect.y,
+                                      &pix_rect.width, &pix_rect.height);
+
+  g_object_get (cellr,
+                "xpad", &xpad,
+                "ypad", &ypad,
+                NULL);
+  pix_rect.x += cell_area->x + xpad;
+  pix_rect.y += cell_area->y + ypad;
+  pix_rect.width -= xpad * 2;
+  pix_rect.height -= ypad * 2;
+
+  if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect) ||
+      !gdk_rectangle_intersect (expose_area, &pix_rect, &draw_rect))
+    {
+      return;
+    }
+
+  state = GTK_STATE_NORMAL;
+  if (GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE || !cellr->sensitive)
+    {
+      state = GTK_STATE_INSENSITIVE;
+    }
+  else
+    {
+      if ((flags & GTK_CELL_RENDERER_SELECTED) != 0)
+        {
+          if (GTK_WIDGET_HAS_FOCUS (widget))
+            state = GTK_STATE_SELECTED;
+          else
+            state = GTK_STATE_ACTIVE;
+        }
+      else
+        state = GTK_STATE_PRELIGHT;
+    }
+
+  gtk_paint_spinner (widget->style,
+                     window,
+                     state,
+                     priv->pulse,
+                     draw_rect.x, draw_rect.y,
+                     draw_rect.width, draw_rect.height);
+}
+
+#define __GTK_CELL_RENDERER_SPINNER_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkcellrendererspinner.h b/gtk/gtkcellrendererspinner.h
new file mode 100644
index 0000000..bd0ce14
--- /dev/null
+++ b/gtk/gtkcellrendererspinner.h
@@ -0,0 +1,60 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2009 Matthias Clasen <mclasen redhat com>
+ * Copyright (C) 2008 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2009 Bastien Nocera <hadess hadess net>
+ *
+ * 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __GTK_CELL_RENDERER_SPINNER_H__
+#define __GTK_CELL_RENDERER_SPINNER_H__
+
+#include <gtk/gtk.h>
+
+#define GTK_TYPE_CELL_RENDERER_SPINNER            (gtk_cell_renderer_spinner_get_type ())
+#define GTK_CELL_RENDERER_SPINNER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_SPINNER, GtkCellRendererSpinner))
+#define GTK_CELL_RENDERER_SPINNER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_SPINNER, GtkCellRendererSpinnerClass))
+#define GTK_IS_CELL_RENDERER_SPINNER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_SPINNER))
+#define GTK_IS_CELL_RENDERER_SPINNER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER_SPINNER))
+#define GTK_CELL_RENDERER_SPINNER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER_SPINNER, GtkCellRendererSpinnerClass))
+
+typedef struct _GtkCellRendererSpinner        GtkCellRendererSpinner;
+typedef struct _GtkCellRendererSpinnerClass   GtkCellRendererSpinnerClass;
+typedef struct _GtkCellRendererSpinnerPrivate GtkCellRendererSpinnerPrivate;
+
+struct _GtkCellRendererSpinner
+{
+  GtkCellRenderer                parent;
+  GtkCellRendererSpinnerPrivate *priv;
+};
+
+struct _GtkCellRendererSpinnerClass
+{
+  GtkCellRendererClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_gtk_reserved1) (void);
+  void (*_gtk_reserved2) (void);
+  void (*_gtk_reserved3) (void);
+  void (*_gtk_reserved4) (void);
+};
+
+GType            gtk_cell_renderer_spinner_get_type (void) G_GNUC_CONST;
+GtkCellRenderer *gtk_cell_renderer_spinner_new      (void);
+
+#endif /* __GTK_CELL_RENDERER_SPINNER_H__ */
+
diff --git a/gtk/gtkspinner.c b/gtk/gtkspinner.c
new file mode 100644
index 0000000..c79e14e
--- /dev/null
+++ b/gtk/gtkspinner.c
@@ -0,0 +1,525 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2007 John Stowers, Neil Jagdish Patel.
+ * Copyright (C) 2009 Bastien Nocera, David Zeuthen
+ *
+ * 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ *
+ * Code adapted from egg-spinner
+ * by Christian Hergert <christian hergert gmail com>
+ */
+
+/*
+ * Modified by the GTK+ Team and others 2007.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gtkintl.h"
+#include "gtkaccessible.h"
+#include "gtkimage.h"
+#include "gtkspinner.h"
+#include "gtkstyle.h"
+#include "gtkalias.h"
+
+#define GTK_SPINNER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SPINNER, GtkSpinnerPrivate))
+
+G_DEFINE_TYPE (GtkSpinner, gtk_spinner, GTK_TYPE_DRAWING_AREA);
+
+enum {
+  PROP_0,
+  PROP_ACTIVE
+};
+
+struct _GtkSpinnerPrivate
+{
+  guint current;
+  guint num_steps;
+  guint timeout;
+};
+
+static void gtk_spinner_class_init (GtkSpinnerClass *klass);
+static void gtk_spinner_init (GtkSpinner *spinner);
+static void gtk_spinner_dispose (GObject *gobject);
+static gboolean gtk_spinner_expose (GtkWidget *widget, GdkEventExpose *event);
+static void gtk_spinner_screen_changed (GtkWidget* widget, GdkScreen* old_screen);
+static void gtk_spinner_style_set (GtkWidget *widget, GtkStyle *prev_style);
+static void gtk_spinner_get_property (GObject    *object,
+                                      guint       param_id,
+                                      GValue     *value,
+                                      GParamSpec *pspec);
+static void gtk_spinner_set_property (GObject      *object,
+                                      guint         param_id,
+                                      const GValue *value,
+                                      GParamSpec   *pspec);
+static AtkObject *gtk_spinner_get_accessible (GtkWidget *widget);
+static GType gtk_spinner_accessible_get_type (void);
+
+static void
+gtk_spinner_class_init (GtkSpinnerClass *klass)
+{
+  GObjectClass *gobject_class;
+  GtkWidgetClass *widget_class;
+
+  gtk_spinner_parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class = G_OBJECT_CLASS(klass);
+  g_type_class_add_private (gobject_class, sizeof (GtkSpinnerPrivate));
+  gobject_class->dispose = gtk_spinner_dispose;
+  gobject_class->get_property = gtk_spinner_get_property;
+  gobject_class->set_property = gtk_spinner_set_property;
+
+  widget_class = GTK_WIDGET_CLASS(klass);
+  widget_class->expose_event = gtk_spinner_expose;
+  widget_class->screen_changed = gtk_spinner_screen_changed;
+  widget_class->style_set = gtk_spinner_style_set;
+  widget_class->get_accessible = gtk_spinner_get_accessible;
+
+  /* GtkSpinner::active:
+   *
+   * Whether the spinner is active
+   *
+   * Since 2.20
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_ACTIVE,
+                                   g_param_spec_boolean ("active",
+                                                         P_("Active"),
+                                                         P_("Whether the spinner is active"),
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+  /**
+   * GtkSpinner::num-steps:
+   *
+   * The number of steps for the spinner to complete a full loop. The animation will
+   * complete a full revolution in one second.
+   *
+   * Since: 2.20
+   */
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_uint ("num-steps",
+                                                             P_("Number of steps"),
+                                                             P_("The number of steps for the spinner to complete a full loop. The animation will complete a full revolution in one second."),
+                                                             1,
+                                                             G_MAXUINT,
+                                                             12,
+                                                             G_PARAM_READABLE));
+}
+
+static void
+gtk_spinner_get_property (GObject    *object,
+                          guint       param_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  GtkSpinnerPrivate *priv;
+
+  priv = GTK_SPINNER_GET_PRIVATE (object);
+
+  switch (param_id)
+    {
+      case PROP_ACTIVE:
+        g_value_set_boolean (value, priv->timeout != 0);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+    }
+}
+
+static void
+gtk_spinner_set_property (GObject      *object,
+                          guint         param_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  switch (param_id)
+    {
+      case PROP_ACTIVE:
+        gtk_spinner_start (GTK_SPINNER (object));
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+    }
+}
+
+static void
+gtk_spinner_init (GtkSpinner *spinner)
+{
+  GtkSpinnerPrivate *priv;
+
+  priv = GTK_SPINNER_GET_PRIVATE (spinner);
+  priv->current = 0;
+  priv->timeout = 0;
+
+  GTK_WIDGET_SET_FLAGS (GTK_WIDGET (spinner), GTK_NO_WINDOW);
+}
+
+static gboolean
+gtk_spinner_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+  GtkStateType state_type;
+  GtkSpinnerPrivate *priv;
+  int width, height;
+
+  priv = GTK_SPINNER_GET_PRIVATE (widget);
+
+  width = widget->allocation.width;
+  height = widget->allocation.height;
+
+  if ((width < 12) || (height <12))
+    gtk_widget_set_size_request (widget, 12, 12);
+
+  state_type = GTK_STATE_NORMAL;
+  if (!GTK_WIDGET_IS_SENSITIVE (widget))
+   state_type = GTK_STATE_INSENSITIVE;
+
+  gtk_paint_spinner (widget->style,
+                     widget->window,
+                     state_type,
+                     priv->current,
+                     event->area.x, event->area.y,
+                     event->area.width, event->area.height);
+
+  return FALSE;
+}
+
+static void
+gtk_spinner_screen_changed (GtkWidget* widget, GdkScreen* old_screen)
+{
+  GtkSpinner *spinner;
+  GdkScreen* new_screen;
+  GdkColormap* colormap;
+
+  spinner = GTK_SPINNER(widget);
+
+  new_screen = gtk_widget_get_screen (widget);
+  colormap = gdk_screen_get_rgba_colormap (new_screen);
+
+  if (!colormap)
+    {
+      colormap = gdk_screen_get_rgb_colormap (new_screen);
+    }
+
+  gtk_widget_set_colormap (widget, colormap);
+}
+
+static void
+gtk_spinner_style_set (GtkWidget *widget,
+                       GtkStyle  *prev_style)
+{
+  GtkSpinnerPrivate *priv;
+
+  priv = GTK_SPINNER_GET_PRIVATE (widget);
+
+  gtk_widget_style_get (GTK_WIDGET (widget),
+                        "num-steps", &(priv->num_steps),
+                        NULL);
+
+  if (priv->current > priv->num_steps)
+    priv->current = 0;
+}
+
+static gboolean
+gtk_spinner_timeout (gpointer data)
+{
+  GtkSpinnerPrivate *priv;
+
+  priv = GTK_SPINNER_GET_PRIVATE (data);
+
+  if (priv->current + 1 >= priv->num_steps)
+    {
+      priv->current = 0;
+    }
+  else
+    {
+      priv->current++;
+    }
+
+  gtk_widget_queue_draw (GTK_WIDGET (data));
+
+  return TRUE;
+}
+static void
+gtk_spinner_dispose (GObject *gobject)
+{
+  GtkSpinnerPrivate *priv;
+
+  priv = GTK_SPINNER_GET_PRIVATE (gobject);
+
+  if (priv->timeout != 0)
+    {
+      g_source_remove (priv->timeout);
+      priv->timeout = 0;
+    }
+
+  G_OBJECT_CLASS (gtk_spinner_parent_class)->dispose (gobject);
+}
+
+static GType
+gtk_spinner_accessible_factory_get_accessible_type (void)
+{
+  return gtk_spinner_accessible_get_type ();
+}
+
+static AtkObject *
+gtk_spinner_accessible_new (GObject *obj)
+{
+  AtkObject *accessible;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (obj), NULL);
+
+  accessible = g_object_new (gtk_spinner_accessible_get_type (), NULL);
+  atk_object_initialize (accessible, obj);
+
+  return accessible;
+}
+
+static AtkObject*
+gtk_spinner_accessible_factory_create_accessible (GObject *obj)
+{
+  return gtk_spinner_accessible_new (obj);
+}
+
+static void
+gtk_spinner_accessible_factory_class_init (AtkObjectFactoryClass *klass)
+{
+  klass->create_accessible = gtk_spinner_accessible_factory_create_accessible;
+  klass->get_accessible_type = gtk_spinner_accessible_factory_get_accessible_type;
+}
+
+static GType
+gtk_spinner_accessible_factory_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type)
+    {
+      const GTypeInfo tinfo =
+      {
+        sizeof (AtkObjectFactoryClass),
+        NULL,           /* base_init */
+        NULL,           /* base_finalize */
+        (GClassInitFunc) gtk_spinner_accessible_factory_class_init,
+        NULL,           /* class_finalize */
+        NULL,           /* class_data */
+        sizeof (AtkObjectFactory),
+        0,             /* n_preallocs */
+        NULL, NULL
+      };
+
+      type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
+                                    I_("GtkSpinnerAccessibleFactory"),
+                                    &tinfo, 0);
+    }
+  return type;
+}
+
+static AtkObjectClass *a11y_parent_class = NULL;
+
+static void
+gtk_spinner_accessible_initialize (AtkObject *accessible,
+                                   gpointer   widget)
+{
+  atk_object_set_name (accessible, _("spinner"));
+  atk_object_set_description (accessible, _("provides visual status"));
+
+  a11y_parent_class->initialize (accessible, widget);
+}
+
+static void
+gtk_spinner_accessible_class_init (AtkObjectClass *klass)
+{
+  a11y_parent_class = g_type_class_peek_parent (klass);
+
+  klass->initialize = gtk_spinner_accessible_initialize;
+}
+
+static void
+gtk_spinner_accessible_image_get_size (AtkImage *image,
+                                             gint     *width,
+                                             gint     *height)
+{
+  GtkWidget *widget;
+
+  widget = GTK_ACCESSIBLE (image)->widget;
+  if (!widget)
+    {
+      *width = *height = 0;
+    }
+  else
+    {
+      *width = widget->allocation.width;
+      *height = widget->allocation.height;
+    }
+}
+
+static void
+gtk_spinner_accessible_image_interface_init (AtkImageIface *iface)
+{
+  iface->get_image_size = gtk_spinner_accessible_image_get_size;
+}
+
+static GType
+gtk_spinner_accessible_get_type (void)
+{
+  static GType type = 0;
+
+  /* Action interface
+     Name etc. ... */
+  if (G_UNLIKELY (type == 0))
+    {
+      const GInterfaceInfo atk_image_info = {
+              (GInterfaceInitFunc) gtk_spinner_accessible_image_interface_init,
+              (GInterfaceFinalizeFunc) NULL,
+              NULL
+      };
+      GType type;
+      GType parent_atk_type;
+      GTypeInfo tinfo = { 0 };
+      GTypeQuery query;
+      AtkObjectFactory *factory;
+
+      if ((type = g_type_from_name ("GtkSpinnerAccessible")))
+        return type;
+
+      factory = atk_registry_get_factory (atk_get_default_registry (),
+                                          GTK_TYPE_IMAGE);
+      if (!factory)
+        return G_TYPE_INVALID;
+
+      parent_atk_type = atk_object_factory_get_accessible_type (factory);
+      if (!parent_atk_type)
+        return G_TYPE_INVALID;
+
+      /*
+       * Figure out the size of the class and instance
+       * we are deriving from
+       */
+      g_type_query (parent_atk_type, &query);
+
+      tinfo.class_init = (GClassInitFunc) gtk_spinner_accessible_class_init;
+      tinfo.class_size    = query.class_size;
+      tinfo.instance_size = query.instance_size;
+
+      /* Register the type */
+      type = g_type_register_static (parent_atk_type,
+                                     "GtkSpinnerAccessible",
+                                     &tinfo, 0);
+
+      g_type_add_interface_static (type, ATK_TYPE_IMAGE,
+                                   &atk_image_info);
+    }
+
+  return type;
+}
+
+static AtkObject *
+gtk_spinner_get_accessible (GtkWidget *widget)
+{
+  static gboolean first_time = TRUE;
+
+  if (first_time)
+    {
+      AtkObjectFactory *factory;
+      AtkRegistry *registry;
+      GType derived_type;
+      GType derived_atk_type;
+
+      /*
+       * Figure out whether accessibility is enabled by looking at the
+       * type of the accessible object which would be created for
+       * the parent type of GtkSpinner.
+       */
+      derived_type = g_type_parent (GTK_TYPE_SPINNER);
+
+      registry = atk_get_default_registry ();
+      factory = atk_registry_get_factory (registry,
+                                          derived_type);
+      derived_atk_type = atk_object_factory_get_accessible_type (factory);
+      if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
+        atk_registry_set_factory_type (registry,
+                                       GTK_TYPE_SPINNER,
+                                       gtk_spinner_accessible_factory_get_type ());
+      first_time = FALSE;
+    }
+  return GTK_WIDGET_CLASS (gtk_spinner_parent_class)->get_accessible (widget);
+}
+
+/**
+ * gtk_spinner_new
+ *
+ * Returns a new spinner widget. Not yet started.
+ *
+ * Return value: a new #GtkSpinner
+ *
+ * Since: 2.20
+ */
+GtkWidget *
+gtk_spinner_new (void)
+{
+  return g_object_new (GTK_TYPE_SPINNER, NULL);
+}
+
+/**
+ * gtk_spinner_start
+ *
+ * Starts the animation on the #GtkSpinner
+ *
+ * Since: 2.20
+ */
+void
+gtk_spinner_start (GtkSpinner *spinner)
+{
+  GtkSpinnerPrivate *priv;
+
+  g_return_if_fail (GTK_IS_SPINNER (spinner));
+
+  priv = GTK_SPINNER_GET_PRIVATE (spinner);
+  if (priv->timeout != 0)
+    {
+      return;
+    }
+  priv->timeout = gdk_threads_add_timeout (1000 / priv->num_steps, gtk_spinner_timeout, spinner);
+}
+
+/**
+ * gtk_spinner_stop
+ *
+ * Stops the animation on the #GtkSpinner
+ *
+ * Since: 2.20
+ */
+void
+gtk_spinner_stop (GtkSpinner *spinner)
+{
+  GtkSpinnerPrivate *priv;
+
+  g_return_if_fail (GTK_IS_SPINNER (spinner));
+
+  priv = GTK_SPINNER_GET_PRIVATE (spinner);
+  if (priv->timeout == 0)
+    {
+      return;
+    }
+  g_source_remove (priv->timeout);
+  priv->timeout = 0;
+}
+
+#define __GTK_SPINNER_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkspinner.h b/gtk/gtkspinner.h
new file mode 100644
index 0000000..126f589
--- /dev/null
+++ b/gtk/gtkspinner.h
@@ -0,0 +1,65 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2007 John Stowers, Neil Jagdish Patel.
+ * Copyright (C) 2009 Bastien Nocera, David Zeuthen
+ *
+ * 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 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., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ *
+ * Code adapted from egg-spinner
+ * by Christian Hergert <christian hergert gmail com>
+ */
+
+#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_SPINNER_H__
+#define __GTK_SPINNER_H__
+
+#include <gtk/gtkdrawingarea.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SPINNER           (gtk_spinner_get_type ())
+#define GTK_SPINNER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SPINNER, GtkSpinner))
+#define GTK_SPINNER_CLASS(obj)     (G_TYPE_CHECK_CLASS_CAST ((obj), GTK_SPINNER,  GtkSpinnerClass))
+#define GTK_IS_SPINNER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SPINNER))
+#define GTK_IS_SPINNER_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE ((obj), GTK_TYPE_SPINNER))
+#define GTK_SPINNER_GET_CLASS      (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SPINNER, GtkSpinnerClass))
+
+typedef struct _GtkSpinner      GtkSpinner;
+typedef struct _GtkSpinnerClass GtkSpinnerClass;
+typedef struct _GtkSpinnerPrivate  GtkSpinnerPrivate;
+
+struct _GtkSpinner
+{
+  GtkDrawingArea parent;
+};
+
+struct _GtkSpinnerClass
+{
+  GtkDrawingAreaClass parent_class;
+  GtkSpinnerPrivate *priv;
+};
+
+GType      gtk_spinner_get_type  (void) G_GNUC_CONST;
+GtkWidget *gtk_spinner_new (void);
+void       gtk_spinner_start      (GtkSpinner *spinner);
+void       gtk_spinner_stop       (GtkSpinner *spinner);
+
+G_END_DECLS
+
+#endif /* __GTK_SPINNER_H__ */
diff --git a/gtk/gtkstyle.c b/gtk/gtkstyle.c
index 769fe79..01f8ff0 100644
--- a/gtk/gtkstyle.c
+++ b/gtk/gtkstyle.c
@@ -313,6 +313,14 @@ static void gtk_default_draw_resize_grip (GtkStyle       *style,
                                           gint            y,
                                           gint            width,
                                           gint            height);
+static void gtk_default_draw_spinner     (GtkStyle     *style,
+					  GdkWindow    *window,
+					  GtkStateType  state_type,
+					  guint         step,
+					  gint           x,
+					  gint           y,
+					  gint           width,
+					  gint           height);
 
 static void rgb_to_hls			(gdouble	 *r,
 					 gdouble	 *g,
@@ -511,6 +519,7 @@ gtk_style_class_init (GtkStyleClass *klass)
   klass->draw_expander = gtk_default_draw_expander;
   klass->draw_layout = gtk_default_draw_layout;
   klass->draw_resize_grip = gtk_default_draw_resize_grip;
+  klass->draw_spinner = gtk_default_draw_spinner;
 
   g_type_class_add_private (object_class, sizeof (GtkStylePrivate));
 
@@ -1764,8 +1773,9 @@ gtk_style_get_style_property (GtkStyle     *style,
   GtkRcPropertyParser parser;
   const GValue *peek_value;
 
-  klass = g_type_class_peek (widget_type);
+  klass = g_type_class_ref (widget_type);
   pspec = gtk_widget_class_find_style_property (klass, property_name);
+  g_type_class_unref (klass);
 
   if (!pspec)
     {
@@ -5598,6 +5608,80 @@ gtk_default_draw_resize_grip (GtkStyle       *style,
     }
 }
 
+static void
+gtk_default_draw_spinner (GtkStyle     *style,
+                          GdkWindow    *window,
+                          GtkStateType  state_type,
+                          guint         step,
+                          gint          x,
+                          gint          y,
+                          gint          width,
+                          gint          height)
+{
+  GdkColor *color;
+  cairo_t *cr;
+  guint num_steps;
+  gdouble dx, dy;
+  gdouble radius;
+  gdouble half;
+  gint i;
+  guint real_step;
+
+  gtk_style_get (style, GTK_TYPE_SPINNER,
+                 "num-steps", &num_steps,
+                 NULL);
+  real_step = step % num_steps;
+
+  /* get cairo context */
+  cr = gdk_cairo_create (window);
+
+  /* set a clip region for the expose event */
+  cairo_rectangle (cr, x, y, width, height);
+  cairo_clip (cr);
+
+  cairo_translate (cr, x, y);
+
+  /* draw clip region */
+  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+  color = &style->fg[state_type];
+  dx = width / 2;
+  dy = height / 2;
+  radius = MIN (width / 2, height / 2);
+  half = num_steps / 2;
+
+  for (i = 0; i < num_steps; i++)
+    {
+      gint inset = 0.7 * radius;
+
+      /* transparency is a function of time and intial value */
+      gdouble t = (gdouble) ((i + num_steps - real_step)
+                             % num_steps) / num_steps;
+
+      cairo_save (cr);
+
+      cairo_set_source_rgba (cr,
+                             color->red / 65535.,
+                             color->green / 65535.,
+                             color->blue / 65535.,
+                             t);
+
+      cairo_set_line_width (cr, 2.0);
+      cairo_move_to (cr,
+                     dx + (radius - inset) * cos (i * G_PI / half),
+                     dy + (radius - inset) * sin (i * G_PI / half));
+      cairo_line_to (cr,
+                     dx + radius * cos (i * G_PI / half),
+                     dy + radius * sin (i * G_PI / half));
+      cairo_stroke (cr);
+
+      cairo_restore (cr);
+    }
+
+  /* free memory */
+  cairo_destroy (cr);
+}
+
 void
 _gtk_style_shade (const GdkColor *a,
                   GdkColor       *b,
@@ -6632,6 +6716,38 @@ gtk_paint_resize_grip (GtkStyle           *style,
 }
 
 /**
+ * gtk_paint_spinner:
+ * @style: a #GtkStyle
+ * @window: a #GdkWindow
+ * @state_type: a state
+ * @widget: the widget
+ * @step: the nth step, a value between 0 and GtkSpinner::num-steps
+ * @x: the x origin of the rectangle in which to draw the resize grip
+ * @y: the y origin of the rectangle in which to draw the resize grip
+ * @width: the width of the rectangle in which to draw the resize grip
+ * @height: the height of the rectangle in which to draw the resize grip
+ *
+ * Draws a spinner on @window using the given parameters.
+ */
+void
+gtk_paint_spinner (GtkStyle     *style,
+		   GdkWindow    *window,
+		   GtkStateType  state_type,
+		   guint         step,
+		   gint          x,
+		   gint          y,
+		   gint          width,
+		   gint          height)
+{
+  g_return_if_fail (GTK_IS_STYLE (style));
+  g_return_if_fail (GTK_STYLE_GET_CLASS (style)->draw_spinner != NULL);
+  g_return_if_fail (style->depth == gdk_drawable_get_depth (window));
+
+  GTK_STYLE_GET_CLASS (style)->draw_spinner (style, window, state_type,
+					     step, x, y, width, height);
+}
+
+/**
  * gtk_border_new:
  *
  * Allocates a new #GtkBorder structure and initializes its elements to zero.
diff --git a/gtk/gtkstyle.h b/gtk/gtkstyle.h
index a6af177..3e1401b 100644
--- a/gtk/gtkstyle.h
+++ b/gtk/gtkstyle.h
@@ -403,6 +403,14 @@ struct _GtkStyleClass
 				 gint			 y,
 				 gint			 width,
 				 gint			 height);
+  void (*draw_spinner)          (GtkStyle		*style,
+				 GdkWindow		*window,
+				 GtkStateType		 state_type,
+				 guint                   step,
+				 gint			 x,
+				 gint			 y,
+				 gint			 width,
+				 gint			 height);
 
   /* Padding for future expansion */
   void (*_gtk_reserved1)  (void);
@@ -416,7 +424,6 @@ struct _GtkStyleClass
   void (*_gtk_reserved9)  (void);
   void (*_gtk_reserved10) (void);
   void (*_gtk_reserved11) (void);
-  void (*_gtk_reserved12) (void);
 };
 
 struct _GtkBorder
@@ -856,7 +863,14 @@ void gtk_paint_resize_grip (GtkStyle           *style,
                             gint                y,
                             gint                width,
                             gint                height);
-
+void gtk_paint_spinner     (GtkStyle     *style,
+			    GdkWindow    *window,
+			    GtkStateType  state_type,
+			    guint         step,
+			    gint          x,
+			    gint          y,
+			    gint          width,
+			    gint          height);
 
 GType      gtk_border_get_type (void) G_GNUC_CONST;
 GtkBorder *gtk_border_new      (void) G_GNUC_MALLOC;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]