diff options
author | Bastien Nocera <hadess@hadess.net> | 2009-07-21 02:16:56 +0100 |
---|---|---|
committer | Bastien Nocera <hadess@hadess.net> | 2009-10-14 14:58:30 +0100 |
commit | d21700f5105c2e4b215aa2b4d4f8c90121748cda (patch) | |
tree | b03eb0b607309e6eb10b92c97bd333d0485f79b3 | |
parent | df53e6ad8b8939f1e7bb9a48987f972fec6cfa4c (diff) | |
download | gtk+-d21700f5105c2e4b215aa2b4d4f8c90121748cda.tar.gz |
Bug 319607 – Add a throbber (activity widget) to GTK+
Add GtkSpinner activity throbber, as well as a cell renderer.
-rw-r--r-- | demos/gtk-demo/Makefile.am | 1 | ||||
-rw-r--r-- | demos/gtk-demo/list_store.c | 144 | ||||
-rw-r--r-- | demos/gtk-demo/spinner.c | 90 | ||||
-rw-r--r-- | docs/reference/gtk/gtk-sections.txt | 36 | ||||
-rw-r--r-- | gtk/Makefile.am | 4 | ||||
-rw-r--r-- | gtk/gtk.h | 2 | ||||
-rw-r--r-- | gtk/gtk.symbols | 17 | ||||
-rw-r--r-- | gtk/gtkcellrendererspinner.c | 362 | ||||
-rw-r--r-- | gtk/gtkcellrendererspinner.h | 60 | ||||
-rw-r--r-- | gtk/gtkspinner.c | 525 | ||||
-rw-r--r-- | gtk/gtkspinner.h | 65 | ||||
-rw-r--r-- | gtk/gtkstyle.c | 118 | ||||
-rw-r--r-- | 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 b2d856856f..45d3ef3643 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 990e489921..33a7189559 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 0000000000..f18e1a17eb --- /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 ed91762e00..a06e5b593a 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 b6e32cf6a9..94f27ae0ea 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 \ @@ -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 721239f52f..c3952a4673 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 0000000000..6501b267c8 --- /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 0000000000..bd0ce14717 --- /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 0000000000..c79e14e570 --- /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 0000000000..126f589317 --- /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 769fe79143..01f8ff02a6 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 a6af1779c1..3e1401bcf4 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; |