]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkspinner.c
Don't check resize-grip-visible in default values unit test
[~andy/gtk] / gtk / gtkspinner.c
index 63a6477265e08db354e2dd14575ff8f4bf5a5cd6..af96fe9d50d8533b93b2b1711202a4f43ed7a79c 100644 (file)
 #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);
+/**
+ * SECTION:gtkspinner
+ * @Short_description: Show a spinner animation
+ * @Title: GtkSpinner
+ * @See_also: #GtkCellRendererSpinner, #GtkProgressBar
+ *
+ * A GtkSpinner widget displays an icon-size spinning animation.
+ * It is often used as an alternative to a #GtkProgressBar for
+ * displaying indefinite activity, instead of actual progress.
+ *
+ * To start the animation, use gtk_spinner_start(), to stop it
+ * use gtk_spinner_stop().
+ */
+
+
+#define SPINNER_SIZE 12
 
 enum {
   PROP_0,
@@ -51,25 +64,39 @@ struct _GtkSpinnerPrivate
 {
   guint current;
   guint num_steps;
+  guint cycle_duration;
+  gboolean active;
   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_dispose        (GObject         *gobject);
+static void gtk_spinner_map            (GtkWidget       *widget);
+static void gtk_spinner_unmap          (GtkWidget       *widget);
+static gboolean gtk_spinner_draw       (GtkWidget       *widget,
+                                        cairo_t         *cr);
+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 void gtk_spinner_set_active     (GtkSpinner      *spinner,
+                                        gboolean         active);
+static void gtk_spinner_get_preferred_width (GtkWidget  *widget,
+                                        gint            *minimum_size,
+                                        gint            *natural_size);
+static void gtk_spinner_get_preferred_height (GtkWidget *widget,
+                                        gint            *minimum_size,
+                                        gint            *natural_size);
+
+static AtkObject *gtk_spinner_get_accessible      (GtkWidget *widget);
+static GType      gtk_spinner_accessible_get_type (void);
+
+G_DEFINE_TYPE (GtkSpinner, gtk_spinner, GTK_TYPE_WIDGET)
 
 static void
 gtk_spinner_class_init (GtkSpinnerClass *klass)
@@ -77,8 +104,6 @@ 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;
@@ -86,16 +111,19 @@ gtk_spinner_class_init (GtkSpinnerClass *klass)
   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->map = gtk_spinner_map;
+  widget_class->unmap = gtk_spinner_unmap;
+  widget_class->draw = gtk_spinner_draw;
   widget_class->style_set = gtk_spinner_style_set;
   widget_class->get_accessible = gtk_spinner_get_accessible;
+  widget_class->get_preferred_width = gtk_spinner_get_preferred_width;
+  widget_class->get_preferred_height = gtk_spinner_get_preferred_height;
 
-  /* GtkSpinner::active:
+  /* GtkSpinner:active:
    *
    * Whether the spinner is active
    *
-   * Since 2.20
+   * Since: 2.20
    */
   g_object_class_install_property (gobject_class,
                                    PROP_ACTIVE,
@@ -105,21 +133,38 @@ gtk_spinner_class_init (GtkSpinnerClass *klass)
                                                          FALSE,
                                                          G_PARAM_READWRITE));
   /**
-   * GtkSpinner::num-steps:
+   * 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.
+   * The number of steps for the spinner to complete a full loop.
+   * The animation will complete a full cycle in one second by default
+   * (see the #GtkSpinner:cycle-duration style property).
    *
    * 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."),
+                                                             P_("The number of steps for the spinner to complete a full loop. The animation will complete a full cycle in one second by default (see #GtkSpinner:cycle-duration)."),
                                                              1,
                                                              G_MAXUINT,
                                                              12,
                                                              G_PARAM_READABLE));
+
+  /**
+   * GtkSpinner:cycle-duration:
+   *
+   * The duration in milliseconds for the spinner to complete a full cycle.
+   *
+   * Since: 2.20
+   */
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_uint ("cycle-duration",
+                                                             P_("Animation duration"),
+                                                             P_("The length of time in milliseconds for the spinner to complete a full loop"),
+                                                             500,
+                                                             G_MAXUINT,
+                                                             1000,
+                                                             G_PARAM_READABLE));
 }
 
 static void
@@ -130,12 +175,12 @@ gtk_spinner_get_property (GObject    *object,
 {
   GtkSpinnerPrivate *priv;
 
-  priv = GTK_SPINNER_GET_PRIVATE (object);
+  priv = GTK_SPINNER (object)->priv;
 
   switch (param_id)
     {
       case PROP_ACTIVE:
-        g_value_set_boolean (value, priv->timeout != 0);
+        g_value_set_boolean (value, priv->active);
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -151,10 +196,7 @@ gtk_spinner_set_property (GObject      *object,
   switch (param_id)
     {
       case PROP_ACTIVE:
-        if (g_value_get_boolean (value))
-          gtk_spinner_start (GTK_SPINNER (object));
-        else
-          gtk_spinner_stop (GTK_SPINNER (object));
+        gtk_spinner_set_active (GTK_SPINNER (object), g_value_get_boolean (value));
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -166,60 +208,127 @@ gtk_spinner_init (GtkSpinner *spinner)
 {
   GtkSpinnerPrivate *priv;
 
-  priv = GTK_SPINNER_GET_PRIVATE (spinner);
+  priv = G_TYPE_INSTANCE_GET_PRIVATE (spinner,
+                                      GTK_TYPE_SPINNER,
+                                      GtkSpinnerPrivate);
   priv->current = 0;
   priv->timeout = 0;
 
-  GTK_WIDGET_SET_FLAGS (GTK_WIDGET (spinner), GTK_NO_WINDOW);
+  spinner->priv = priv;
+
+  gtk_widget_set_has_window (GTK_WIDGET (spinner), FALSE);
+}
+
+static void
+gtk_spinner_get_preferred_width (GtkWidget *widget,
+                                 gint      *minimum_size,
+                                 gint      *natural_size)
+{
+  if (minimum_size)
+    *minimum_size = SPINNER_SIZE;
+
+  if (natural_size)
+    *natural_size = SPINNER_SIZE;
+}
+
+static void
+gtk_spinner_get_preferred_height (GtkWidget *widget,
+                                  gint      *minimum_size,
+                                  gint      *natural_size)
+{
+  if (minimum_size)
+    *minimum_size = SPINNER_SIZE;
+
+  if (natural_size)
+    *natural_size = SPINNER_SIZE;
 }
 
 static gboolean
-gtk_spinner_expose (GtkWidget *widget, GdkEventExpose *event)
+gtk_spinner_draw (GtkWidget *widget,
+                  cairo_t   *cr)
 {
   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);
+  priv = GTK_SPINNER (widget)->priv;
 
   state_type = GTK_STATE_NORMAL;
-  if (!GTK_WIDGET_IS_SENSITIVE (widget))
+  if (!gtk_widget_is_sensitive (widget))
    state_type = GTK_STATE_INSENSITIVE;
 
-  gtk_paint_spinner (widget->style,
-                     widget->window,
+  gtk_paint_spinner (gtk_widget_get_style (widget),
+                     cr,
                      state_type,
+                     widget,
+                     "spinner",
                      priv->current,
-                     event->area.x, event->area.y,
-                     event->area.width, event->area.height);
+                     0, 0,
+                     gtk_widget_get_allocated_width (widget),
+                     gtk_widget_get_allocated_height (widget));
 
   return FALSE;
 }
 
+static gboolean
+gtk_spinner_timeout (gpointer data)
+{
+  GtkSpinnerPrivate *priv;
+
+  priv = GTK_SPINNER (data)->priv;
+
+  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_screen_changed (GtkWidget* widget, GdkScreen* old_screen)
+gtk_spinner_add_timeout (GtkSpinner *spinner)
 {
-  GtkSpinner *spinner;
-  GdkScreen* new_screen;
-  GdkColormap* colormap;
+  GtkSpinnerPrivate *priv;
 
-  spinner = GTK_SPINNER(widget);
+  priv = spinner->priv;
 
-  new_screen = gtk_widget_get_screen (widget);
-  colormap = gdk_screen_get_rgba_colormap (new_screen);
+  priv->timeout = gdk_threads_add_timeout ((guint) priv->cycle_duration / priv->num_steps, gtk_spinner_timeout, spinner);
+}
 
-  if (!colormap)
-    {
-      colormap = gdk_screen_get_rgb_colormap (new_screen);
-    }
+static void
+gtk_spinner_remove_timeout (GtkSpinner *spinner)
+{
+  GtkSpinnerPrivate *priv;
 
-  gtk_widget_set_colormap (widget, colormap);
+  priv = spinner->priv;
+
+  g_source_remove (priv->timeout);
+  priv->timeout = 0;
+}
+
+static void
+gtk_spinner_map (GtkWidget *widget)
+{
+  GtkSpinner *spinner = GTK_SPINNER (widget);
+  GtkSpinnerPrivate *priv = spinner->priv;
+
+  GTK_WIDGET_CLASS (gtk_spinner_parent_class)->map (widget);
+
+  if (priv->active)
+    gtk_spinner_add_timeout (spinner);
+}
+
+static void
+gtk_spinner_unmap (GtkWidget *widget)
+{
+  GtkSpinner *spinner = GTK_SPINNER (widget);
+  GtkSpinnerPrivate *priv = spinner->priv;
+
+  if (priv->timeout != 0)
+    gtk_spinner_remove_timeout (spinner);
+
+  GTK_WIDGET_CLASS (gtk_spinner_parent_class)->unmap (widget);
 }
 
 static void
@@ -228,50 +337,55 @@ gtk_spinner_style_set (GtkWidget *widget,
 {
   GtkSpinnerPrivate *priv;
 
-  priv = GTK_SPINNER_GET_PRIVATE (widget);
+  priv = GTK_SPINNER (widget)->priv;
 
   gtk_widget_style_get (GTK_WIDGET (widget),
                         "num-steps", &(priv->num_steps),
+                        "cycle-duration", &(priv->cycle_duration),
                         NULL);
 
   if (priv->current > priv->num_steps)
     priv->current = 0;
 }
 
-static gboolean
-gtk_spinner_timeout (gpointer data)
+static void
+gtk_spinner_dispose (GObject *gobject)
 {
   GtkSpinnerPrivate *priv;
 
-  priv = GTK_SPINNER_GET_PRIVATE (data);
+  priv = GTK_SPINNER (gobject)->priv;
 
-  if (priv->current + 1 >= priv->num_steps)
-    {
-      priv->current = 0;
-    }
-  else
+  if (priv->timeout != 0)
     {
-      priv->current++;
+      gtk_spinner_remove_timeout (GTK_SPINNER (gobject));
     }
 
-  gtk_widget_queue_draw (GTK_WIDGET (data));
-
-  return TRUE;
+  G_OBJECT_CLASS (gtk_spinner_parent_class)->dispose (gobject);
 }
+
 static void
-gtk_spinner_dispose (GObject *gobject)
+gtk_spinner_set_active (GtkSpinner *spinner, gboolean active)
 {
   GtkSpinnerPrivate *priv;
 
-  priv = GTK_SPINNER_GET_PRIVATE (gobject);
+  active = active != FALSE;
 
-  if (priv->timeout != 0)
+  priv = GTK_SPINNER (spinner)->priv;
+
+  if (priv->active != active)
     {
-      g_source_remove (priv->timeout);
-      priv->timeout = 0;
+      priv->active = active;
+      g_object_notify (G_OBJECT (spinner), "active");
+
+      if (active && gtk_widget_get_realized (GTK_WIDGET (spinner)) && priv->timeout == 0)
+        {
+          gtk_spinner_add_timeout (spinner);
+        }
+      else if (!active && priv->timeout != 0)
+        {
+          gtk_spinner_remove_timeout (spinner);
+        }
     }
-
-  G_OBJECT_CLASS (gtk_spinner_parent_class)->dispose (gobject);
 }
 
 static GType
@@ -339,8 +453,8 @@ static void
 gtk_spinner_accessible_initialize (AtkObject *accessible,
                                    gpointer   widget)
 {
-  atk_object_set_name (accessible, _("Spinner"));
-  atk_object_set_description (accessible, _("Provides visual status"));
+  atk_object_set_name (accessible, C_("throbbing progress animation widget", "Spinner"));
+  atk_object_set_description (accessible, _("Provides visual indication of progress"));
 
   a11y_parent_class->initialize (accessible, widget);
 }
@@ -355,20 +469,22 @@ gtk_spinner_accessible_class_init (AtkObjectClass *klass)
 
 static void
 gtk_spinner_accessible_image_get_size (AtkImage *image,
-                                             gint     *width,
-                                             gint     *height)
+                                       gint     *width,
+                                       gint     *height)
 {
+  GtkAllocation allocation;
   GtkWidget *widget;
 
-  widget = GTK_ACCESSIBLE (image)->widget;
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (image));
   if (!widget)
     {
       *width = *height = 0;
     }
   else
     {
-      *width = widget->allocation.width;
-      *height = widget->allocation.height;
+      gtk_widget_get_allocation (widget, &allocation);
+      *width = allocation.width;
+      *height = allocation.height;
     }
 }
 
@@ -392,7 +508,6 @@ gtk_spinner_accessible_get_type (void)
               (GInterfaceFinalizeFunc) NULL,
               NULL
       };
-      GType type;
       GType parent_atk_type;
       GTypeInfo tinfo = { 0 };
       GTypeQuery query;
@@ -465,7 +580,7 @@ gtk_spinner_get_accessible (GtkWidget *widget)
 }
 
 /**
- * gtk_spinner_new
+ * gtk_spinner_new:
  *
  * Returns a new spinner widget. Not yet started.
  *
@@ -480,51 +595,33 @@ gtk_spinner_new (void)
 }
 
 /**
- * gtk_spinner_start
+ * gtk_spinner_start:
+ * @spinner: a #GtkSpinner
  *
- * Starts the animation on the #GtkSpinner
+ * Starts the animation of the spinner.
  *
  * 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);
-  g_object_notify (G_OBJECT (spinner), "active");
+  gtk_spinner_set_active (spinner, TRUE);
 }
 
 /**
- * gtk_spinner_stop
+ * gtk_spinner_stop:
+ * @spinner: a #GtkSpinner
  *
- * Stops the animation on the #GtkSpinner
+ * Stops the animation of the spinner.
  *
  * 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;
-  g_object_notify (G_OBJECT (spinner), "active");
+  gtk_spinner_set_active (spinner, FALSE);
 }
-
-#define __GTK_SPINNER_C__
-#include "gtkaliasdef.c"