2 * Copyright © 2000 Eazel, Inc.
3 * Copyright © 2002-2004 Marco Pesenti Gritti
4 * Copyright © 2004, 2006 Christian Persch
6 * Nautilus is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Nautilus is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Author: Andy Hertzfeld <andy@eazel.com>
22 * Ephy port by Marco Pesenti Gritti <marco@it.gnome.org>
24 * $Id: ephy-spinner.c 2114 2006-12-25 12:15:00Z mr $
29 #include "ephy-spinner.h"
31 /* #include "ephy-debug.h" */
32 #define LOG(msg, args...)
33 #define START_PROFILER(name)
34 #define STOP_PROFILER(name)
36 #include <gdk-pixbuf/gdk-pixbuf.h>
39 /* Spinner cache implementation */
41 #define EPHY_TYPE_SPINNER_CACHE (ephy_spinner_cache_get_type())
42 #define EPHY_SPINNER_CACHE(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCache))
43 #define EPHY_SPINNER_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCacheClass))
44 #define EPHY_IS_SPINNER_CACHE(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EPHY_TYPE_SPINNER_CACHE))
45 #define EPHY_IS_SPINNER_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EPHY_TYPE_SPINNER_CACHE))
46 #define EPHY_SPINNER_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCacheClass))
48 typedef struct _EphySpinnerCache EphySpinnerCache;
49 typedef struct _EphySpinnerCacheClass EphySpinnerCacheClass;
50 typedef struct _EphySpinnerCachePrivate EphySpinnerCachePrivate;
52 struct _EphySpinnerCacheClass
54 GObjectClass parent_class;
57 struct _EphySpinnerCache
59 GObject parent_object;
62 EphySpinnerCachePrivate *priv;
65 #define EPHY_SPINNER_CACHE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCachePrivate))
67 struct _EphySpinnerCachePrivate
69 /* Hash table of GdkScreen -> EphySpinnerCacheData */
79 GdkPixbuf **animation_pixbufs;
80 guint n_animation_pixbufs;
83 #define LAST_ICON_SIZE GTK_ICON_SIZE_DIALOG + 1
84 #define SPINNER_ICON_NAME "process-working"
85 #define SPINNER_FALLBACK_ICON_NAME "gnome-spinner"
86 #define EPHY_SPINNER_IMAGES_INVALID ((EphySpinnerImages *) 0x1)
91 GtkIconTheme *icon_theme;
92 EphySpinnerImages *images[LAST_ICON_SIZE];
93 } EphySpinnerCacheData;
95 static void ephy_spinner_cache_class_init (EphySpinnerCacheClass *klass);
96 static void ephy_spinner_cache_init (EphySpinnerCache *cache);
98 static GObjectClass *ephy_spinner_cache_parent_class;
101 ephy_spinner_cache_get_type (void)
103 static GType type = 0;
105 if (G_UNLIKELY (type == 0))
107 const GTypeInfo our_info =
109 sizeof (EphySpinnerCacheClass),
112 (GClassInitFunc) ephy_spinner_cache_class_init,
115 sizeof (EphySpinnerCache),
117 (GInstanceInitFunc) ephy_spinner_cache_init
120 type = g_type_register_static (G_TYPE_OBJECT,
128 static EphySpinnerImages *
129 ephy_spinner_images_ref (EphySpinnerImages *images)
131 g_return_val_if_fail (images != NULL, NULL);
139 ephy_spinner_images_unref (EphySpinnerImages *images)
141 g_return_if_fail (images != NULL);
144 if (images->ref_count == 0)
148 LOG ("Freeing spinner images %p for size %d", images, images->size);
150 for (i = 0; i < images->n_animation_pixbufs; ++i)
152 g_object_unref (images->animation_pixbufs[i]);
154 g_free (images->animation_pixbufs);
161 ephy_spinner_cache_data_unload (EphySpinnerCacheData *data)
164 EphySpinnerImages *images;
166 g_return_if_fail (data != NULL);
168 LOG ("EphySpinnerDataCache unload for screen %p", data->screen);
170 for (size = GTK_ICON_SIZE_INVALID; size < LAST_ICON_SIZE; ++size)
172 images = data->images[size];
173 data->images[size] = NULL;
175 if (images != NULL && images != EPHY_SPINNER_IMAGES_INVALID)
177 ephy_spinner_images_unref (images);
183 extract_frame (GdkPixbuf *grid_pixbuf,
190 if (x + size > gdk_pixbuf_get_width (grid_pixbuf) ||
191 y + size > gdk_pixbuf_get_height (grid_pixbuf))
196 pixbuf = gdk_pixbuf_new_subpixbuf (grid_pixbuf,
199 g_return_val_if_fail (pixbuf != NULL, NULL);
205 scale_to_size (GdkPixbuf *pixbuf,
212 g_return_val_if_fail (pixbuf != NULL, NULL);
214 pw = gdk_pixbuf_get_width (pixbuf);
215 ph = gdk_pixbuf_get_height (pixbuf);
217 if (pw != dw || ph != dh)
219 result = gdk_pixbuf_scale_simple (pixbuf, dw, dh,
220 GDK_INTERP_BILINEAR);
221 g_object_unref (pixbuf);
228 static EphySpinnerImages *
229 ephy_spinner_images_load (GdkScreen *screen,
230 GtkIconTheme *icon_theme,
231 GtkIconSize icon_size)
233 EphySpinnerImages *images;
234 GdkPixbuf *icon_pixbuf, *pixbuf;
235 GtkIconInfo *icon_info = NULL;
236 int grid_width, grid_height, x, y, requested_size, size, isw, ish, n;
238 GSList *list = NULL, *l;
240 LOG ("EphySpinnerCacheData loading for screen %p at size %d", screen, icon_size);
242 START_PROFILER ("loading spinner animation")
244 if (!gtk_icon_size_lookup_for_settings (gtk_settings_get_for_screen (screen),
245 icon_size, &isw, &ish)) goto loser;
247 requested_size = MAX (ish, isw);
249 /* Load the animation. The 'rest icon' is the 0th frame */
250 icon_info = gtk_icon_theme_lookup_icon (icon_theme,
253 if (icon_info == NULL)
255 g_warning ("Throbber animation not found");
257 /* If the icon naming spec compliant name wasn't found, try the old name */
258 icon_info = gtk_icon_theme_lookup_icon (icon_theme,
259 SPINNER_FALLBACK_ICON_NAME,
261 if (icon_info == NULL)
263 g_warning ("Throbber fallback animation not found either");
267 g_assert (icon_info != NULL);
269 size = gtk_icon_info_get_base_size (icon_info);
270 icon = gtk_icon_info_get_filename (icon_info);
271 if (icon == NULL) goto loser;
273 icon_pixbuf = gdk_pixbuf_new_from_file (icon, NULL);
274 gtk_icon_info_free (icon_info);
277 if (icon_pixbuf == NULL)
279 g_warning ("Could not load the spinner file");
283 grid_width = gdk_pixbuf_get_width (icon_pixbuf);
284 grid_height = gdk_pixbuf_get_height (icon_pixbuf);
287 for (y = 0; y < grid_height; y += size)
289 for (x = 0; x < grid_width ; x += size)
291 pixbuf = extract_frame (icon_pixbuf, x, y, size);
295 list = g_slist_prepend (list, pixbuf);
300 g_warning ("Cannot extract frame (%d, %d) from the grid\n", x, y);
305 g_object_unref (icon_pixbuf);
307 if (list == NULL) goto loser;
310 if (size > requested_size)
312 for (l = list; l != NULL; l = l->next)
314 l->data = scale_to_size (l->data, isw, ish);
318 /* Now we've successfully got all the data */
319 images = g_new (EphySpinnerImages, 1);
320 images->ref_count = 1;
322 images->size = icon_size;
323 images->width = images->height = requested_size;
325 images->n_animation_pixbufs = n;
326 images->animation_pixbufs = g_new (GdkPixbuf *, n);
328 for (l = list; l != NULL; l = l->next)
330 g_assert (l->data != NULL);
331 images->animation_pixbufs[--n] = l->data;
337 STOP_PROFILER ("loading spinner animation")
344 gtk_icon_info_free (icon_info);
346 g_slist_foreach (list, (GFunc) g_object_unref, NULL);
348 STOP_PROFILER ("loading spinner animation")
353 static EphySpinnerCacheData *
354 ephy_spinner_cache_data_new (GdkScreen *screen)
356 EphySpinnerCacheData *data;
358 data = g_new0 (EphySpinnerCacheData, 1);
360 data->screen = screen;
361 data->icon_theme = gtk_icon_theme_get_for_screen (screen);
362 g_signal_connect_swapped (data->icon_theme, "changed",
363 G_CALLBACK (ephy_spinner_cache_data_unload),
370 ephy_spinner_cache_data_free (EphySpinnerCacheData *data)
372 g_return_if_fail (data != NULL);
373 g_return_if_fail (data->icon_theme != NULL);
375 g_signal_handlers_disconnect_by_func
377 G_CALLBACK (ephy_spinner_cache_data_unload), data);
379 ephy_spinner_cache_data_unload (data);
384 static EphySpinnerImages *
385 ephy_spinner_cache_get_images (EphySpinnerCache *cache,
387 GtkIconSize icon_size)
389 EphySpinnerCachePrivate *priv = cache->priv;
390 EphySpinnerCacheData *data;
391 EphySpinnerImages *images;
393 LOG ("Getting animation images for screen %p at size %d", screen, icon_size);
395 g_return_val_if_fail (icon_size >= 0 && icon_size < LAST_ICON_SIZE, NULL);
397 /* Backward compat: "invalid" meant "native" size which doesn't exist anymore */
398 if (icon_size == GTK_ICON_SIZE_INVALID)
400 icon_size = GTK_ICON_SIZE_DIALOG;
403 data = g_hash_table_lookup (priv->hash, screen);
406 data = ephy_spinner_cache_data_new (screen);
407 /* FIXME: think about what happens when the screen's display is closed later on */
408 g_hash_table_insert (priv->hash, screen, data);
411 images = data->images[icon_size];
412 if (images == EPHY_SPINNER_IMAGES_INVALID)
414 /* Load failed, but don't try endlessly again! */
420 /* Return cached data */
421 return ephy_spinner_images_ref (images);
424 images = ephy_spinner_images_load (screen, data->icon_theme, icon_size);
428 /* Mark as failed-to-load */
429 data->images[icon_size] = EPHY_SPINNER_IMAGES_INVALID;
434 data->images[icon_size] = images;
436 return ephy_spinner_images_ref (images);
440 ephy_spinner_cache_init (EphySpinnerCache *cache)
442 EphySpinnerCachePrivate *priv;
444 priv = cache->priv = EPHY_SPINNER_CACHE_GET_PRIVATE (cache);
446 LOG ("EphySpinnerCache initialising");
448 priv->hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
450 (GDestroyNotify) ephy_spinner_cache_data_free);
454 ephy_spinner_cache_finalize (GObject *object)
456 EphySpinnerCache *cache = EPHY_SPINNER_CACHE (object);
457 EphySpinnerCachePrivate *priv = cache->priv;
459 g_hash_table_destroy (priv->hash);
461 LOG ("EphySpinnerCache finalised");
463 G_OBJECT_CLASS (ephy_spinner_cache_parent_class)->finalize (object);
467 ephy_spinner_cache_class_init (EphySpinnerCacheClass *klass)
469 GObjectClass *object_class = G_OBJECT_CLASS (klass);
471 ephy_spinner_cache_parent_class = g_type_class_peek_parent (klass);
473 object_class->finalize = ephy_spinner_cache_finalize;
475 g_type_class_add_private (object_class, sizeof (EphySpinnerCachePrivate));
478 static EphySpinnerCache *spinner_cache = NULL;
480 static EphySpinnerCache *
481 ephy_spinner_cache_ref (void)
483 if (spinner_cache == NULL)
485 EphySpinnerCache **cache_ptr;
487 spinner_cache = g_object_new (EPHY_TYPE_SPINNER_CACHE, NULL);
488 cache_ptr = &spinner_cache;
489 g_object_add_weak_pointer (G_OBJECT (spinner_cache),
490 (gpointer) cache_ptr);
492 return spinner_cache;
495 return g_object_ref (spinner_cache);
498 /* Spinner implementation */
500 #define SPINNER_TIMEOUT 125 /* ms */
502 #define EPHY_SPINNER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_SPINNER, EphySpinnerDetails))
504 struct _EphySpinnerDetails
506 GtkIconTheme *icon_theme;
507 EphySpinnerCache *cache;
509 EphySpinnerImages *images;
517 static void ephy_spinner_class_init (EphySpinnerClass *class);
518 static void ephy_spinner_init (EphySpinner *spinner);
520 static GObjectClass *parent_class;
523 ephy_spinner_get_type (void)
525 static GType type = 0;
527 if (G_UNLIKELY (type == 0))
529 const GTypeInfo our_info =
531 sizeof (EphySpinnerClass),
532 NULL, /* base_init */
533 NULL, /* base_finalize */
534 (GClassInitFunc) ephy_spinner_class_init,
536 NULL, /* class_data */
537 sizeof (EphySpinner),
539 (GInstanceInitFunc) ephy_spinner_init
542 type = g_type_register_static (GTK_TYPE_WIDGET,
551 ephy_spinner_load_images (EphySpinner *spinner)
553 EphySpinnerDetails *details = spinner->details;
555 if (details->need_load)
557 START_PROFILER ("ephy_spinner_load_images")
560 ephy_spinner_cache_get_images
562 gtk_widget_get_screen (GTK_WIDGET (spinner)),
565 STOP_PROFILER ("ephy_spinner_load_images")
567 details->current_image = 0; /* 'rest' icon */
568 details->need_load = FALSE;
571 return details->images != NULL;
575 ephy_spinner_unload_images (EphySpinner *spinner)
577 EphySpinnerDetails *details = spinner->details;
579 if (details->images != NULL)
581 ephy_spinner_images_unref (details->images);
582 details->images = NULL;
585 details->current_image = 0;
586 details->need_load = TRUE;
590 icon_theme_changed_cb (GtkIconTheme *icon_theme,
591 EphySpinner *spinner)
593 ephy_spinner_unload_images (spinner);
594 gtk_widget_queue_resize (GTK_WIDGET (spinner));
598 ephy_spinner_init (EphySpinner *spinner)
600 EphySpinnerDetails *details;
602 details = spinner->details = EPHY_SPINNER_GET_PRIVATE (spinner);
604 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (spinner), GTK_NO_WINDOW);
606 details->cache = ephy_spinner_cache_ref ();
607 details->size = GTK_ICON_SIZE_DIALOG;
608 details->spinning = FALSE;
609 details->timeout = SPINNER_TIMEOUT;
610 details->need_load = TRUE;
614 ephy_spinner_expose (GtkWidget *widget,
615 GdkEventExpose *event)
617 EphySpinner *spinner = EPHY_SPINNER (widget);
618 EphySpinnerDetails *details = spinner->details;
619 EphySpinnerImages *images;
622 int x_offset, y_offset, width, height;
623 GdkRectangle pix_area, dest;
625 if (!GTK_WIDGET_DRAWABLE (spinner))
630 if (details->need_load &&
631 !ephy_spinner_load_images (spinner))
636 images = details->images;
642 /* Otherwise |images| will be NULL anyway */
643 g_assert (images->n_animation_pixbufs > 0);
645 g_assert (details->current_image >= 0 &&
646 details->current_image < images->n_animation_pixbufs);
648 pixbuf = images->animation_pixbufs[details->current_image];
650 g_assert (pixbuf != NULL);
652 width = gdk_pixbuf_get_width (pixbuf);
653 height = gdk_pixbuf_get_height (pixbuf);
655 /* Compute the offsets for the image centered on our allocation */
656 x_offset = (widget->allocation.width - width) / 2;
657 y_offset = (widget->allocation.height - height) / 2;
659 pix_area.x = x_offset + widget->allocation.x;
660 pix_area.y = y_offset + widget->allocation.y;
661 pix_area.width = width;
662 pix_area.height = height;
664 if (!gdk_rectangle_intersect (&event->area, &pix_area, &dest))
669 gc = gdk_gc_new (gtk_widget_get_window (widget));
670 gdk_draw_pixbuf (gtk_widget_get_window (widget), gc, pixbuf,
671 dest.x - x_offset - widget->allocation.x,
672 dest.y - y_offset - widget->allocation.y,
674 dest.width, dest.height,
675 GDK_RGB_DITHER_MAX, 0, 0);
682 bump_spinner_frame_cb (EphySpinner *spinner)
684 EphySpinnerDetails *details = spinner->details;
686 /* This can happen when we've unloaded the images on a theme
687 * change, but haven't been in the queued size request yet.
688 * Just skip this update.
690 if (details->images == NULL) return TRUE;
692 details->current_image++;
693 if (details->current_image >= details->images->n_animation_pixbufs)
695 /* the 0th frame is the 'rest' icon */
696 details->current_image = MIN (1, details->images->n_animation_pixbufs);
699 gtk_widget_queue_draw (GTK_WIDGET (spinner));
706 * ephy_spinner_start:
707 * @spinner: a #EphySpinner
709 * Start the spinner animation.
712 ephy_spinner_start (EphySpinner *spinner)
714 EphySpinnerDetails *details = spinner->details;
716 details->spinning = TRUE;
718 if (GTK_WIDGET_MAPPED (GTK_WIDGET (spinner)) &&
719 details->timer_task == 0 &&
720 ephy_spinner_load_images (spinner))
722 /* the 0th frame is the 'rest' icon */
723 details->current_image = MIN (1, details->images->n_animation_pixbufs);
725 details->timer_task =
726 g_timeout_add_full (G_PRIORITY_LOW,
728 (GSourceFunc) bump_spinner_frame_cb,
735 ephy_spinner_remove_update_callback (EphySpinner *spinner)
737 EphySpinnerDetails *details = spinner->details;
739 if (details->timer_task != 0)
741 g_source_remove (details->timer_task);
742 details->timer_task = 0;
748 * @spinner: a #EphySpinner
750 * Stop the spinner animation.
753 ephy_spinner_stop (EphySpinner *spinner)
755 EphySpinnerDetails *details = spinner->details;
757 details->spinning = FALSE;
758 details->current_image = 0;
760 if (details->timer_task != 0)
762 ephy_spinner_remove_update_callback (spinner);
764 if (GTK_WIDGET_MAPPED (GTK_WIDGET (spinner)))
766 gtk_widget_queue_draw (GTK_WIDGET (spinner));
772 * ephy_spinner_set_size:
773 * @spinner: a #EphySpinner
774 * @size: the size of type %GtkIconSize
776 * Set the size of the spinner.
779 ephy_spinner_set_size (EphySpinner *spinner,
782 if (size == GTK_ICON_SIZE_INVALID)
784 size = GTK_ICON_SIZE_DIALOG;
787 if (size != spinner->details->size)
789 ephy_spinner_unload_images (spinner);
791 spinner->details->size = size;
793 gtk_widget_queue_resize (GTK_WIDGET (spinner));
799 * ephy_spinner_set_timeout:
800 * @spinner: a #EphySpinner
801 * @timeout: time delay between updates to the spinner.
803 * Sets the timeout delay for spinner updates.
806 ephy_spinner_set_timeout (EphySpinner *spinner,
809 EphySpinnerDetails *details = spinner->details;
811 if (timeout != details->timeout)
813 ephy_spinner_stop (spinner);
815 details->timeout = timeout;
817 if (details->spinning)
819 ephy_spinner_start (spinner);
826 ephy_spinner_size_request (GtkWidget *widget,
827 GtkRequisition *requisition)
829 EphySpinner *spinner = EPHY_SPINNER (widget);
830 EphySpinnerDetails *details = spinner->details;
832 if ((details->need_load &&
833 !ephy_spinner_load_images (spinner)) ||
834 details->images == NULL)
836 requisition->width = requisition->height = 0;
837 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget),
840 &requisition->height);
844 requisition->width = details->images->width;
845 requisition->height = details->images->height;
847 /* FIXME fix this hack */
848 /* allocate some extra margin so we don't butt up against toolbar edges */
849 if (details->size != GTK_ICON_SIZE_MENU)
851 requisition->width += 2;
852 requisition->height += 2;
857 ephy_spinner_map (GtkWidget *widget)
859 EphySpinner *spinner = EPHY_SPINNER (widget);
860 EphySpinnerDetails *details = spinner->details;
862 GTK_WIDGET_CLASS (parent_class)->map (widget);
864 if (details->spinning)
866 ephy_spinner_start (spinner);
871 ephy_spinner_unmap (GtkWidget *widget)
873 EphySpinner *spinner = EPHY_SPINNER (widget);
875 ephy_spinner_remove_update_callback (spinner);
877 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
881 ephy_spinner_dispose (GObject *object)
883 EphySpinner *spinner = EPHY_SPINNER (object);
885 g_signal_handlers_disconnect_by_func
886 (spinner->details->icon_theme,
887 G_CALLBACK (icon_theme_changed_cb), spinner);
889 G_OBJECT_CLASS (parent_class)->dispose (object);
893 ephy_spinner_finalize (GObject *object)
895 EphySpinner *spinner = EPHY_SPINNER (object);
897 ephy_spinner_remove_update_callback (spinner);
898 ephy_spinner_unload_images (spinner);
900 g_object_unref (spinner->details->cache);
902 G_OBJECT_CLASS (parent_class)->finalize (object);
906 ephy_spinner_screen_changed (GtkWidget *widget,
907 GdkScreen *old_screen)
909 EphySpinner *spinner = EPHY_SPINNER (widget);
910 EphySpinnerDetails *details = spinner->details;
913 if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
915 GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, old_screen);
918 screen = gtk_widget_get_screen (widget);
920 /* FIXME: this seems to be happening when then spinner is destroyed!? */
921 if (old_screen == screen) return;
923 /* We'll get mapped again on the new screen, but not unmapped from
924 * the old screen, so remove timeout here.
926 ephy_spinner_remove_update_callback (spinner);
928 ephy_spinner_unload_images (spinner);
930 if (old_screen != NULL)
932 g_signal_handlers_disconnect_by_func
933 (gtk_icon_theme_get_for_screen (old_screen),
934 G_CALLBACK (icon_theme_changed_cb), spinner);
937 details->icon_theme = gtk_icon_theme_get_for_screen (screen);
938 g_signal_connect (details->icon_theme, "changed",
939 G_CALLBACK (icon_theme_changed_cb), spinner);
943 ephy_spinner_class_init (EphySpinnerClass *class)
945 GObjectClass *object_class = G_OBJECT_CLASS (class);
946 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
948 parent_class = g_type_class_peek_parent (class);
950 object_class->dispose = ephy_spinner_dispose;
951 object_class->finalize = ephy_spinner_finalize;
953 widget_class->expose_event = ephy_spinner_expose;
954 widget_class->size_request = ephy_spinner_size_request;
955 widget_class->map = ephy_spinner_map;
956 widget_class->unmap = ephy_spinner_unmap;
957 widget_class->screen_changed = ephy_spinner_screen_changed;
959 g_type_class_add_private (object_class, sizeof (EphySpinnerDetails));
965 * Create a new #EphySpinner. The spinner is a widget
966 * that gives the user feedback about network status with
969 * Return Value: the spinner #GtkWidget
972 ephy_spinner_new (void)
974 return GTK_WIDGET (g_object_new (EPHY_TYPE_SPINNER, NULL));