fixed a crash when creating account
[empathy-mirror.git] / src / ephy-spinner.c
blob7efacb18aec31fda233be5a70d019b3973360c28
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: ephy-spinner.c 2114 2006-12-25 12:15:00Z mr $
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/gtk.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;
61 /*< private >*/
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 */
70 GHashTable *hash;
73 typedef struct
75 guint ref_count;
76 GtkIconSize size;
77 int width;
78 int height;
79 GdkPixbuf **animation_pixbufs;
80 guint n_animation_pixbufs;
81 } EphySpinnerImages;
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)
88 typedef struct
90 GdkScreen *screen;
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;
100 static GType
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),
110 NULL,
111 NULL,
112 (GClassInitFunc) ephy_spinner_cache_class_init,
113 NULL,
114 NULL,
115 sizeof (EphySpinnerCache),
117 (GInstanceInitFunc) ephy_spinner_cache_init
120 type = g_type_register_static (G_TYPE_OBJECT,
121 "EphySpinnerCache",
122 &our_info, 0);
125 return type;
128 static EphySpinnerImages *
129 ephy_spinner_images_ref (EphySpinnerImages *images)
131 g_return_val_if_fail (images != NULL, NULL);
133 images->ref_count++;
135 return images;
138 static void
139 ephy_spinner_images_unref (EphySpinnerImages *images)
141 g_return_if_fail (images != NULL);
143 images->ref_count--;
144 if (images->ref_count == 0)
146 guint i;
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);
156 g_free (images);
160 static void
161 ephy_spinner_cache_data_unload (EphySpinnerCacheData *data)
163 GtkIconSize size;
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);
182 static GdkPixbuf *
183 extract_frame (GdkPixbuf *grid_pixbuf,
184 int x,
185 int y,
186 int size)
188 GdkPixbuf *pixbuf;
190 if (x + size > gdk_pixbuf_get_width (grid_pixbuf) ||
191 y + size > gdk_pixbuf_get_height (grid_pixbuf))
193 return NULL;
196 pixbuf = gdk_pixbuf_new_subpixbuf (grid_pixbuf,
197 x, y,
198 size, size);
199 g_return_val_if_fail (pixbuf != NULL, NULL);
201 return pixbuf;
204 static GdkPixbuf *
205 scale_to_size (GdkPixbuf *pixbuf,
206 int dw,
207 int dh)
209 GdkPixbuf *result;
210 int pw, ph;
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);
222 return result;
225 return 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;
237 const char *icon;
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,
251 SPINNER_ICON_NAME,
252 requested_size, 0);
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,
260 requested_size, 0);
261 if (icon_info == NULL)
263 g_warning ("Throbber fallback animation not found either");
264 goto loser;
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);
275 icon_info = NULL;
277 if (icon_pixbuf == NULL)
279 g_warning ("Could not load the spinner file");
280 goto loser;
283 grid_width = gdk_pixbuf_get_width (icon_pixbuf);
284 grid_height = gdk_pixbuf_get_height (icon_pixbuf);
286 n = 0;
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);
293 if (pixbuf)
295 list = g_slist_prepend (list, pixbuf);
296 ++n;
298 else
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;
308 g_assert (n > 0);
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;
333 g_assert (n == 0);
335 g_slist_free (list);
337 STOP_PROFILER ("loading spinner animation")
339 return images;
341 loser:
342 if (icon_info)
344 gtk_icon_info_free (icon_info);
346 g_slist_foreach (list, (GFunc) g_object_unref, NULL);
348 STOP_PROFILER ("loading spinner animation")
350 return NULL;
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),
364 data);
366 return data;
369 static void
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
376 (data->icon_theme,
377 G_CALLBACK (ephy_spinner_cache_data_unload), data);
379 ephy_spinner_cache_data_unload (data);
381 g_free (data);
384 static EphySpinnerImages *
385 ephy_spinner_cache_get_images (EphySpinnerCache *cache,
386 GdkScreen *screen,
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 < 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);
404 if (data == NULL)
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! */
415 return NULL;
418 if (images != NULL)
420 /* Return cached data */
421 return ephy_spinner_images_ref (images);
424 images = ephy_spinner_images_load (screen, data->icon_theme, icon_size);
426 if (images == NULL)
428 /* Mark as failed-to-load */
429 data->images[icon_size] = EPHY_SPINNER_IMAGES_INVALID;
431 return NULL;
434 data->images[icon_size] = images;
436 return ephy_spinner_images_ref (images);
439 static void
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,
449 NULL,
450 (GDestroyNotify) ephy_spinner_cache_data_free);
453 static void
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);
466 static void
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;
508 GtkIconSize size;
509 EphySpinnerImages *images;
510 guint current_image;
511 guint timeout;
512 guint timer_task;
513 guint spinning : 1;
514 guint need_load : 1;
517 static void ephy_spinner_class_init (EphySpinnerClass *class);
518 static void ephy_spinner_init (EphySpinner *spinner);
520 static GObjectClass *parent_class;
522 GType
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,
535 NULL,
536 NULL, /* class_data */
537 sizeof (EphySpinner),
538 0, /* n_preallocs */
539 (GInstanceInitFunc) ephy_spinner_init
542 type = g_type_register_static (GTK_TYPE_WIDGET,
543 "EphySpinner",
544 &our_info, 0);
547 return type;
550 static gboolean
551 ephy_spinner_load_images (EphySpinner *spinner)
553 EphySpinnerDetails *details = spinner->details;
555 if (details->need_load)
557 START_PROFILER ("ephy_spinner_load_images")
559 details->images =
560 ephy_spinner_cache_get_images
561 (details->cache,
562 gtk_widget_get_screen (GTK_WIDGET (spinner)),
563 details->size);
565 STOP_PROFILER ("ephy_spinner_load_images")
567 details->current_image = 0; /* 'rest' icon */
568 details->need_load = FALSE;
571 return details->images != NULL;
574 static void
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;
589 static void
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));
597 static void
598 ephy_spinner_init (EphySpinner *spinner)
600 EphySpinnerDetails *details;
602 details = spinner->details = EPHY_SPINNER_GET_PRIVATE (spinner);
604 gtk_widget_set_has_window (GTK_WIDGET (spinner), FALSE);
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;
613 static int
614 ephy_spinner_expose (GtkWidget *widget,
615 GdkEventExpose *event)
617 EphySpinner *spinner = EPHY_SPINNER (widget);
618 EphySpinnerDetails *details = spinner->details;
619 EphySpinnerImages *images;
620 GdkPixbuf *pixbuf;
621 GdkGC *gc;
622 int x_offset, y_offset, width, height;
623 GdkRectangle pix_area, dest;
624 GtkAllocation allocation;
626 if (!gtk_widget_is_drawable (GTK_WIDGET (spinner)))
628 return FALSE;
631 if (details->need_load &&
632 !ephy_spinner_load_images (spinner))
634 return FALSE;
637 images = details->images;
638 if (images == NULL)
640 return FALSE;
643 /* Otherwise |images| will be NULL anyway */
644 g_assert (images->n_animation_pixbufs > 0);
646 g_assert (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 gtk_widget_get_allocation (widget, &allocation);
657 x_offset = (allocation.width - width) / 2;
658 y_offset = (allocation.height - height) / 2;
660 pix_area.x = x_offset + allocation.x;
661 pix_area.y = y_offset + allocation.y;
662 pix_area.width = width;
663 pix_area.height = height;
665 if (!gdk_rectangle_intersect (&event->area, &pix_area, &dest))
667 return FALSE;
670 gc = gdk_gc_new (gtk_widget_get_window (widget));
671 gdk_draw_pixbuf (gtk_widget_get_window (widget), gc, pixbuf,
672 dest.x - x_offset - allocation.x,
673 dest.y - y_offset - allocation.y,
674 dest.x, dest.y,
675 dest.width, dest.height,
676 GDK_RGB_DITHER_MAX, 0, 0);
677 g_object_unref (gc);
679 return FALSE;
682 static gboolean
683 bump_spinner_frame_cb (EphySpinner *spinner)
685 EphySpinnerDetails *details = spinner->details;
687 /* This can happen when we've unloaded the images on a theme
688 * change, but haven't been in the queued size request yet.
689 * Just skip this update.
691 if (details->images == NULL) return TRUE;
693 details->current_image++;
694 if (details->current_image >= details->images->n_animation_pixbufs)
696 /* the 0th frame is the 'rest' icon */
697 details->current_image = MIN (1, details->images->n_animation_pixbufs);
700 gtk_widget_queue_draw (GTK_WIDGET (spinner));
702 /* run again */
703 return TRUE;
707 * ephy_spinner_start:
708 * @spinner: a #EphySpinner
710 * Start the spinner animation.
712 void
713 ephy_spinner_start (EphySpinner *spinner)
715 EphySpinnerDetails *details = spinner->details;
717 details->spinning = TRUE;
719 if (GTK_WIDGET_MAPPED (GTK_WIDGET (spinner)) &&
720 details->timer_task == 0 &&
721 ephy_spinner_load_images (spinner))
723 /* the 0th frame is the 'rest' icon */
724 details->current_image = MIN (1, details->images->n_animation_pixbufs);
726 details->timer_task =
727 g_timeout_add_full (G_PRIORITY_LOW,
728 details->timeout,
729 (GSourceFunc) bump_spinner_frame_cb,
730 spinner,
731 NULL);
735 static void
736 ephy_spinner_remove_update_callback (EphySpinner *spinner)
738 EphySpinnerDetails *details = spinner->details;
740 if (details->timer_task != 0)
742 g_source_remove (details->timer_task);
743 details->timer_task = 0;
748 * ephy_spinner_stop:
749 * @spinner: a #EphySpinner
751 * Stop the spinner animation.
753 void
754 ephy_spinner_stop (EphySpinner *spinner)
756 EphySpinnerDetails *details = spinner->details;
758 details->spinning = FALSE;
759 details->current_image = 0;
761 if (details->timer_task != 0)
763 ephy_spinner_remove_update_callback (spinner);
765 if (GTK_WIDGET_MAPPED (GTK_WIDGET (spinner)))
767 gtk_widget_queue_draw (GTK_WIDGET (spinner));
773 * ephy_spinner_set_size:
774 * @spinner: a #EphySpinner
775 * @size: the size of type %GtkIconSize
777 * Set the size of the spinner.
779 void
780 ephy_spinner_set_size (EphySpinner *spinner,
781 GtkIconSize size)
783 if (size == GTK_ICON_SIZE_INVALID)
785 size = GTK_ICON_SIZE_DIALOG;
788 if (size != spinner->details->size)
790 ephy_spinner_unload_images (spinner);
792 spinner->details->size = size;
794 gtk_widget_queue_resize (GTK_WIDGET (spinner));
798 #if 0
800 * ephy_spinner_set_timeout:
801 * @spinner: a #EphySpinner
802 * @timeout: time delay between updates to the spinner.
804 * Sets the timeout delay for spinner updates.
806 void
807 ephy_spinner_set_timeout (EphySpinner *spinner,
808 guint timeout)
810 EphySpinnerDetails *details = spinner->details;
812 if (timeout != details->timeout)
814 ephy_spinner_stop (spinner);
816 details->timeout = timeout;
818 if (details->spinning)
820 ephy_spinner_start (spinner);
824 #endif
826 static void
827 ephy_spinner_size_request (GtkWidget *widget,
828 GtkRequisition *requisition)
830 EphySpinner *spinner = EPHY_SPINNER (widget);
831 EphySpinnerDetails *details = spinner->details;
833 if ((details->need_load &&
834 !ephy_spinner_load_images (spinner)) ||
835 details->images == NULL)
837 requisition->width = requisition->height = 0;
838 gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget),
839 details->size,
840 &requisition->width,
841 &requisition->height);
842 return;
845 requisition->width = details->images->width;
846 requisition->height = details->images->height;
848 /* FIXME fix this hack */
849 /* allocate some extra margin so we don't butt up against toolbar edges */
850 if (details->size != GTK_ICON_SIZE_MENU)
852 requisition->width += 2;
853 requisition->height += 2;
857 static void
858 ephy_spinner_map (GtkWidget *widget)
860 EphySpinner *spinner = EPHY_SPINNER (widget);
861 EphySpinnerDetails *details = spinner->details;
863 GTK_WIDGET_CLASS (parent_class)->map (widget);
865 if (details->spinning)
867 ephy_spinner_start (spinner);
871 static void
872 ephy_spinner_unmap (GtkWidget *widget)
874 EphySpinner *spinner = EPHY_SPINNER (widget);
876 ephy_spinner_remove_update_callback (spinner);
878 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
881 static void
882 ephy_spinner_dispose (GObject *object)
884 EphySpinner *spinner = EPHY_SPINNER (object);
886 g_signal_handlers_disconnect_by_func
887 (spinner->details->icon_theme,
888 G_CALLBACK (icon_theme_changed_cb), spinner);
890 G_OBJECT_CLASS (parent_class)->dispose (object);
893 static void
894 ephy_spinner_finalize (GObject *object)
896 EphySpinner *spinner = EPHY_SPINNER (object);
898 ephy_spinner_remove_update_callback (spinner);
899 ephy_spinner_unload_images (spinner);
901 g_object_unref (spinner->details->cache);
903 G_OBJECT_CLASS (parent_class)->finalize (object);
906 static void
907 ephy_spinner_screen_changed (GtkWidget *widget,
908 GdkScreen *old_screen)
910 EphySpinner *spinner = EPHY_SPINNER (widget);
911 EphySpinnerDetails *details = spinner->details;
912 GdkScreen *screen;
914 if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
916 GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, old_screen);
919 screen = gtk_widget_get_screen (widget);
921 /* FIXME: this seems to be happening when then spinner is destroyed!? */
922 if (old_screen == screen) return;
924 /* We'll get mapped again on the new screen, but not unmapped from
925 * the old screen, so remove timeout here.
927 ephy_spinner_remove_update_callback (spinner);
929 ephy_spinner_unload_images (spinner);
931 if (old_screen != NULL)
933 g_signal_handlers_disconnect_by_func
934 (gtk_icon_theme_get_for_screen (old_screen),
935 G_CALLBACK (icon_theme_changed_cb), spinner);
938 details->icon_theme = gtk_icon_theme_get_for_screen (screen);
939 g_signal_connect (details->icon_theme, "changed",
940 G_CALLBACK (icon_theme_changed_cb), spinner);
943 static void
944 ephy_spinner_class_init (EphySpinnerClass *class)
946 GObjectClass *object_class = G_OBJECT_CLASS (class);
947 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
949 parent_class = g_type_class_peek_parent (class);
951 object_class->dispose = ephy_spinner_dispose;
952 object_class->finalize = ephy_spinner_finalize;
954 widget_class->expose_event = ephy_spinner_expose;
955 widget_class->size_request = ephy_spinner_size_request;
956 widget_class->map = ephy_spinner_map;
957 widget_class->unmap = ephy_spinner_unmap;
958 widget_class->screen_changed = ephy_spinner_screen_changed;
960 g_type_class_add_private (object_class, sizeof (EphySpinnerDetails));
964 * ephy_spinner_new:
966 * Create a new #EphySpinner. The spinner is a widget
967 * that gives the user feedback about network status with
968 * an animated image.
970 * Return Value: the spinner #GtkWidget
972 GtkWidget *
973 ephy_spinner_new (void)
975 return GTK_WIDGET (g_object_new (EPHY_TYPE_SPINNER, NULL));