Index: nautilus-file-management-properties.c
===================================================================
RCS file: /cvs/gnome/nautilus/src/nautilus-file-management-properties.c,v
retrieving revision 1.16
diff -u -3 -p -u -p -r1.16 nautilus-file-management-properties.c
--- nautilus-file-management-properties.c	16 Jul 2004 12:04:29 -0000	1.16
+++ nautilus-file-management-properties.c	18 Aug 2004 19:53:40 -0000
@@ -37,6 +37,9 @@
 #include <gtk/gtkoptionmenu.h>
 #include <gtk/gtksizegroup.h>
 
+#include <libgnomevfs/gnome-vfs.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
 #include <libgnome/gnome-help.h>
 #include <libgnome/gnome-i18n.h>
 
@@ -666,6 +669,12 @@ nautilus_file_management_properties_dial
 			  G_CALLBACK (nautilus_file_management_properties_dialog_response_cb),
 			  xml_dialog);
 
+	g_signal_connect (G_OBJECT (glade_xml_get_widget (xml_dialog, "preview_cleanup_button")),
+	  "clicked", G_CALLBACK(thumb_cleanup), glade_xml_get_widget (xml_dialog, "file_management_dialog"));
+			  
+ 	g_signal_connect (G_OBJECT (glade_xml_get_widget (xml_dialog, "preview_empty_button")),
+	  "clicked", G_CALLBACK(thumb_empty), glade_xml_get_widget (xml_dialog, "file_management_dialog"));
+
 	nautilus_file_management_properties_dialog_set_icons (GTK_WINDOW (dialog));
 
 	if (window) {
@@ -704,3 +713,205 @@ nautilus_file_management_properties_dial
 
 	nautilus_file_management_properties_dialog_setup (xml_dialog, window);
 }
+
+static int thumb_filesprocessed=0;
+static int thumb_filesunlinked=0;
+
+gboolean thumb_recurseclean(const gchar *rel_path,
+			GnomeVFSFileInfo *info,
+			gboolean recursing_will_loop,
+			gpointer data,
+			gboolean *recurse);
+
+/*Read the PNG metadata of a file, look for tEXt:Thumb:URI field
+ *and return TRUE if the field is not found or the file that 
+ *it points to is not found.*/
+gboolean
+thumb_is_stale (GnomeVFSURI *uri)
+{
+	GdkPixbuf *pixbuf;
+	gchar *thumb_targetstring;
+	gchar *filename;
+	const gchar *scheme;
+	GnomeVFSURI *target_uri;
+	gboolean is_stale=FALSE;
+	static int notagcount=5;
+
+	filename=g_strdup(uri->text);	
+	pixbuf=gdk_pixbuf_new_from_file(filename,NULL);
+	g_free(filename);
+
+	if(pixbuf){
+		thumb_targetstring=g_strdup(gdk_pixbuf_get_option(pixbuf,
+							"tEXt::Thumb::URI"));
+		
+		if(!thumb_targetstring){
+			if(notagcount>0){
+				g_warning("%s does not have property text::thumb::uri"\
+					"\n\tAssuming stale, deleting anyway!",uri->text);
+				notagcount--;
+			}
+			else if(notagcount==0){
+				g_warning("Suppressing further warnings about thumbs"\
+					"with no text:thumb:uri metadata.\n");
+				notagcount=-1;
+			}
+			return TRUE;
+		}
+
+		target_uri=gnome_vfs_uri_new(thumb_targetstring);
+
+		scheme=gnome_vfs_uri_get_scheme(target_uri);
+		
+		if(g_strstr_len(scheme,strlen(scheme),"file"))
+			if(!gnome_vfs_uri_exists(target_uri))
+				is_stale=TRUE;
+
+		gnome_vfs_uri_unref(target_uri);
+		
+		g_object_unref(pixbuf);
+
+		g_free(thumb_targetstring);
+	}
+	else
+		g_warning("Failed to open %s to check for staleness\n",filename);
+
+	return is_stale;
+}
+
+/*Act on each file examined in the recursion.
+ *	-If it's a png, judge it
+ *	-If it's a directory, descend into it*/
+gboolean
+thumb_recurseclean (const gchar *rel_path,
+                    GnomeVFSFileInfo *info,
+                    gboolean recursing_will_loop,
+                    gpointer data,
+                    gboolean *recurse)
+{
+	gchar* basedir=data;
+	gchar* fullname;
+	GnomeVFSURI *uri;
+
+	fullname=g_build_filename(basedir,info->name,NULL);
+	if(info->type==GNOME_VFS_FILE_TYPE_DIRECTORY){
+		gnome_vfs_directory_visit(fullname,
+			GNOME_VFS_FILE_INFO_GET_MIME_TYPE,
+			GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK,
+			thumb_recurseclean,fullname);
+	}
+	else{
+		thumb_filesprocessed++;
+		if(g_strrstr(info->mime_type,"image/png")){
+			uri=gnome_vfs_uri_new(fullname);
+			if(thumb_is_stale(uri)){
+				gnome_vfs_unlink_from_uri(uri);
+				thumb_filesunlinked++;
+			}
+			gnome_vfs_uri_unref(uri);
+		}
+		else{
+			/* What about these .info files I see in my .thumbnails?
+			 * Don't know who makes them.  Would be nice to tidy  
+			 * them away, but they don't take up much space so  
+			 * might as well let them stay*/
+		}
+	}
+	g_free(fullname);
+
+	return TRUE;
+}
+
+/*Act on each file examined in the recursion.
+ *	-If it's a png file, unlink it
+ *	-If it's a directory, descend into it*/
+gboolean
+thumb_recurseempty (const gchar *rel_path,
+                    GnomeVFSFileInfo *info,
+                    gboolean recursing_will_loop,
+                    gpointer data,
+                    gboolean *recurse)
+{
+	gchar* basedir=data;
+	gchar* fullname;
+	GnomeVFSURI *uri;
+
+	fullname=g_build_filename(basedir,info->name,NULL);
+	if(info->type==GNOME_VFS_FILE_TYPE_DIRECTORY){
+		gnome_vfs_directory_visit(fullname,
+			GNOME_VFS_FILE_INFO_GET_MIME_TYPE,
+			GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK,
+			thumb_recurseempty,fullname);
+	}
+	else{
+		if(g_strrstr(info->mime_type,"image/png")){
+			uri=gnome_vfs_uri_new(fullname);
+			gnome_vfs_unlink_from_uri(uri);
+			thumb_filesunlinked++;
+			gnome_vfs_uri_unref(uri);
+		}
+	}
+	g_free(fullname);
+
+	return TRUE;
+}
+
+
+/* Recurse through ~/.thumbnails and delete all png files that
+ * do not have a Thumb:URI metadata field that points to an 
+ * existing file.*/
+void
+thumb_cleanup (gpointer widget,gpointer data)
+{
+	gchar *thumb_dir,*thumb_dir_uristr;
+
+	gtk_widget_set_sensitive(GTK_WIDGET(data),FALSE);
+	while(gtk_events_pending())
+		gtk_main_iteration();
+
+	thumb_dir=g_build_filename(g_get_home_dir(),".thumbnails",NULL);
+	thumb_dir_uristr = g_strdup_printf("file://%s", thumb_dir);
+
+	g_free(thumb_dir);
+
+	gnome_vfs_directory_visit(thumb_dir_uristr,
+		GNOME_VFS_FILE_INFO_GET_MIME_TYPE,
+		GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK,
+		thumb_recurseclean,thumb_dir_uristr);
+
+	g_free(thumb_dir_uristr);
+
+	g_message("%d thumbnail files checked for staleness, %d deleted.",
+		thumb_filesprocessed,thumb_filesunlinked);
+	thumb_filesprocessed=thumb_filesunlinked=0;
+
+	gtk_widget_set_sensitive(GTK_WIDGET(data),TRUE);
+}
+
+/* Recurse through ~/.thumbnails and delete all png files*/
+void
+thumb_empty (gpointer widget,gpointer data)
+{
+	gchar *thumb_dir,*thumb_dir_uristr;
+
+	gtk_widget_set_sensitive(GTK_WIDGET(data),FALSE);
+	while(gtk_events_pending())
+		gtk_main_iteration();
+
+	thumb_dir=g_build_filename(g_get_home_dir(),".thumbnails",NULL);
+	thumb_dir_uristr = g_strdup_printf("file://%s", thumb_dir);
+
+	g_free(thumb_dir);
+
+	gnome_vfs_directory_visit(thumb_dir_uristr,
+		GNOME_VFS_FILE_INFO_GET_MIME_TYPE,
+		GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK,
+		thumb_recurseempty,thumb_dir_uristr);
+
+	g_free(thumb_dir_uristr);
+
+	g_message("%d files deleted.",thumb_filesunlinked);
+	thumb_filesunlinked=0;
+
+	gtk_widget_set_sensitive(GTK_WIDGET(data),TRUE);
+}
Index: nautilus-file-management-properties.h
===================================================================
RCS file: /cvs/gnome/nautilus/src/nautilus-file-management-properties.h,v
retrieving revision 1.2
diff -u -3 -p -u -p -r1.2 nautilus-file-management-properties.h
--- nautilus-file-management-properties.h	17 Feb 2003 09:20:56 -0000	1.2
+++ nautilus-file-management-properties.h	18 Aug 2004 19:53:59 -0000
@@ -32,6 +32,9 @@ G_BEGIN_DECLS
 
 void nautilus_file_management_properties_dialog_show (GCallback close_callback, GtkWindow *window);
 
+void thumb_cleanup(gpointer widget,gpointer data);
+void thumb_empty(gpointer widget,gpointer data);
+
 G_END_DECLS
 
 #endif /* NAUTILUS_FILE_MANAGEMENT_PROPERTIES_H */
Index: nautilus-file-management-properties.glade
===================================================================
RCS file: /cvs/gnome/nautilus/src/nautilus-file-management-properties.glade,v
retrieving revision 1.13
diff -u -3 -p -u -p -r1.13 nautilus-file-management-properties.glade
--- nautilus-file-management-properties.glade	16 Jul 2004 12:04:29 -0000	1.13
+++ nautilus-file-management-properties.glade	18 Aug 2004 19:54:05 -0000
@@ -2568,6 +2568,225 @@
 			      <property name="fill">True</property>
 			    </packing>
 			  </child>
+
+			  <child>
+			    <widget class="GtkHBox" id="hbox32">
+			      <property name="visible">True</property>
+			      <property name="homogeneous">False</property>
+			      <property name="spacing">0</property>
+
+			      <child>
+				<widget class="GtkLabel" id="label34">
+				  <property name="visible">True</property>
+				  <property name="label" translatable="yes"></property>
+				  <property name="use_underline">False</property>
+				  <property name="use_markup">False</property>
+				  <property name="justify">GTK_JUSTIFY_LEFT</property>
+				  <property name="wrap">False</property>
+				  <property name="selectable">False</property>
+				  <property name="xalign">0.5</property>
+				  <property name="yalign">0.5</property>
+				  <property name="xpad">0</property>
+				  <property name="ypad">0</property>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">True</property>
+				  <property name="fill">False</property>
+				</packing>
+			      </child>
+
+			      <child>
+				<widget class="GtkButton" id="preview_cleanup_button">
+				  <property name="visible">True</property>
+				  <property name="tooltip" translatable="yes">Delete cached thumbnails which are no longer needed</property>
+				  <property name="can_focus">True</property>
+				  <property name="relief">GTK_RELIEF_NORMAL</property>
+				  <property name="focus_on_click">True</property>
+
+				  <child>
+				    <widget class="GtkAlignment" id="alignment2">
+				      <property name="visible">True</property>
+				      <property name="xalign">0.5</property>
+				      <property name="yalign">0.5</property>
+				      <property name="xscale">0</property>
+				      <property name="yscale">0</property>
+				      <property name="top_padding">0</property>
+				      <property name="bottom_padding">0</property>
+				      <property name="left_padding">0</property>
+				      <property name="right_padding">0</property>
+
+				      <child>
+					<widget class="GtkHBox" id="hbox35">
+					  <property name="visible">True</property>
+					  <property name="homogeneous">False</property>
+					  <property name="spacing">2</property>
+
+					  <child>
+					    <widget class="GtkImage" id="image2">
+					      <property name="visible">True</property>
+					      <property name="stock">gtk-clear</property>
+					      <property name="icon_size">4</property>
+					      <property name="xalign">0.5</property>
+					      <property name="yalign">0.5</property>
+					      <property name="xpad">0</property>
+					      <property name="ypad">0</property>
+					    </widget>
+					    <packing>
+					      <property name="padding">0</property>
+					      <property name="expand">False</property>
+					      <property name="fill">False</property>
+					    </packing>
+					  </child>
+
+					  <child>
+					    <widget class="GtkLabel" id="label37">
+					      <property name="visible">True</property>
+					      <property name="label" translatable="yes">C_lean Up Thumbnail Cache</property>
+					      <property name="use_underline">True</property>
+					      <property name="use_markup">False</property>
+					      <property name="justify">GTK_JUSTIFY_LEFT</property>
+					      <property name="wrap">False</property>
+					      <property name="selectable">False</property>
+					      <property name="xalign">0.5</property>
+					      <property name="yalign">0.5</property>
+					      <property name="xpad">0</property>
+					      <property name="ypad">0</property>
+					    </widget>
+					    <packing>
+					      <property name="padding">0</property>
+					      <property name="expand">False</property>
+					      <property name="fill">False</property>
+					    </packing>
+					  </child>
+					</widget>
+				      </child>
+				    </widget>
+				  </child>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">False</property>
+				</packing>
+			      </child>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">True</property>
+			      <property name="fill">True</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkHBox" id="hbox33">
+			      <property name="visible">True</property>
+			      <property name="homogeneous">False</property>
+			      <property name="spacing">0</property>
+
+			      <child>
+				<widget class="GtkLabel" id="label35">
+				  <property name="visible">True</property>
+				  <property name="label" translatable="yes"></property>
+				  <property name="use_underline">False</property>
+				  <property name="use_markup">False</property>
+				  <property name="justify">GTK_JUSTIFY_LEFT</property>
+				  <property name="wrap">False</property>
+				  <property name="selectable">False</property>
+				  <property name="xalign">0.5</property>
+				  <property name="yalign">0.5</property>
+				  <property name="xpad">0</property>
+				  <property name="ypad">0</property>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">False</property>
+				</packing>
+			      </child>
+
+			      <child>
+				<widget class="GtkButton" id="preview_empty_button">
+				  <property name="visible">True</property>
+				  <property name="tooltip" translatable="yes">Delete all cached thumbnails</property>
+				  <property name="can_focus">True</property>
+				  <property name="relief">GTK_RELIEF_NORMAL</property>
+				  <property name="focus_on_click">True</property>
+
+				  <child>
+				    <widget class="GtkAlignment" id="alignment1">
+				      <property name="visible">True</property>
+				      <property name="xalign">0.5</property>
+				      <property name="yalign">0.5</property>
+				      <property name="xscale">0</property>
+				      <property name="yscale">0</property>
+				      <property name="top_padding">0</property>
+				      <property name="bottom_padding">0</property>
+				      <property name="left_padding">0</property>
+				      <property name="right_padding">0</property>
+
+				      <child>
+					<widget class="GtkHBox" id="hbox34">
+					  <property name="visible">True</property>
+					  <property name="homogeneous">False</property>
+					  <property name="spacing">2</property>
+
+					  <child>
+					    <widget class="GtkImage" id="image1">
+					      <property name="visible">True</property>
+					      <property name="stock">gtk-delete</property>
+					      <property name="icon_size">4</property>
+					      <property name="xalign">0.5</property>
+					      <property name="yalign">0.5</property>
+					      <property name="xpad">0</property>
+					      <property name="ypad">0</property>
+					    </widget>
+					    <packing>
+					      <property name="padding">0</property>
+					      <property name="expand">False</property>
+					      <property name="fill">False</property>
+					    </packing>
+					  </child>
+
+					  <child>
+					    <widget class="GtkLabel" id="label36">
+					      <property name="visible">True</property>
+					      <property name="label" translatable="yes">_Empty Thumbnail Cache</property>
+					      <property name="use_underline">True</property>
+					      <property name="use_markup">False</property>
+					      <property name="justify">GTK_JUSTIFY_LEFT</property>
+					      <property name="wrap">False</property>
+					      <property name="selectable">False</property>
+					      <property name="xalign">0.5</property>
+					      <property name="yalign">0.5</property>
+					      <property name="xpad">0</property>
+					      <property name="ypad">0</property>
+					    </widget>
+					    <packing>
+					      <property name="padding">0</property>
+					      <property name="expand">False</property>
+					      <property name="fill">False</property>
+					    </packing>
+					  </child>
+					</widget>
+				      </child>
+				    </widget>
+				  </child>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">False</property>
+				  <property name="pack_type">GTK_PACK_END</property>
+				</packing>
+			      </child>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">True</property>
+			      <property name="fill">True</property>
+			    </packing>
+			  </child>
 			</widget>
 			<packing>
 			  <property name="padding">0</property>
