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>
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
;
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 */
81 GdkPixbuf
**animation_pixbufs
;
82 guint n_animation_pixbufs
;
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)
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
;
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
),
114 (GClassInitFunc
) ephy_spinner_cache_class_init
,
117 sizeof (EphySpinnerCache
),
119 (GInstanceInitFunc
) ephy_spinner_cache_init
122 type
= g_type_register_static (G_TYPE_OBJECT
,
130 static EphySpinnerImages
*
131 ephy_spinner_images_ref (EphySpinnerImages
*images
)
133 g_return_val_if_fail (images
!= NULL
, NULL
);
141 ephy_spinner_images_unref (EphySpinnerImages
*images
)
143 g_return_if_fail (images
!= NULL
);
146 if (images
->ref_count
== 0)
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
);
163 ephy_spinner_cache_data_unload (EphySpinnerCacheData
*data
)
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
);
185 extract_frame (GdkPixbuf
*grid_pixbuf
,
192 if (x
+ size
> gdk_pixbuf_get_width (grid_pixbuf
) ||
193 y
+ size
> gdk_pixbuf_get_height (grid_pixbuf
))
198 pixbuf
= gdk_pixbuf_new_subpixbuf (grid_pixbuf
,
201 g_return_val_if_fail (pixbuf
!= NULL
, NULL
);
207 scale_to_size (GdkPixbuf
*pixbuf
,
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
);
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
;
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
,
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
,
263 if (icon_info
== NULL
)
265 g_warning ("Throbber fallback animation not found either");
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
);
279 if (icon_pixbuf
== NULL
)
281 g_warning ("Could not load the spinner file");
285 grid_width
= gdk_pixbuf_get_width (icon_pixbuf
);
286 grid_height
= gdk_pixbuf_get_height (icon_pixbuf
);
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
);
297 list
= g_slist_prepend (list
, pixbuf
);
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
;
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
;
339 STOP_PROFILER ("loading spinner animation")
346 gtk_icon_info_free (icon_info
);
348 g_slist_foreach (list
, (GFunc
) g_object_unref
, NULL
);
350 STOP_PROFILER ("loading spinner animation")
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
),
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
379 G_CALLBACK (ephy_spinner_cache_data_unload
), data
);
381 ephy_spinner_cache_data_unload (data
);
386 static EphySpinnerImages
*
387 ephy_spinner_cache_get_images (EphySpinnerCache
*cache
,
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
);
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! */
422 /* Return cached data */
423 return ephy_spinner_images_ref (images
);
426 images
= ephy_spinner_images_load (screen
, data
->icon_theme
, icon_size
);
430 /* Mark as failed-to-load */
431 data
->images
[icon_size
] = EPHY_SPINNER_IMAGES_INVALID
;
436 data
->images
[icon_size
] = images
;
438 return ephy_spinner_images_ref (images
);
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
,
452 (GDestroyNotify
) ephy_spinner_cache_data_free
);
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
);
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
;
511 EphySpinnerImages
*images
;
519 static void ephy_spinner_class_init (EphySpinnerClass
*class);
520 static void ephy_spinner_init (EphySpinner
*spinner
);
522 static GObjectClass
*parent_class
;
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
,
538 NULL
, /* class_data */
539 sizeof (EphySpinner
),
541 (GInstanceInitFunc
) ephy_spinner_init
544 type
= g_type_register_static (GTK_TYPE_WIDGET
,
553 ephy_spinner_load_images (EphySpinner
*spinner
)
555 EphySpinnerDetails
*details
= spinner
->details
;
557 if (details
->need_load
)
559 START_PROFILER ("ephy_spinner_load_images")
562 ephy_spinner_cache_get_images
564 gtk_widget_get_screen (GTK_WIDGET (spinner
)),
567 STOP_PROFILER ("ephy_spinner_load_images")
569 details
->current_image
= 0; /* 'rest' icon */
570 details
->need_load
= FALSE
;
573 return details
->images
!= NULL
;
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
;
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
));
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
;
616 ephy_spinner_expose (GtkWidget
*widget
,
617 GdkEventExpose
*event
)
619 EphySpinner
*spinner
= EPHY_SPINNER (widget
);
620 EphySpinnerDetails
*details
= spinner
->details
;
621 EphySpinnerImages
*images
;
624 int x_offset
, y_offset
, width
, height
;
625 GdkRectangle pix_area
, dest
;
627 if (!GTK_WIDGET_DRAWABLE (spinner
))
632 if (details
->need_load
&&
633 !ephy_spinner_load_images (spinner
))
638 images
= details
->images
;
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
))
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
,
676 dest
.width
, dest
.height
,
677 GDK_RGB_DITHER_MAX
, 0, 0);
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
));
708 * ephy_spinner_start:
709 * @spinner: a #EphySpinner
711 * Start the spinner animation.
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
,
730 (GSourceFunc
) bump_spinner_frame_cb
,
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;
750 * @spinner: a #EphySpinner
752 * Stop the spinner animation.
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.
781 ephy_spinner_set_size (EphySpinner
*spinner
,
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
));
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.
808 ephy_spinner_set_timeout (EphySpinner
*spinner
,
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
);
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
),
842 &requisition
->height
);
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;
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
);
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
);
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
);
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
);
908 ephy_spinner_screen_changed (GtkWidget
*widget
,
909 GdkScreen
*old_screen
)
911 EphySpinner
*spinner
= EPHY_SPINNER (widget
);
912 EphySpinnerDetails
*details
= spinner
->details
;
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
);
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
));
967 * Create a new #EphySpinner. The spinner is a widget
968 * that gives the user feedback about network status with
971 * Return Value: the spinner #GtkWidget
974 ephy_spinner_new (void)
976 return GTK_WIDGET (g_object_new (EPHY_TYPE_SPINNER
, NULL
));