2008-04-30 Cosimo Cecchi <cosimoc@gnome.org>
[nautilus.git] / src / ephy-spinner.c
blob47aee8da48c49f827c0c0c0bce6135b65c597e39
1 /*
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$
27 #include "config.h"
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>
37 #include <gtk/gtkicontheme.h>
38 #include <gtk/gtkiconfactory.h>
39 #include <gtk/gtksettings.h>
41 /* Spinner cache implementation */
43 #define EPHY_TYPE_SPINNER_CACHE (ephy_spinner_cache_get_type())
44 #define EPHY_SPINNER_CACHE(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCache))
45 #define EPHY_SPINNER_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCacheClass))
46 #define EPHY_IS_SPINNER_CACHE(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EPHY_TYPE_SPINNER_CACHE))
47 #define EPHY_IS_SPINNER_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EPHY_TYPE_SPINNER_CACHE))
48 #define EPHY_SPINNER_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCacheClass))
50 typedef struct _EphySpinnerCache EphySpinnerCache;
51 typedef struct _EphySpinnerCacheClass EphySpinnerCacheClass;
52 typedef struct _EphySpinnerCachePrivate EphySpinnerCachePrivate;
54 struct _EphySpinnerCacheClass
56 GObjectClass parent_class;
59 struct _EphySpinnerCache
61 GObject parent_object;
63 /*< private >*/
64 EphySpinnerCachePrivate *priv;
67 #define EPHY_SPINNER_CACHE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_SPINNER_CACHE, EphySpinnerCachePrivate))
69 struct _EphySpinnerCachePrivate
71 /* Hash table of GdkScreen -> EphySpinnerCacheData */
72 GHashTable *hash;
75 typedef struct
77 guint ref_count;
78 GtkIconSize size;
79 int width;
80 int height;
81 GdkPixbuf **animation_pixbufs;
82 guint n_animation_pixbufs;
83 } EphySpinnerImages;
85 #define LAST_ICON_SIZE GTK_ICON_SIZE_DIALOG + 1
86 #define SPINNER_ICON_NAME "process-working"
87 #define SPINNER_FALLBACK_ICON_NAME "gnome-spinner"
88 #define EPHY_SPINNER_IMAGES_INVALID ((EphySpinnerImages *) 0x1)
90 typedef struct
92 GdkScreen *screen;
93 GtkIconTheme *icon_theme;
94 EphySpinnerImages *images[LAST_ICON_SIZE];
95 } EphySpinnerCacheData;
97 static void ephy_spinner_cache_class_init (EphySpinnerCacheClass *klass);
98 static void ephy_spinner_cache_init (EphySpinnerCache *cache);
100 static GObjectClass *ephy_spinner_cache_parent_class;
102 static GType
103 ephy_spinner_cache_get_type (void)
105 static GType type = 0;
107 if (G_UNLIKELY (type == 0))
109 const GTypeInfo our_info =
111 sizeof (EphySpinnerCacheClass),
112 NULL,
113 NULL,
114 (GClassInitFunc) ephy_spinner_cache_class_init,
115 NULL,
116 NULL,
117 sizeof (EphySpinnerCache),
119 (GInstanceInitFunc) ephy_spinner_cache_init
122 type = g_type_register_static (G_TYPE_OBJECT,
123 "EphySpinnerCache",
124 &our_info, 0);
127 return type;
130 static EphySpinnerImages *
131 ephy_spinner_images_ref (EphySpinnerImages *images)
133 g_return_val_if_fail (images != NULL, NULL);
135 images->ref_count++;
137 return images;
140 static void
141 ephy_spinner_images_unref (EphySpinnerImages *images)
143 g_return_if_fail (images != NULL);
145 images->ref_count--;
146 if (images->ref_count == 0)
148 guint i;
150 LOG ("Freeing spinner images %p for size %d", images, images->size);
152 for (i = 0; i < images->n_animation_pixbufs; ++i)
154 g_object_unref (images->animation_pixbufs[i]);
156 g_free (images->animation_pixbufs);
158 g_free (images);
162 static void
163 ephy_spinner_cache_data_unload (EphySpinnerCacheData *data)
165 GtkIconSize size;
166 EphySpinnerImages *images;
168 g_return_if_fail (data != NULL);
170 LOG ("EphySpinnerDataCache unload for screen %p", data->screen);
172 for (size = GTK_ICON_SIZE_INVALID; size < LAST_ICON_SIZE; ++size)
174 images = data->images[size];
175 data->images[size] = NULL;
177 if (images != NULL && images != EPHY_SPINNER_IMAGES_INVALID)
179 ephy_spinner_images_unref (images);
184 static GdkPixbuf *
185 extract_frame (GdkPixbuf *grid_pixbuf,
186 int x,
187 int y,
188 int size)
190 GdkPixbuf *pixbuf;
192 if (x + size > gdk_pixbuf_get_width (grid_pixbuf) ||
193 y + size > gdk_pixbuf_get_height (grid_pixbuf))
195 return NULL;
198 pixbuf = gdk_pixbuf_new_subpixbuf (grid_pixbuf,
199 x, y,
200 size, size);
201 g_return_val_if_fail (pixbuf != NULL, NULL);
203 return pixbuf;
206 static GdkPixbuf *
207 scale_to_size (GdkPixbuf *pixbuf,
208 int dw,
209 int dh)
211 GdkPixbuf *result;
212 int pw, ph;
214 g_return_val_if_fail (pixbuf != NULL, NULL);
216 pw = gdk_pixbuf_get_width (pixbuf);
217 ph = gdk_pixbuf_get_height (pixbuf);
219 if (pw != dw || ph != dh)
221 result = gdk_pixbuf_scale_simple (pixbuf, dw, dh,
222 GDK_INTERP_BILINEAR);
223 g_object_unref (pixbuf);
224 return result;
227 return pixbuf;
230 static EphySpinnerImages *
231 ephy_spinner_images_load (GdkScreen *screen,
232 GtkIconTheme *icon_theme,
233 GtkIconSize icon_size)
235 EphySpinnerImages *images;
236 GdkPixbuf *icon_pixbuf, *pixbuf;
237 GtkIconInfo *icon_info = NULL;
238 int grid_width, grid_height, x, y, requested_size, size, isw, ish, n;
239 const char *icon;
240 GSList *list = NULL, *l;
242 LOG ("EphySpinnerCacheData loading for screen %p at size %d", screen, icon_size);
244 START_PROFILER ("loading spinner animation")
246 if (!gtk_icon_size_lookup_for_settings (gtk_settings_get_for_screen (screen),
247 icon_size, &isw, &ish)) goto loser;
249 requested_size = MAX (ish, isw);
251 /* Load the animation. The 'rest icon' is the 0th frame */
252 icon_info = gtk_icon_theme_lookup_icon (icon_theme,
253 SPINNER_ICON_NAME,
254 requested_size, 0);
255 if (icon_info == NULL)
257 g_warning ("Throbber animation not found");
259 /* If the icon naming spec compliant name wasn't found, try the old name */
260 icon_info = gtk_icon_theme_lookup_icon (icon_theme,
261 SPINNER_FALLBACK_ICON_NAME,
262 requested_size, 0);
263 if (icon_info == NULL)
265 g_warning ("Throbber fallback animation not found either");
266 goto loser;
269 g_assert (icon_info != NULL);
271 size = gtk_icon_info_get_base_size (icon_info);
272 icon = gtk_icon_info_get_filename (icon_info);
273 if (icon == NULL) goto loser;
275 icon_pixbuf = gdk_pixbuf_new_from_file (icon, NULL);
276 gtk_icon_info_free (icon_info);
277 icon_info = NULL;
279 if (icon_pixbuf == NULL)
281 g_warning ("Could not load the spinner file");
282 goto loser;
285 grid_width = gdk_pixbuf_get_width (icon_pixbuf);
286 grid_height = gdk_pixbuf_get_height (icon_pixbuf);
288 n = 0;
289 for (y = 0; y < grid_height; y += size)
291 for (x = 0; x < grid_width ; x += size)
293 pixbuf = extract_frame (icon_pixbuf, x, y, size);
295 if (pixbuf)
297 list = g_slist_prepend (list, pixbuf);
298 ++n;
300 else
302 g_warning ("Cannot extract frame (%d, %d) from the grid\n", x, y);
307 g_object_unref (icon_pixbuf);
309 if (list == NULL) goto loser;
310 g_assert (n > 0);
312 if (size > requested_size)
314 for (l = list; l != NULL; l = l->next)
316 l->data = scale_to_size (l->data, isw, ish);
320 /* Now we've successfully got all the data */
321 images = g_new (EphySpinnerImages, 1);
322 images->ref_count = 1;
324 images->size = icon_size;
325 images->width = images->height = requested_size;
327 images->n_animation_pixbufs = n;
328 images->animation_pixbufs = g_new (GdkPixbuf *, n);
330 for (l = list; l != NULL; l = l->next)
332 g_assert (l->data != NULL);
333 images->animation_pixbufs[--n] = l->data;
335 g_assert (n == 0);
337 g_slist_free (list);
339 STOP_PROFILER ("loading spinner animation")
341 return images;
343 loser:
344 if (icon_info)
346 gtk_icon_info_free (icon_info);
348 g_slist_foreach (list, (GFunc) g_object_unref, NULL);
350 STOP_PROFILER ("loading spinner animation")
352 return NULL;
355 static EphySpinnerCacheData *
356 ephy_spinner_cache_data_new (GdkScreen *screen)
358 EphySpinnerCacheData *data;
360 data = g_new0 (EphySpinnerCacheData, 1);
362 data->screen = screen;
363 data->icon_theme = gtk_icon_theme_get_for_screen (screen);
364 g_signal_connect_swapped (data->icon_theme, "changed",
365 G_CALLBACK (ephy_spinner_cache_data_unload),
366 data);
368 return data;
371 static void
372 ephy_spinner_cache_data_free (EphySpinnerCacheData *data)
374 g_return_if_fail (data != NULL);
375 g_return_if_fail (data->icon_theme != NULL);
377 g_signal_handlers_disconnect_by_func
378 (data->icon_theme,
379 G_CALLBACK (ephy_spinner_cache_data_unload), data);
381 ephy_spinner_cache_data_unload (data);
383 g_free (data);
386 static EphySpinnerImages *
387 ephy_spinner_cache_get_images (EphySpinnerCache *cache,
388 GdkScreen *screen,
389 GtkIconSize icon_size)
391 EphySpinnerCachePrivate *priv = cache->priv;
392 EphySpinnerCacheData *data;
393 EphySpinnerImages *images;
395 LOG ("Getting animation images for screen %p at size %d", screen, icon_size);
397 g_return_val_if_fail (icon_size >= 0 && icon_size < LAST_ICON_SIZE, NULL);
399 /* Backward compat: "invalid" meant "native" size which doesn't exist anymore */
400 if (icon_size == GTK_ICON_SIZE_INVALID)
402 icon_size = GTK_ICON_SIZE_DIALOG;
405 data = g_hash_table_lookup (priv->hash, screen);
406 if (data == NULL)
408 data = ephy_spinner_cache_data_new (screen);
409 /* FIXME: think about what happens when the screen's display is closed later on */
410 g_hash_table_insert (priv->hash, screen, data);
413 images = data->images[icon_size];
414 if (images == EPHY_SPINNER_IMAGES_INVALID)
416 /* Load failed, but don't try endlessly again! */
417 return NULL;
420 if (images != NULL)
422 /* Return cached data */
423 return ephy_spinner_images_ref (images);
426 images = ephy_spinner_images_load (screen, data->icon_theme, icon_size);
428 if (images == NULL)
430 /* Mark as failed-to-load */
431 data->images[icon_size] = EPHY_SPINNER_IMAGES_INVALID;
433 return NULL;
436 data->images[icon_size] = images;
438 return ephy_spinner_images_ref (images);
441 static void
442 ephy_spinner_cache_init (EphySpinnerCache *cache)
444 EphySpinnerCachePrivate *priv;
446 priv = cache->priv = EPHY_SPINNER_CACHE_GET_PRIVATE (cache);
448 LOG ("EphySpinnerCache initialising");
450 priv->hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
451 NULL,
452 (GDestroyNotify) ephy_spinner_cache_data_free);
455 static void
456 ephy_spinner_cache_finalize (GObject *object)
458 EphySpinnerCache *cache = EPHY_SPINNER_CACHE (object);
459 EphySpinnerCachePrivate *priv = cache->priv;
461 g_hash_table_destroy (priv->hash);
463 LOG ("EphySpinnerCache finalised");
465 G_OBJECT_CLASS (ephy_spinner_cache_parent_class)->finalize (object);
468 static void
469 ephy_spinner_cache_class_init (EphySpinnerCacheClass *klass)
471 GObjectClass *object_class = G_OBJECT_CLASS (klass);
473 ephy_spinner_cache_parent_class = g_type_class_peek_parent (klass);
475 object_class->finalize = ephy_spinner_cache_finalize;
477 g_type_class_add_private (object_class, sizeof (EphySpinnerCachePrivate));
480 static EphySpinnerCache *spinner_cache = NULL;
482 static EphySpinnerCache *
483 ephy_spinner_cache_ref (void)
485 if (spinner_cache == NULL)
487 EphySpinnerCache **cache_ptr;
489 spinner_cache = g_object_new (EPHY_TYPE_SPINNER_CACHE, NULL);
490 cache_ptr = &spinner_cache;
491 g_object_add_weak_pointer (G_OBJECT (spinner_cache),
492 (gpointer *) cache_ptr);
494 return spinner_cache;
497 return g_object_ref (spinner_cache);
500 /* Spinner implementation */
502 #define SPINNER_TIMEOUT 125 /* ms */
504 #define EPHY_SPINNER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_SPINNER, EphySpinnerDetails))
506 struct _EphySpinnerDetails
508 GtkIconTheme *icon_theme;
509 EphySpinnerCache *cache;
510 GtkIconSize size;
511 EphySpinnerImages *images;
512 guint current_image;
513 guint timeout;
514 guint timer_task;
515 guint spinning : 1;
516 guint need_load : 1;
519 static void ephy_spinner_class_init (EphySpinnerClass *class);
520 static void ephy_spinner_init (EphySpinner *spinner);
522 static GObjectClass *parent_class;
524 GType
525 ephy_spinner_get_type (void)
527 static GType type = 0;
529 if (G_UNLIKELY (type == 0))
531 const GTypeInfo our_info =
533 sizeof (EphySpinnerClass),
534 NULL, /* base_init */
535 NULL, /* base_finalize */
536 (GClassInitFunc) ephy_spinner_class_init,
537 NULL,
538 NULL, /* class_data */
539 sizeof (EphySpinner),
540 0, /* n_preallocs */
541 (GInstanceInitFunc) ephy_spinner_init
544 type = g_type_register_static (GTK_TYPE_WIDGET,
545 "EphySpinner",
546 &our_info, 0);
549 return type;
552 static gboolean
553 ephy_spinner_load_images (EphySpinner *spinner)
555 EphySpinnerDetails *details = spinner->details;
557 if (details->need_load)
559 START_PROFILER ("ephy_spinner_load_images")
561 details->images =
562 ephy_spinner_cache_get_images
563 (details->cache,
564 gtk_widget_get_screen (GTK_WIDGET (spinner)),
565 details->size);
567 STOP_PROFILER ("ephy_spinner_load_images")
569 details->current_image = 0; /* 'rest' icon */
570 details->need_load = FALSE;
573 return details->images != NULL;
576 static void
577 ephy_spinner_unload_images (EphySpinner *spinner)
579 EphySpinnerDetails *details = spinner->details;
581 if (details->images != NULL)
583 ephy_spinner_images_unref (details->images);
584 details->images = NULL;
587 details->current_image = 0;
588 details->need_load = TRUE;
591 static void
592 icon_theme_changed_cb (GtkIconTheme *icon_theme,
593 EphySpinner *spinner)
595 ephy_spinner_unload_images (spinner);
596 gtk_widget_queue_resize (GTK_WIDGET (spinner));
599 static void
600 ephy_spinner_init (EphySpinner *spinner)
602 EphySpinnerDetails *details;
604 details = spinner->details = EPHY_SPINNER_GET_PRIVATE (spinner);
606 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (spinner), GTK_NO_WINDOW);
608 details->cache = ephy_spinner_cache_ref ();
609 details->size = GTK_ICON_SIZE_DIALOG;
610 details->spinning = FALSE;
611 details->timeout = SPINNER_TIMEOUT;
612 details->need_load = TRUE;
615 static int
616 ephy_spinner_expose (GtkWidget *widget,
617 GdkEventExpose *event)
619 EphySpinner *spinner = EPHY_SPINNER (widget);
620 EphySpinnerDetails *details = spinner->details;
621 EphySpinnerImages *images;
622 GdkPixbuf *pixbuf;
623 GdkGC *gc;
624 int x_offset, y_offset, width, height;
625 GdkRectangle pix_area, dest;
627 if (!GTK_WIDGET_DRAWABLE (spinner))
629 return FALSE;
632 if (details->need_load &&
633 !ephy_spinner_load_images (spinner))
635 return FALSE;
638 images = details->images;
639 if (images == NULL)
641 return FALSE;
644 /* Otherwise |images| will be NULL anyway */
645 g_assert (images->n_animation_pixbufs > 0);
647 g_assert (details->current_image >= 0 &&
648 details->current_image < images->n_animation_pixbufs);
650 pixbuf = images->animation_pixbufs[details->current_image];
652 g_assert (pixbuf != NULL);
654 width = gdk_pixbuf_get_width (pixbuf);
655 height = gdk_pixbuf_get_height (pixbuf);
657 /* Compute the offsets for the image centered on our allocation */
658 x_offset = (widget->allocation.width - width) / 2;
659 y_offset = (widget->allocation.height - height) / 2;
661 pix_area.x = x_offset + widget->allocation.x;
662 pix_area.y = y_offset + widget->allocation.y;
663 pix_area.width = width;
664 pix_area.height = height;
666 if (!gdk_rectangle_intersect (&event->area, &pix_area, &dest))
668 return FALSE;
671 gc = gdk_gc_new (widget->window);
672 gdk_draw_pixbuf (widget->window, gc, pixbuf,
673 dest.x - x_offset - widget->allocation.x,
674 dest.y - y_offset - widget->allocation.y,
675 dest.x, dest.y,
676 dest.width, dest.height,
677 GDK_RGB_DITHER_MAX, 0, 0);
678 g_object_unref (gc);
680 return FALSE;
683 static gboolean
684 bump_spinner_frame_cb (EphySpinner *spinner)
686 EphySpinnerDetails *details = spinner->details;
688 /* This can happen when we've unloaded the images on a theme
689 * change, but haven't been in the queued size request yet.
690 * Just skip this update.
692 if (details->images == NULL) return TRUE;
694 details->current_image++;
695 if (details->current_image >= details->images->n_animation_pixbufs)
697 /* the 0th frame is the 'rest' icon */
698 details->current_image = MIN (1, details->images->n_animation_pixbufs);
701 gtk_widget_queue_draw (GTK_WIDGET (spinner));
703 /* run again */
704 return TRUE;
708 * ephy_spinner_start:
709 * @spinner: a #EphySpinner
711 * Start the spinner animation.
713 void
714 ephy_spinner_start (EphySpinner *spinner)
716 EphySpinnerDetails *details = spinner->details;
718 details->spinning = TRUE;
720 if (GTK_WIDGET_MAPPED (GTK_WIDGET (spinner)) &&
721 details->timer_task == 0 &&
722 ephy_spinner_load_images (spinner))
724 /* the 0th frame is the 'rest' icon */
725 details->current_image = MIN (1, details->images->n_animation_pixbufs);
727 details->timer_task =
728 g_timeout_add_full (G_PRIORITY_LOW,
729 details->timeout,
730 (GSourceFunc) bump_spinner_frame_cb,
731 spinner,
732 NULL);
736 static void
737 ephy_spinner_remove_update_callback (EphySpinner *spinner)
739 EphySpinnerDetails *details = spinner->details;
741 if (details->timer_task != 0)
743 g_source_remove (details->timer_task);
744 details->timer_task = 0;
749 * ephy_spinner_stop:
750 * @spinner: a #EphySpinner
752 * Stop the spinner animation.
754 void
755 ephy_spinner_stop (EphySpinner *spinner)
757 EphySpinnerDetails *details = spinner->details;
759 details->spinning = FALSE;
760 details->current_image = 0;
762 if (details->timer_task != 0)
764 ephy_spinner_remove_update_callback (spinner);
766 if (GTK_WIDGET_MAPPED (GTK_WIDGET (spinner)))
768 gtk_widget_queue_draw (GTK_WIDGET (spinner));
774 * ephy_spinner_set_size:
775 * @spinner: a #EphySpinner
776 * @size: the size of type %GtkIconSize
778 * Set the size of the spinner.
780 void
781 ephy_spinner_set_size (EphySpinner *spinner,
782 GtkIconSize size)
784 if (size == GTK_ICON_SIZE_INVALID)
786 size = GTK_ICON_SIZE_DIALOG;
789 if (size != spinner->details->size)
791 ephy_spinner_unload_images (spinner);
793 spinner->details->size = size;
795 gtk_widget_queue_resize (GTK_WIDGET (spinner));
799 #if 0
801 * ephy_spinner_set_timeout:
802 * @spinner: a #EphySpinner
803 * @timeout: time delay between updates to the spinner.
805 * Sets the timeout delay for spinner updates.
807 void
808 ephy_spinner_set_timeout (EphySpinner *spinner,
809 guint timeout)
811 EphySpinnerDetails *details = spinner->details;
813 if (timeout != details->timeout)
815 ephy_spinner_stop (spinner);
817 details->timeout = timeout;
819 if (details->spinning)
821 ephy_spinner_start (spinner);
825 #endif
827 static void
828 ephy_spinner_size_request (GtkWidget *widget,
829 GtkRequisition *requisition)
831 EphySpinner *spinner = EPHY_SPINNER (widget);
832 EphySpinnerDetails *details = spinner->details;
834 if ((details->need_load &&
835 !ephy_spinner_load_images (spinner)) ||
836 details->images == NULL)
838 requisition->width = requisition->height = 0;
839 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget),
840 details->size,
841 &requisition->width,
842 &requisition->height);
843 return;
846 requisition->width = details->images->width;
847 requisition->height = details->images->height;
849 /* FIXME fix this hack */
850 /* allocate some extra margin so we don't butt up against toolbar edges */
851 if (details->size != GTK_ICON_SIZE_MENU)
853 requisition->width += 2;
854 requisition->height += 2;
858 static void
859 ephy_spinner_map (GtkWidget *widget)
861 EphySpinner *spinner = EPHY_SPINNER (widget);
862 EphySpinnerDetails *details = spinner->details;
864 GTK_WIDGET_CLASS (parent_class)->map (widget);
866 if (details->spinning)
868 ephy_spinner_start (spinner);
872 static void
873 ephy_spinner_unmap (GtkWidget *widget)
875 EphySpinner *spinner = EPHY_SPINNER (widget);
877 ephy_spinner_remove_update_callback (spinner);
879 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
882 static void
883 ephy_spinner_dispose (GObject *object)
885 EphySpinner *spinner = EPHY_SPINNER (object);
887 g_signal_handlers_disconnect_by_func
888 (spinner->details->icon_theme,
889 G_CALLBACK (icon_theme_changed_cb), spinner);
891 G_OBJECT_CLASS (parent_class)->dispose (object);
894 static void
895 ephy_spinner_finalize (GObject *object)
897 EphySpinner *spinner = EPHY_SPINNER (object);
899 ephy_spinner_remove_update_callback (spinner);
900 ephy_spinner_unload_images (spinner);
902 g_object_unref (spinner->details->cache);
904 G_OBJECT_CLASS (parent_class)->finalize (object);
907 static void
908 ephy_spinner_screen_changed (GtkWidget *widget,
909 GdkScreen *old_screen)
911 EphySpinner *spinner = EPHY_SPINNER (widget);
912 EphySpinnerDetails *details = spinner->details;
913 GdkScreen *screen;
915 if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
917 GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, old_screen);
920 screen = gtk_widget_get_screen (widget);
922 /* FIXME: this seems to be happening when then spinner is destroyed!? */
923 if (old_screen == screen) return;
925 /* We'll get mapped again on the new screen, but not unmapped from
926 * the old screen, so remove timeout here.
928 ephy_spinner_remove_update_callback (spinner);
930 ephy_spinner_unload_images (spinner);
932 if (old_screen != NULL)
934 g_signal_handlers_disconnect_by_func
935 (gtk_icon_theme_get_for_screen (old_screen),
936 G_CALLBACK (icon_theme_changed_cb), spinner);
939 details->icon_theme = gtk_icon_theme_get_for_screen (screen);
940 g_signal_connect (details->icon_theme, "changed",
941 G_CALLBACK (icon_theme_changed_cb), spinner);
944 static void
945 ephy_spinner_class_init (EphySpinnerClass *class)
947 GObjectClass *object_class = G_OBJECT_CLASS (class);
948 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
950 parent_class = g_type_class_peek_parent (class);
952 object_class->dispose = ephy_spinner_dispose;
953 object_class->finalize = ephy_spinner_finalize;
955 widget_class->expose_event = ephy_spinner_expose;
956 widget_class->size_request = ephy_spinner_size_request;
957 widget_class->map = ephy_spinner_map;
958 widget_class->unmap = ephy_spinner_unmap;
959 widget_class->screen_changed = ephy_spinner_screen_changed;
961 g_type_class_add_private (object_class, sizeof (EphySpinnerDetails));
965 * ephy_spinner_new:
967 * Create a new #EphySpinner. The spinner is a widget
968 * that gives the user feedback about network status with
969 * an animated image.
971 * Return Value: the spinner #GtkWidget
973 GtkWidget *
974 ephy_spinner_new (void)
976 return GTK_WIDGET (g_object_new (EPHY_TYPE_SPINNER, NULL));