diff -rN -u -p old-hidden/data/ui/rhythmbox-ui.xml new-hidden/data/ui/rhythmbox-ui.xml
--- old-hidden/data/ui/rhythmbox-ui.xml	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/data/ui/rhythmbox-ui.xml	2006-02-19 19:20:38.000000000 +1000
@@ -207,4 +207,9 @@
     <menuitem name="DeleteAutoPlaylistPopup" action="MusicPlaylistDeletePlaylist"/>
   </popup>
 
+  <popup name="MissingFilesViewPopup">
+    <menuitem name="DeleteMissingFilesPopup" action="EditDelete"/>
+    <menuitem name="PropertiesMissingFilesPopup" action="MusicProperties"/>
+  </popup>
+
 </ui>
diff -rN -u -p old-hidden/rhythmdb/rhythmdb.c new-hidden/rhythmdb/rhythmdb.c
--- old-hidden/rhythmdb/rhythmdb.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/rhythmdb/rhythmdb.c	2006-02-19 19:20:39.000000000 +1000
@@ -243,7 +243,6 @@ enum
 	ENTRY_DELETED,
 	LOAD_COMPLETE,
 	SAVE_COMPLETE,
-	LOAD_ERROR,
 	SAVE_ERROR,
 	READ_ONLY,
 	LAST_SIGNAL
@@ -333,18 +332,6 @@ rhythmdb_class_init (RhythmDBClass *klas
 			      G_TYPE_NONE,
 			      0);
 
-	rhythmdb_signals[LOAD_ERROR] =
-		g_signal_new ("load-error",
-			      G_OBJECT_CLASS_TYPE (object_class),
-			      G_SIGNAL_RUN_LAST,
-			      G_STRUCT_OFFSET (RhythmDBClass, load_error),
-			      NULL, NULL,
-			      rhythmdb_marshal_VOID__STRING_STRING,
-			      G_TYPE_NONE,
-			      2,
-			      G_TYPE_STRING,
-			      G_TYPE_STRING);
-
 	rhythmdb_signals[SAVE_ERROR] =
 		g_signal_new ("save-error",
 			      G_OBJECT_CLASS_TYPE (object_class),
@@ -1489,15 +1476,44 @@ typedef struct
 	char *msg;
 } RhythmDBLoadErrorData;
 
-static gboolean
-emit_load_error_idle (RhythmDBLoadErrorData *data)
+static void
+rhythmdb_add_import_error_entry (RhythmDB *db, GError *error, const char *uri)
 {
-	g_signal_emit (G_OBJECT (data->db), rhythmdb_signals[LOAD_ERROR], 0, data->uri, data->msg);
-	g_object_unref (G_OBJECT (data->db));
-	g_free (data->uri);
-	g_free (data->msg);
-	g_free (data);
-	return FALSE;
+	RhythmDBEntry *entry;
+	GValue value = {0,};
+	
+	if (g_error_matches (error, RB_METADATA_ERROR, RB_METADATA_ERROR_NOT_AUDIO_IGNORE)) {
+		rb_debug ("found file which will be ignored %s", uri);
+		return;
+	}
+
+	entry = rhythmdb_entry_lookup_by_location (db, uri);
+	if (entry) {
+		if (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TYPE) == RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR) {
+			/* we've already got an error for this file, so just update it */
+			g_value_init (&value, G_TYPE_STRING);
+			g_value_set_string (&value, error->message);
+			rhythmdb_entry_set (db, entry, RHYTHMDB_PROP_PLAYBACK_ERROR, &value);
+			g_value_unset (&value);
+
+			rhythmdb_commit_internal (db, FALSE);
+		} else {
+			/* we've successfully read this file before.. so what should we do? */
+			g_print ("file %s already exists in the library.. ignoring import error?", uri);
+		}
+	} else {
+		/* create a new import error entry */
+		entry = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR, uri);
+		
+		g_value_init (&value, G_TYPE_STRING);
+		g_value_set_string (&value, error->message);
+		rhythmdb_entry_set_uninserted (db, entry, RHYTHMDB_PROP_PLAYBACK_ERROR, &value);
+		g_value_unset (&value);
+	
+		rhythmdb_entry_set_visibility (db, entry, TRUE);
+
+		rhythmdb_commit_internal (db, FALSE);
+	}
 }
 
 static gboolean
@@ -1508,31 +1524,18 @@ rhythmdb_process_metadata_load (RhythmDB
 	const char *mime;
 	GTimeVal time;
 
-	if (event->error) {
-		if (g_error_matches (event->error, RB_METADATA_ERROR, RB_METADATA_ERROR_NOT_AUDIO_IGNORE)) {
-			rb_debug ("found file which will be ignored %s", event->real_uri);
-			return TRUE;
-		} else {
-			RhythmDBLoadErrorData *data;
-
-			rb_debug ("error loading %s: %s", event->real_uri, event->error->message);
-			data = g_new0 (RhythmDBLoadErrorData, 1);
-			g_object_ref (G_OBJECT (db));
-			data->db = db;
-			data->uri = g_strdup (event->real_uri);
-			data->msg = g_strdup (event->error->message);
-
-			g_idle_add ((GSourceFunc)emit_load_error_idle, data);
-			return TRUE;
-		}
-	}
-
 	if (rhythmdb_get_readonly (db)) {
 		rb_debug ("database is read-only right now, re-queuing event");
 		g_async_queue_push (db->priv->event_queue, event);
 		return FALSE;
 	}
 
+	if (event->error) {
+		rhythmdb_add_import_error_entry (db, event->error, event->real_uri);
+		return TRUE;
+	}
+
+	/* do we really need to do this? */
 	mime = rb_metadata_get_mime (event->metadata);
 	if (!mime) {
 		rb_debug ("unsupported file");
@@ -1577,6 +1580,8 @@ rhythmdb_process_metadata_load (RhythmDB
 	set_props_from_metadata (db, entry, event->vfsinfo, event->metadata);
 
 	/* we've seen this entry */
+	rhythmdb_entry_set_visibility (db, entry, TRUE);
+	
 	g_value_init (&value, G_TYPE_ULONG);
 	g_value_set_ulong (&value, time.tv_sec);
 	rhythmdb_entry_set_internal (db, entry, TRUE, RHYTHMDB_PROP_LAST_SEEN, &value);
@@ -1633,6 +1638,7 @@ rhythmdb_process_file_deleted (RhythmDB 
 	if (entry) {
 		rb_debug ("deleting entry for %s", event->uri);
 		rhythmdb_entry_set_visibility (db, entry, FALSE);
+		rhythmdb_commit (db);
 	}
 }
 
@@ -2611,13 +2617,25 @@ rhythmdb_entry_sync_mirrored (RhythmDB *
 	}
 	case RHYTHMDB_PROP_FIRST_SEEN:
 	{
-			rb_refstring_unref (entry->first_seen_str);
-
+		rb_refstring_unref (entry->first_seen_str);
 		val = eel_strdup_strftime (format, localtime ((glong*)&entry->first_seen));
 		entry->first_seen_str = rb_refstring_new_full (val, FALSE);
 		g_free (val);
 		break;
 	}
+	case RHYTHMDB_PROP_LAST_SEEN:
+	{
+		/* only store last seen time as a string for hidden entries */
+		rb_refstring_unref (entry->last_seen_str);
+		if (entry->hidden) {
+			val = eel_strdup_strftime (format, localtime ((glong*)&entry->last_seen));
+			entry->last_seen_str = rb_refstring_new_full (val, FALSE);
+			g_free (val);
+		} else {
+			entry->last_seen_str = NULL;
+		}
+		break;
+	}
 	default:
 		break;
 	}
@@ -2658,7 +2676,7 @@ rhythmdb_entry_move_to_trash_cb (GnomeVF
 }
 
 static void
-rhythmbd_entry_move_to_trash_set_error (RhythmDB *db, RhythmDBEntry *entry,
+rhythmdb_entry_move_to_trash_set_error (RhythmDB *db, RhythmDBEntry *entry,
 				        GnomeVFSResult res)
 {
 	GValue value = { 0, };
@@ -2684,7 +2702,7 @@ rhythmdb_entry_move_to_trash (RhythmDB *
 
 	uri = gnome_vfs_uri_new (entry->location);
 	if (uri == NULL) {
-		rhythmbd_entry_move_to_trash_set_error (db, entry, -1);
+		rhythmdb_entry_move_to_trash_set_error (db, entry, -1);
 		return;
 	}
 
@@ -2700,7 +2718,7 @@ rhythmdb_entry_move_to_trash (RhythmDB *
 		    res == GNOME_VFS_ERROR_NOT_SUPPORTED) {
 			rhythmdb_entry_set_visibility (db, entry, FALSE);
 		} else {
-			rhythmbd_entry_move_to_trash_set_error (db, entry, -1);
+			rhythmdb_entry_move_to_trash_set_error (db, entry, -1);
 		}
 
 		gnome_vfs_uri_unref (uri);
@@ -2722,7 +2740,8 @@ rhythmdb_entry_move_to_trash (RhythmDB *
 
 	shortname = gnome_vfs_uri_extract_short_name (uri);
 	if (shortname == NULL) {
-		rhythmbd_entry_move_to_trash_set_error (db, entry, -1);
+		rhythmdb_entry_move_to_trash_set_error (db, entry, -1);
+		rhythmdb_commit (db);
 		gnome_vfs_uri_unref (uri);
 		gnome_vfs_uri_unref (trash);
 		return;
@@ -2733,7 +2752,8 @@ rhythmdb_entry_move_to_trash (RhythmDB *
 	gnome_vfs_uri_unref (trash);
 	g_free (shortname);
 	if (dest == NULL) {
-		rhythmbd_entry_move_to_trash_set_error (db, entry, -1);
+		rhythmdb_entry_move_to_trash_set_error (db, entry, -1);
+		rhythmdb_commit (db);
 		gnome_vfs_uri_unref (uri);
 		return;
 	}
@@ -2749,8 +2769,9 @@ rhythmdb_entry_move_to_trash (RhythmDB *
 	if (res == GNOME_VFS_OK) {
 		rhythmdb_entry_set_visibility (db, entry, FALSE);
 	} else {
-		rhythmbd_entry_move_to_trash_set_error (db, entry, res);
+		rhythmdb_entry_move_to_trash_set_error (db, entry, res);
 	}
+	rhythmdb_commit (db);
 
 	gnome_vfs_uri_unref (dest);
 	gnome_vfs_uri_unref (uri);
@@ -3524,6 +3545,7 @@ rhythmdb_prop_get_type (void)
 			ENUM_ENTRY (RHYTHMDB_PROP_PLAYBACK_ERROR, "Playback error string (gchararray) [playback-error]"),
 			ENUM_ENTRY (RHYTHMDB_PROP_HIDDEN, "Hidden (gboolean) [hidden]"),
 			ENUM_ENTRY (RHYTHMDB_PROP_FIRST_SEEN_STR, "Time Added to Library (gchararray) [first-seen-str]"),
+			ENUM_ENTRY (RHYTHMDB_PROP_LAST_SEEN_STR, "Last time the song was available (gchararray) [last-seen-str]"),
 			ENUM_ENTRY (RHYTHMDB_PROP_SEARCH_MATCH, "Search matching key (gchararray) [search-match]"),
 			ENUM_ENTRY (RHYTHMDB_PROP_YEAR, "Year of date (gulong) [year]"),
 
@@ -3738,6 +3760,19 @@ RhythmDBEntryType rhythmdb_entry_podcast
 	return podcast_feed_type;
 }
 
+RhythmDBEntryType rhythmdb_entry_import_error_get_type (void) 
+{
+	static RhythmDBEntryType import_error_type = -1;
+       
+	g_static_mutex_lock (&entry_type_mutex);
+	if (import_error_type == -1) {
+		import_error_type = rhythmdb_entry_register_type ();
+	}
+	g_static_mutex_unlock (&entry_type_mutex);
+
+	return import_error_type;
+}
+
 
 typedef struct
 {
@@ -3793,6 +3828,7 @@ rhythmdb_volume_mounted_cb (GnomeVFSVolu
 	rhythmdb_entry_foreach (RHYTHMDB (data), 
 				(GFunc)entry_volume_mounted_or_unmounted, 
 				&ctxt);
+	rhythmdb_commit (RHYTHMDB (data));
 	g_free (ctxt.mount_point);
 }
 
@@ -3810,6 +3846,7 @@ rhythmdb_volume_unmounted_cb (GnomeVFSVo
 	rhythmdb_entry_foreach (RHYTHMDB (data), 
 				(GFunc)entry_volume_mounted_or_unmounted, 
 				&ctxt);
+	rhythmdb_commit (RHYTHMDB (data));
 	g_free (ctxt.mount_point);
 }
 
@@ -3852,6 +3889,8 @@ rhythmdb_entry_set_visibility (RhythmDB 
 		rhythmdb_entry_set_internal (db, entry, TRUE,
 					     RHYTHMDB_PROP_HIDDEN, &new_val);
 		g_value_unset (&new_val);
+
+		rhythmdb_entry_sync_mirrored (db, entry, RHYTHMDB_PROP_LAST_SEEN);
 	}
 	g_value_unset (&old_val);
 }
@@ -4207,6 +4246,8 @@ rhythmdb_entry_get_string (RhythmDBEntry
 		return entry->playback_error;
 	case RHYTHMDB_PROP_FIRST_SEEN_STR:
 		return rb_refstring_get (entry->first_seen_str);
+	case RHYTHMDB_PROP_LAST_SEEN_STR:
+		return rb_refstring_get (entry->last_seen_str);
 	case RHYTHMDB_PROP_SEARCH_MATCH:
 		return NULL;	/* synthetic property */
 	/* Podcast properties */
diff -rN -u -p old-hidden/rhythmdb/rhythmdb.h new-hidden/rhythmdb/rhythmdb.h
--- old-hidden/rhythmdb/rhythmdb.h	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/rhythmdb/rhythmdb.h	2006-02-19 19:20:39.000000000 +1000
@@ -50,6 +50,7 @@ typedef gint32 RhythmDBEntryType;
 #define RHYTHMDB_ENTRY_TYPE_IRADIO_STATION (rhythmdb_entry_iradio_get_type ())
 #define RHYTHMDB_ENTRY_TYPE_PODCAST_POST (rhythmdb_entry_podcast_post_get_type ())
 #define RHYTHMDB_ENTRY_TYPE_PODCAST_FEED (rhythmdb_entry_podcast_feed_get_type ())
+#define RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR (rhythmdb_entry_import_error_get_type ())
 
 typedef enum
 {
@@ -109,6 +110,7 @@ typedef enum
 	RHYTHMDB_PROP_HIDDEN,
 	RHYTHMDB_PROP_PLAYBACK_ERROR,
 	RHYTHMDB_PROP_FIRST_SEEN_STR,
+	RHYTHMDB_PROP_LAST_SEEN_STR,
 
 	/* synthetic properties */
 	RHYTHMDB_PROP_SEARCH_MATCH,
@@ -330,6 +332,7 @@ RhythmDBEntryType rhythmdb_entry_song_ge
 RhythmDBEntryType rhythmdb_entry_iradio_get_type        (void);
 RhythmDBEntryType rhythmdb_entry_podcast_post_get_type  (void);
 RhythmDBEntryType rhythmdb_entry_podcast_feed_get_type  (void);
+RhythmDBEntryType rhythmdb_entry_import_error_get_type	(void);
 RhythmDBEntryType rhythmdb_entry_icecast_get_type        (void);
 
 extern GType rhythmdb_property_type_map[RHYTHMDB_NUM_PROPERTIES];
diff -rN -u -p old-hidden/rhythmdb/rhythmdb-private.h new-hidden/rhythmdb/rhythmdb-private.h
--- old-hidden/rhythmdb/rhythmdb-private.h	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/rhythmdb/rhythmdb-private.h	2006-02-19 19:20:39.000000000 +1000
@@ -89,6 +89,7 @@ struct RhythmDBEntry_ {
 	/* cached data */
 	RBRefString *last_played_str;
 	RBRefString *first_seen_str;
+	RBRefString *last_seen_str;
 
 	/* playback error string */
 	char *playback_error;
diff -rN -u -p old-hidden/rhythmdb/rhythmdb-tree.c new-hidden/rhythmdb/rhythmdb-tree.c
--- old-hidden/rhythmdb/rhythmdb-tree.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/rhythmdb/rhythmdb-tree.c	2006-02-19 19:20:39.000000000 +1000
@@ -517,6 +517,7 @@ rhythmdb_tree_parser_end_element (struct
 		case RHYTHMDB_PROP_LAST_PLAYED_STR:
 		case RHYTHMDB_PROP_PLAYBACK_ERROR:
 		case RHYTHMDB_PROP_FIRST_SEEN_STR:
+		case RHYTHMDB_PROP_LAST_SEEN_STR:
 		case RHYTHMDB_PROP_SEARCH_MATCH:
 		case RHYTHMDB_PROP_YEAR:
 		case RHYTHMDB_NUM_PROPERTIES:
@@ -881,6 +882,7 @@ save_entry (RhythmDBTree *db, RhythmDBEn
 		case RHYTHMDB_PROP_LAST_PLAYED_STR:
 		case RHYTHMDB_PROP_PLAYBACK_ERROR:
 		case RHYTHMDB_PROP_FIRST_SEEN_STR:
+		case RHYTHMDB_PROP_LAST_SEEN_STR:
 		case RHYTHMDB_PROP_SEARCH_MATCH:
 		case RHYTHMDB_PROP_YEAR:
 		case RHYTHMDB_NUM_PROPERTIES:
diff -rN -u -p old-hidden/shell/rb-shell.c new-hidden/shell/rb-shell.c
--- old-hidden/shell/rb-shell.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/shell/rb-shell.c	2006-02-19 19:20:39.000000000 +1000
@@ -77,7 +77,6 @@
 #include "rb-daap-source.h"
 #include "rb-daap-sharing.h"
 #endif /* WITH_DAAP_SUPPORT */
-#include "rb-load-failure-dialog.h"
 #include "rb-iradio-source.h"
 #include "rb-shell-preferences.h"
 #include "rb-playlist-source.h"
@@ -85,6 +84,8 @@
 #include "rb-play-queue-source.h"
 #include "eel-gconf-extensions.h"
 #include "bacon-volume.h"
+#include "rb-missing-files-source.h"
+#include "rb-import-errors-source.h"
 
 #ifdef WITH_AUDIOSCROBBLER
 #include "rb-audioscrobbler.h"
@@ -135,23 +136,15 @@ static void rb_shell_playing_from_queue_
 static void source_activated_cb (RBSourceList *sourcelist,
 				 RBSource *source,
 				 RBShell *shell);
-static void rb_shell_db_load_error_cb (RhythmDB *db,
-				       const char *uri, const char *msg,
-				       RBShell *shell); 
 static void rb_shell_db_save_error_cb (RhythmDB *db,
 				       const char *uri, const GError *error,
 				       RBShell *shell); 
 static void rb_shell_db_entry_added_cb (RhythmDB *db,
 					RhythmDBEntry *entry,
 					RBShell *shell);
-static void rb_shell_load_failure_dialog_response_cb (GtkDialog *dialog,
-						      int response_id,
-						      RBShell *shell);
 
 static void rb_shell_playlist_added_cb (RBPlaylistManager *mgr, RBSource *source, RBShell *shell);
 static void rb_shell_playlist_created_cb (RBPlaylistManager *mgr, RBSource *source, RBShell *shell);
-static void rb_shell_playlist_load_start_cb (RBPlaylistManager *mgr, RBShell *shell);
-static void rb_shell_playlist_load_finish_cb (RBPlaylistManager *mgr, RBShell *shell);
 static void rb_shell_medium_added_cb (RBRemovableMediaManager *mgr, RBSource *source, RBShell *shell);
 static void rb_shell_source_deleted_cb (RBSource *source, RBShell *shell);
 static void rb_shell_set_window_title (RBShell *shell, const char *window_title);
@@ -377,14 +370,14 @@ struct RBShellPrivate
 	RBPlaylistManager *playlist_manager;
 	RBRemovableMediaManager *removable_media_manager;
 
-	GtkWidget *load_error_dialog;
 	GList *supported_media_extensions;
-	gboolean show_db_errors;
 
 	RBLibrarySource *library_source;
 	RBIRadioSource *iradio_source;
 	RBPodcastSource *podcast_source;
 	RBPlaylistSource *queue_source;
+	RBSource *missing_files_source;
+	RBSource *import_errors_source;
 
 #ifdef WITH_AUDIOSCROBBLER
 	RBAudioscrobbler *audioscrobbler;
@@ -868,7 +861,6 @@ rb_shell_finalize (GObject *object)
 	eel_gconf_notification_remove (shell->priv->toolbar_visibility_notify_id);
 	eel_gconf_notification_remove (shell->priv->smalldisplay_notify_id);
 
-	gtk_widget_destroy (GTK_WIDGET (shell->priv->load_error_dialog));
 	g_list_free (shell->priv->supported_media_extensions);
 
 	gtk_widget_destroy (GTK_WIDGET (shell->priv->tray_icon));
@@ -1183,20 +1175,11 @@ rb_shell_constructor (GType type,
 	rb_shell_sync_sourcelist_visibility (shell);
 	rb_shell_sync_pane_visibility (shell);
 
-	shell->priv->load_error_dialog = rb_load_failure_dialog_new ();
-	shell->priv->show_db_errors = FALSE;
-	gtk_widget_hide (shell->priv->load_error_dialog);
-
-	g_signal_connect_object (G_OBJECT (shell->priv->db), "load-error",
-				 G_CALLBACK (rb_shell_db_load_error_cb), shell, 0);
 	g_signal_connect_object (G_OBJECT (shell->priv->db), "save-error",
 				 G_CALLBACK (rb_shell_db_save_error_cb), shell, 0);
 	g_signal_connect_object (G_OBJECT (shell->priv->db), "entry-added",
 				 G_CALLBACK (rb_shell_db_entry_added_cb), shell, 0);
 
-	g_signal_connect_object (G_OBJECT (shell->priv->load_error_dialog), "response",
-				 G_CALLBACK (rb_shell_load_failure_dialog_response_cb), shell, 0);
-
 
 	shell->priv->library_source = RB_LIBRARY_SOURCE (rb_library_source_new (shell));
 	rb_shell_append_source (shell, RB_SOURCE (shell->priv->library_source), NULL);
@@ -1205,6 +1188,10 @@ rb_shell_constructor (GType type,
 	shell->priv->podcast_source = RB_PODCAST_SOURCE (rb_podcast_source_new (shell));
 	rb_shell_append_source (shell, RB_SOURCE (shell->priv->podcast_source), NULL);
 
+	shell->priv->missing_files_source = rb_missing_files_source_new (shell, shell->priv->library_source);
+	rb_shell_append_source (shell, shell->priv->missing_files_source, RB_SOURCE (shell->priv->library_source));
+	shell->priv->import_errors_source = rb_import_errors_source_new (shell, shell->priv->library_source);
+	rb_shell_append_source (shell, shell->priv->import_errors_source, RB_SOURCE (shell->priv->library_source));
 
 	/* Initialize playlist manager */
 	rb_debug ("shell: creating playlist manager");
@@ -1217,10 +1204,6 @@ rb_shell_constructor (GType type,
 				 G_CALLBACK (rb_shell_playlist_added_cb), shell, 0);
 	g_signal_connect_object (G_OBJECT (shell->priv->playlist_manager), "playlist_created",
 				 G_CALLBACK (rb_shell_playlist_created_cb), shell, 0);
-	g_signal_connect_object (G_OBJECT (shell->priv->playlist_manager), "load_start",
-				 G_CALLBACK (rb_shell_playlist_load_start_cb), shell, 0);
-	g_signal_connect_object (G_OBJECT (shell->priv->playlist_manager), "load_finish",
-				 G_CALLBACK (rb_shell_playlist_load_finish_cb), shell, 0);
 
 #ifdef WITH_DAAP_SUPPORT
 	rb_daap_sources_init (shell);
@@ -1309,7 +1292,6 @@ rb_shell_constructor (GType type,
 	/* Stop here if this is the first time. */
 	if (!eel_gconf_get_boolean (CONF_FIRST_TIME)) {
 		RBDruid *druid;
-		shell->priv->show_db_errors = TRUE;
 		druid = rb_druid_new (shell->priv->db);
 		gtk_widget_hide (GTK_WIDGET (shell->priv->window));
 		rb_druid_show (druid);
@@ -1584,28 +1566,6 @@ source_activated_cb (RBSourceList *sourc
 }
 
 static void
-rb_shell_db_load_error_cb (RhythmDB *db,
-			   const char *uri, const char *msg,
-		  	   RBShell *shell)
-{
-	rb_debug ("got db error, showing: %s",
-		  shell->priv->show_db_errors ? "TRUE" : "FALSE");
-
-	if (shell->priv->pending_entry && 
-	    strcmp(uri, shell->priv->pending_entry) == 0) {
-		g_free (shell->priv->pending_entry);
-		shell->priv->pending_entry = NULL;
-	}
-	
-	if (!shell->priv->show_db_errors)
-		return;
-
-	rb_load_failure_dialog_add (RB_LOAD_FAILURE_DIALOG (shell->priv->load_error_dialog),
-				    uri, msg);
-	gtk_widget_show (GTK_WIDGET (shell->priv->load_error_dialog));
-}
-
-static void
 rb_shell_db_save_error_cb (RhythmDB *db,
 			   const char *uri, const GError *error,
 		  	   RBShell *shell)
@@ -1632,15 +1592,6 @@ rb_shell_db_entry_added_cb (RhythmDB *db
 	}
 }
 
-static void
-rb_shell_load_failure_dialog_response_cb (GtkDialog *dialog,
-					  int response_id,
-					  RBShell *shell)
-{
-	rb_debug ("got response");
-	shell->priv->show_db_errors = FALSE;
-}
-
 static RBSource *
 rb_shell_get_source_by_entry_type (RBShell *shell,
 				   RhythmDBEntryType type)
@@ -1705,20 +1656,6 @@ rb_shell_playlist_created_cb (RBPlaylist
 }
 
 static void
-rb_shell_playlist_load_start_cb (RBPlaylistManager *mgr,
-				 RBShell *shell)
-{
-	shell->priv->show_db_errors = TRUE;
-}
-
-static void
-rb_shell_playlist_load_finish_cb (RBPlaylistManager *mgr,
-				  RBShell *shell)
-{
-	shell->priv->show_db_errors = FALSE;
-}
-
-static void
 rb_shell_medium_added_cb (RBRemovableMediaManager *mgr,
 			  RBSource *source,
 			  RBShell *shell)
@@ -1796,7 +1733,7 @@ rb_shell_playing_entry_changed_cb (RBShe
 
 	g_signal_emit_by_name (RB_REMOTE_PROXY (shell), "song_changed", &song);
 
-	// Translators: Trackname by Artist
+	/* Translators: Trackname by Artist */
 	notifytitle = g_strdup_printf (_("%s by %s"),
 				       song.title, song.artist);
 	rb_shell_hidden_notify (shell, 4000, _("Now Playing"), NULL, notifytitle);
@@ -2183,8 +2120,6 @@ add_to_library_response_cb (GtkDialog *d
 		uri_list = g_slist_prepend (uri_list, g_strdup (current_dir));
 	}
 
-	shell->priv->show_db_errors = TRUE;
-
 	for (uris = uri_list; uris; uris = uris->next) {
 			rb_shell_load_uri (shell, (char *)uris->data, FALSE, NULL);
 			g_free (uris->data);
diff -rN -u -p old-hidden/shell/rb-shell-player.c new-hidden/shell/rb-shell-player.c
--- old-hidden/shell/rb-shell-player.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/shell/rb-shell-player.c	2006-02-19 19:20:39.000000000 +1000
@@ -1764,6 +1764,14 @@ rb_shell_player_entry_activated_cb (RBEn
 
 	rb_debug  ("got entry %p activated", entry);
 
+	/* don't play hidden entries */
+	if (rhythmdb_entry_get_boolean (entry, RHYTHMDB_PROP_HIDDEN))
+		return;
+
+	/* don't play import error entries */
+	if (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TYPE) == RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR)
+		return;
+
 	/* ensure the podcast has been downloaded */
 	if (rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TYPE) == RHYTHMDB_ENTRY_TYPE_PODCAST_POST) {
 		if (!rb_podcast_manager_entry_downloaded (entry))
diff -rN -u -p old-hidden/sources/Makefile.am new-hidden/sources/Makefile.am
--- old-hidden/sources/Makefile.am	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/sources/Makefile.am	2006-02-19 19:20:39.000000000 +1000
@@ -30,7 +30,11 @@ libsourcesimpl_la_SOURCES =		\
 	rb-generic-player-source.c	\
 	rb-generic-player-source.h	\
 	rb-play-queue-source.c		\
-	rb-play-queue-source.h
+	rb-play-queue-source.h		\
+	rb-missing-files-source.c	\
+	rb-missing-files-source.h	\
+	rb-import-errors-source.c	\
+	rb-import-errors-source.h
 
 INCLUDES =						\
         -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
diff -rN -u -p old-hidden/sources/rb-import-errors-source.c new-hidden/sources/rb-import-errors-source.c
--- old-hidden/sources/rb-import-errors-source.c	1970-01-01 10:00:00.000000000 +1000
+++ new-hidden/sources/rb-import-errors-source.c	2006-02-19 19:20:39.000000000 +1000
@@ -0,0 +1,211 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Copyright (C) 2006  Jonathan Matthew <jonathan@kaolin.wh9.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+/*
+ * This source lists files rhythmbox failed to import.
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "rb-entry-view.h"
+#include "rb-import-errors-source.h"
+#include "rb-util.h"
+
+static void rb_import_errors_source_class_init (RBImportErrorsSourceClass *klass);
+static void rb_import_errors_source_init (RBImportErrorsSource *source);
+static GObject *rb_import_errors_source_constructor (GType type, guint n_construct_properties,
+						     GObjectConstructParam *construct_properties);
+static void rb_import_errors_source_dispose (GObject *object);
+
+static RBEntryView *impl_get_entry_view (RBSource *source);
+static void impl_delete (RBSource *source);
+
+struct RBImportErrorsSourcePrivate
+{
+	RhythmDB *db;
+	RhythmDBQueryModel *model;
+	RBEntryView *view;
+	RhythmDBEntryType entry_type;
+};
+
+G_DEFINE_TYPE (RBImportErrorsSource, rb_import_errors_source, RB_TYPE_SOURCE);
+
+static void
+rb_import_errors_source_class_init (RBImportErrorsSourceClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
+
+	object_class->dispose = rb_import_errors_source_dispose;
+	object_class->constructor = rb_import_errors_source_constructor;
+
+	source_class->impl_can_browse = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_get_entry_view = impl_get_entry_view;
+	source_class->impl_can_rename = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_search = (RBSourceFeatureFunc) rb_false_function;
+
+	source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_add_to_queue = (RBSourceFeatureFunc) rb_false_function;
+
+	source_class->impl_delete = impl_delete;
+
+	source_class->impl_try_playlist = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_pause = (RBSourceFeatureFunc) rb_false_function;
+
+	source_class->impl_have_url = (RBSourceFeatureFunc) rb_false_function;
+	
+	g_type_class_add_private (klass, sizeof (RBImportErrorsSourcePrivate));
+}
+
+static void
+rb_import_errors_source_init (RBImportErrorsSource *source)
+{
+	gint size;
+	GdkPixbuf *pixbuf;
+
+	source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source, RB_TYPE_IMPORT_ERRORS_SOURCE, RBImportErrorsSourcePrivate);
+	
+	gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &size, NULL);
+	pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+					   GTK_STOCK_DIALOG_ERROR,
+					   size,
+					   0, NULL);
+	rb_source_set_pixbuf (RB_SOURCE (source), pixbuf);
+	if (pixbuf != NULL) {
+		g_object_unref (pixbuf);
+	}
+}
+
+static GObject *
+rb_import_errors_source_constructor (GType type, guint n_construct_properties,
+				     GObjectConstructParam *construct_properties)
+{
+	GObject *shell_player;
+	RBImportErrorsSource *source;
+	RBImportErrorsSourceClass *klass;
+	RBShell *shell;
+	GPtrArray *query;
+
+	klass = RB_IMPORT_ERRORS_SOURCE_CLASS (g_type_class_peek (RB_TYPE_IMPORT_ERRORS_SOURCE));
+
+	source = RB_IMPORT_ERRORS_SOURCE (G_OBJECT_CLASS (rb_import_errors_source_parent_class)->
+			constructor (type, n_construct_properties, construct_properties));
+
+	g_object_get (G_OBJECT (source), "shell", &shell, NULL);
+	g_object_get (G_OBJECT (shell), "db", &source->priv->db, NULL);
+	shell_player = rb_shell_get_player (shell);
+	g_object_unref (G_OBJECT (shell));
+	
+	/* construct real query */
+	query = rhythmdb_query_parse (source->priv->db,
+				      RHYTHMDB_QUERY_PROP_EQUALS,
+				      	RHYTHMDB_PROP_TYPE,
+					RHYTHMDB_ENTRY_TYPE_IMPORT_ERROR,
+				      RHYTHMDB_QUERY_END);
+	source->priv->model = rhythmdb_query_model_new (source->priv->db, query,
+							(GCompareDataFunc) rhythmdb_query_model_string_sort_func,
+							RHYTHMDB_PROP_LOCATION, FALSE);
+	_rb_source_hide_when_empty (RB_SOURCE (source), source->priv->model);
+
+	g_ptr_array_free (query, TRUE);
+
+	/* set up entry view */
+	source->priv->view = rb_entry_view_new (source->priv->db, shell_player, 
+						NULL, FALSE, FALSE);
+
+	rb_entry_view_set_model (source->priv->view, source->priv->model);
+
+	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_LOCATION, TRUE);
+	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_ERROR, TRUE);
+	
+	gtk_container_add (GTK_CONTAINER (source), GTK_WIDGET (source->priv->view));
+		
+	gtk_widget_show_all (GTK_WIDGET (source));
+
+	/* use a fake model for the source's query model property, so we can't try to play
+	 * any of the hidden entries.
+	 */
+	g_object_set (G_OBJECT (source), 
+		      "query-model", rhythmdb_query_model_new_empty (source->priv->db), 
+		      NULL);
+
+	return G_OBJECT (source);
+}
+
+static void
+rb_import_errors_source_dispose (GObject *object)
+{
+	RBImportErrorsSource *source = RB_IMPORT_ERRORS_SOURCE (object);
+
+	if (source->priv->db) {
+		g_object_unref (G_OBJECT (source->priv->db));
+		source->priv->db = NULL;
+	}
+	if (source->priv->model) {
+		g_object_unref (G_OBJECT (source->priv->model));
+		source->priv->model = NULL;
+	}
+
+	G_OBJECT_CLASS (rb_import_errors_source_parent_class)->dispose (object);
+}
+
+
+static RBEntryView *
+impl_get_entry_view (RBSource *asource)
+{
+	RBImportErrorsSource *source = RB_IMPORT_ERRORS_SOURCE (asource);
+	return source->priv->view;
+}
+
+RBSource *
+rb_import_errors_source_new (RBShell *shell,
+			     RBLibrarySource *library)
+{
+	RBSource *source;
+	RhythmDBEntryType entry_type;
+
+	g_object_get (G_OBJECT (library), "entry-type", &entry_type, NULL);
+	source = RB_SOURCE (g_object_new (RB_TYPE_IMPORT_ERRORS_SOURCE,
+					  "name", _("Import Errors"),
+					  "shell", shell,
+					  "visibility", FALSE,
+					  NULL));
+	return source;
+}
+
+static void
+impl_delete (RBSource *asource)
+{
+	RBImportErrorsSource *source = RB_IMPORT_ERRORS_SOURCE (asource);
+	GList *sel, *tem;
+
+	sel = rb_entry_view_get_selected_entries (source->priv->view);
+	for (tem = sel; tem != NULL; tem = tem->next) {
+		rhythmdb_entry_delete (source->priv->db, tem->data);
+		rhythmdb_commit (source->priv->db);
+	}
+	g_list_free (sel);
+}
+
diff -rN -u -p old-hidden/sources/rb-import-errors-source.h new-hidden/sources/rb-import-errors-source.h
--- old-hidden/sources/rb-import-errors-source.h	1970-01-01 10:00:00.000000000 +1000
+++ new-hidden/sources/rb-import-errors-source.h	2006-02-19 19:20:39.000000000 +1000
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (C) 2006  Jonathan Matthew  <jonathan@kaolin.wh9.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_IMPORT_ERRORS_SOURCE_H
+#define __RB_IMPORT_ERRORS_SOURCE_H
+
+#include "rb-shell.h"
+#include "rb-source.h"
+#include "rb-library-source.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_IMPORT_ERRORS_SOURCE         (rb_import_errors_source_get_type ())
+#define RB_IMPORT_ERRORS_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_IMPORT_ERRORS_SOURCE, RBImportErrorsSource))
+#define RB_IMPORT_ERRORS_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_IMPORT_ERRORS_SOURCE, RBImportErrorsSourceClass))
+#define RB_IS_IMPORT_ERRORS_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_IMPORT_ERRORS_SOURCE))
+#define RB_IS_IMPORT_ERRORS_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_IMPORT_ERRORS_SOURCE))
+#define RB_IMPORT_ERRORS_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_IMPORT_ERRORS_SOURCE, RBImportErrorsSourceClass))
+
+typedef struct RBImportErrorsSourcePrivate RBImportErrorsSourcePrivate;
+
+typedef struct
+{
+	RBSource parent;
+
+	RBImportErrorsSourcePrivate *priv;
+} RBImportErrorsSource;
+
+typedef struct
+{
+	RBSourceClass parent;
+} RBImportErrorsSourceClass;
+
+GType		rb_import_errors_source_get_type		(void);
+
+RBSource *      rb_import_errors_source_new			(RBShell *shell,
+								 RBLibrarySource *library_source);
+
+G_END_DECLS
+
+#endif /* __RB_IMPORT_ERRORS_SOURCE_H */
+
diff -rN -u -p old-hidden/sources/rb-iradio-source.c new-hidden/sources/rb-iradio-source.c
--- old-hidden/sources/rb-iradio-source.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/sources/rb-iradio-source.c	2006-02-19 19:20:39.000000000 +1000
@@ -304,12 +304,12 @@ rb_iradio_source_constructor (GType type
 	source->priv->stations = rb_entry_view_new (source->priv->db, shell_player,
 						    CONF_STATE_IRADIO_SORTING,
 						    FALSE, FALSE);
-	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_TITLE);
-	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_GENRE);
-/* 	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_QUALITY); */
-	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_RATING);
-/*	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_PLAY_COUNT);*/
-	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_LAST_PLAYED);
+	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_TITLE, TRUE);
+	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_GENRE, FALSE);
+/* 	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_QUALITY, FALSE); */
+	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_RATING, FALSE);
+/*	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);*/
+	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
 	g_signal_connect_object (G_OBJECT (source->priv->stations),
 				 "sort-order-changed",
 				 G_CALLBACK (rb_iradio_source_songs_view_sort_order_changed_cb),
diff -rN -u -p old-hidden/sources/rb-library-source.c new-hidden/sources/rb-library-source.c
--- old-hidden/sources/rb-library-source.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/sources/rb-library-source.c	2006-02-19 19:20:39.000000000 +1000
@@ -391,19 +391,20 @@ rb_library_source_constructor (GType typ
 	source->priv->songs = rb_entry_view_new (source->priv->db, shell_player,
 						 source->priv->sorting_key,
 						 TRUE, FALSE);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TRACK_NUMBER);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TITLE);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_GENRE);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ARTIST);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ALBUM);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_YEAR);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_DURATION);
- 	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_QUALITY);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_RATING);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED);
+
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TITLE, TRUE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_GENRE, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_YEAR, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_DURATION, FALSE);
+ 	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_QUALITY, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_RATING, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
 	if (rb_library_source_has_first_added_column (source))
-		rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_FIRST_SEEN);
+		rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_FIRST_SEEN, FALSE);
 
 	g_signal_connect_object (G_OBJECT (source->priv->songs), "show_popup",
 				 G_CALLBACK (rb_library_source_songs_show_popup_cb), source, 0);
diff -rN -u -p old-hidden/sources/rb-missing-files-source.c new-hidden/sources/rb-missing-files-source.c
--- old-hidden/sources/rb-missing-files-source.c	1970-01-01 10:00:00.000000000 +1000
+++ new-hidden/sources/rb-missing-files-source.c	2006-02-19 19:20:39.000000000 +1000
@@ -0,0 +1,335 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Copyright (C) 2006  Jonathan Matthew <jonathan@kaolin.wh9.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+/*
+ * This source lists files rhythmbox cannot find, and maybe tries to stop 
+ * you from trying to play them.
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "rb-entry-view.h"
+#include "rb-missing-files-source.h"
+#include "rb-song-info.h"
+#include "rb-util.h"
+#include "rb-debug.h"
+
+
+static void rb_missing_files_source_class_init (RBMissingFilesSourceClass *klass);
+static void rb_missing_files_source_init (RBMissingFilesSource *source);
+static GObject *rb_missing_files_source_constructor (GType type, guint n_construct_properties,
+						     GObjectConstructParam *construct_properties);
+static void rb_missing_files_source_dispose (GObject *object);
+static void rb_missing_files_source_set_property (GObject *object,
+						  guint prop_id,
+						  const GValue *value,
+						  GParamSpec *pspec);
+static void rb_missing_files_source_get_property (GObject *object,
+						  guint prop_id,
+						  GValue *value,
+						  GParamSpec *pspec);
+
+static RBEntryView *impl_get_entry_view (RBSource *source);
+static void impl_song_properties (RBSource *source);
+static void impl_delete (RBSource *source);
+
+static void rb_missing_files_source_songs_show_popup_cb (RBEntryView *view,
+							 gboolean over_entry,
+							 RBMissingFilesSource *source);
+static void rb_missing_files_source_songs_sort_order_changed_cb (RBEntryView *view, 
+								 RBMissingFilesSource *source); 
+
+#define MISSING_FILES_SOURCE_SONGS_POPUP_PATH "/MissingFilesViewPopup"
+
+struct RBMissingFilesSourcePrivate
+{
+	RhythmDB *db;
+	RhythmDBQueryModel *model;
+	RBEntryView *view;
+	RhythmDBEntryType entry_type;
+};
+
+enum
+{
+	PROP_0,
+	PROP_ENTRY_TYPE
+};
+
+G_DEFINE_TYPE (RBMissingFilesSource, rb_missing_files_source, RB_TYPE_SOURCE);
+
+static void
+rb_missing_files_source_class_init (RBMissingFilesSourceClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
+
+	object_class->dispose = rb_missing_files_source_dispose;
+	object_class->constructor = rb_missing_files_source_constructor;
+
+	object_class->set_property = rb_missing_files_source_set_property;
+	object_class->get_property = rb_missing_files_source_get_property;
+
+	source_class->impl_can_browse = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_get_entry_view = impl_get_entry_view;
+	source_class->impl_can_rename = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_search = (RBSourceFeatureFunc) rb_false_function;
+
+	source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
+	source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_copy = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_add_to_queue = (RBSourceFeatureFunc) rb_false_function;
+
+	source_class->impl_delete = impl_delete;
+
+	source_class->impl_song_properties = impl_song_properties;
+	source_class->impl_try_playlist = (RBSourceFeatureFunc) rb_false_function;
+	source_class->impl_can_pause = (RBSourceFeatureFunc) rb_false_function;
+
+	source_class->impl_have_url = (RBSourceFeatureFunc) rb_false_function;
+	
+	g_object_class_install_property (object_class,
+					 PROP_ENTRY_TYPE,
+					 g_param_spec_uint ("entry-type",
+							    "Entry type",
+							    "Type of the entries which should be displayed by this source",
+							    0,
+							    G_MAXINT,
+							    RHYTHMDB_ENTRY_TYPE_SONG,
+							    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	
+	g_type_class_add_private (klass, sizeof (RBMissingFilesSourcePrivate));
+}
+
+static void
+rb_missing_files_source_init (RBMissingFilesSource *source)
+{
+	gint size;
+	GdkPixbuf *pixbuf;
+
+	source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source, RB_TYPE_MISSING_FILES_SOURCE, RBMissingFilesSourcePrivate);
+	
+	gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &size, NULL);
+	pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+					   GTK_STOCK_MISSING_IMAGE,
+					   size,
+					   0, NULL);
+	rb_source_set_pixbuf (RB_SOURCE (source), pixbuf);
+	if (pixbuf != NULL) {
+		g_object_unref (pixbuf);
+	}
+}
+
+static GObject *
+rb_missing_files_source_constructor (GType type, guint n_construct_properties,
+				     GObjectConstructParam *construct_properties)
+{
+	GObject *shell_player;
+	RBMissingFilesSource *source;
+	RBMissingFilesSourceClass *klass;
+	RBShell *shell;
+	GPtrArray *query;
+
+	klass = RB_MISSING_FILES_SOURCE_CLASS (g_type_class_peek (RB_TYPE_MISSING_FILES_SOURCE));
+
+	source = RB_MISSING_FILES_SOURCE (G_OBJECT_CLASS (rb_missing_files_source_parent_class)->
+			constructor (type, n_construct_properties, construct_properties));
+
+	g_object_get (G_OBJECT (source), "shell", &shell, NULL);
+	g_object_get (G_OBJECT (shell), "db", &source->priv->db, NULL);
+	shell_player = rb_shell_get_player (shell);
+	g_object_unref (G_OBJECT (shell));
+	
+	/* construct real query */
+	query = rhythmdb_query_parse (source->priv->db,
+				      RHYTHMDB_QUERY_PROP_EQUALS,
+				      	RHYTHMDB_PROP_TYPE,
+					source->priv->entry_type,
+				      RHYTHMDB_QUERY_PROP_EQUALS,
+				      	RHYTHMDB_PROP_HIDDEN,
+					TRUE,
+				      RHYTHMDB_QUERY_END);
+	source->priv->model = rhythmdb_query_model_new (source->priv->db, query,
+							NULL, 0, FALSE);
+	g_ptr_array_free (query, TRUE);
+
+	g_object_set (G_OBJECT (source->priv->model), "show-hidden", TRUE, NULL);
+	_rb_source_hide_when_empty (RB_SOURCE (source), source->priv->model);
+
+	/* set up entry view */
+	source->priv->view = rb_entry_view_new (source->priv->db, shell_player, 
+						NULL, FALSE, FALSE);
+
+	rb_entry_view_set_model (source->priv->view, source->priv->model);
+
+	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE);
+	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_TITLE, TRUE);
+/*	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_GENRE, FALSE); */
+	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
+	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
+	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_LOCATION, TRUE);
+	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_LAST_SEEN, TRUE);
+
+	rb_entry_view_set_columns_clickable (source->priv->view, TRUE);
+	
+	gtk_container_add (GTK_CONTAINER (source), GTK_WIDGET (source->priv->view));
+	g_signal_connect_object (G_OBJECT (source->priv->view), "show_popup",
+				 G_CALLBACK (rb_missing_files_source_songs_show_popup_cb), source, 0);
+	g_signal_connect_object (G_OBJECT (source->priv->view), "sort-order-changed",
+				 G_CALLBACK (rb_missing_files_source_songs_sort_order_changed_cb), source, 0);
+		
+	gtk_widget_show_all (GTK_WIDGET (source));
+
+	/* use a fake model for the source's query model property, so we can't try to play
+	 * any of the hidden entries.
+	 */
+	g_object_set (G_OBJECT (source), 
+		      "query-model", rhythmdb_query_model_new_empty (source->priv->db), 
+		      NULL);
+
+	return G_OBJECT (source);
+}
+
+static void
+rb_missing_files_source_dispose (GObject *object)
+{
+	RBMissingFilesSource *source = RB_MISSING_FILES_SOURCE (object);
+
+	if (source->priv->db) {
+		g_object_unref (G_OBJECT (source->priv->db));
+		source->priv->db = NULL;
+	}
+	if (source->priv->model) {
+		g_object_unref (G_OBJECT (source->priv->model));
+		source->priv->model = NULL;
+	}
+
+	G_OBJECT_CLASS (rb_missing_files_source_parent_class)->dispose (object);
+}
+
+
+static RBEntryView *
+impl_get_entry_view (RBSource *asource)
+{
+	RBMissingFilesSource *source = RB_MISSING_FILES_SOURCE (asource);
+	return source->priv->view;
+}
+
+static void 
+rb_missing_files_source_set_property (GObject *object,
+				      guint prop_id,
+				      const GValue *value,
+				      GParamSpec *pspec)
+{
+	RBMissingFilesSource *source = RB_MISSING_FILES_SOURCE (object);
+
+	switch (prop_id)
+	{
+	case PROP_ENTRY_TYPE:
+		source->priv->entry_type = g_value_get_uint (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void 
+rb_missing_files_source_get_property (GObject *object,
+				      guint prop_id,
+				      GValue *value,
+				      GParamSpec *pspec)
+{
+	RBMissingFilesSource *source = RB_MISSING_FILES_SOURCE (object);
+
+	switch (prop_id)
+	{
+	case PROP_ENTRY_TYPE:
+		g_value_set_uint (value, source->priv->entry_type);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+RBSource *
+rb_missing_files_source_new (RBShell *shell,
+			     RBLibrarySource *library)
+{
+	RBSource *source;
+	RhythmDBEntryType entry_type;
+
+	g_object_get (G_OBJECT (library), "entry-type", &entry_type, NULL);
+	source = RB_SOURCE (g_object_new (RB_TYPE_MISSING_FILES_SOURCE,
+					  "name", _("Missing Files"),
+					  "entry-type", entry_type,
+					  "shell", shell,
+					  "visibility", FALSE,
+					  NULL));
+	return source;
+}
+
+static void 
+rb_missing_files_source_songs_show_popup_cb (RBEntryView *view,
+					     gboolean over_entry,
+					     RBMissingFilesSource *source)
+{
+	if (over_entry)
+		_rb_source_show_popup (RB_SOURCE (source), MISSING_FILES_SOURCE_SONGS_POPUP_PATH);
+}
+
+static void 
+impl_song_properties (RBSource *asource)
+{
+	RBMissingFilesSource *source = RB_MISSING_FILES_SOURCE (asource);
+	GtkWidget *song_info = NULL;
+
+	g_return_if_fail (source->priv->view != NULL);
+
+	song_info = rb_song_info_new (asource, NULL);
+	if (song_info)
+		gtk_widget_show_all (song_info);
+	else
+		rb_debug ("failed to create dialog, or no selection!");
+}
+
+static void
+impl_delete (RBSource *asource)
+{
+	RBMissingFilesSource *source = RB_MISSING_FILES_SOURCE (asource);
+	GList *sel, *tem;
+
+	sel = rb_entry_view_get_selected_entries (source->priv->view);
+	for (tem = sel; tem != NULL; tem = tem->next) {
+		rhythmdb_entry_delete (source->priv->db, tem->data);
+		rhythmdb_commit (source->priv->db);
+	}
+	g_list_free (sel);
+}
+
+static void 
+rb_missing_files_source_songs_sort_order_changed_cb (RBEntryView *view, 
+						     RBMissingFilesSource *source)
+{
+	rb_entry_view_resort_model (view);
+}
diff -rN -u -p old-hidden/sources/rb-missing-files-source.h new-hidden/sources/rb-missing-files-source.h
--- old-hidden/sources/rb-missing-files-source.h	1970-01-01 10:00:00.000000000 +1000
+++ new-hidden/sources/rb-missing-files-source.h	2006-02-19 19:20:39.000000000 +1000
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (C) 2006  Jonathan Matthew  <jonathan@kaolin.wh9.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __RB_MISSING_FILES_SOURCE_H
+#define __RB_MISSING_FILES_SOURCE_H
+
+#include "rb-shell.h"
+#include "rb-source.h"
+#include "rb-library-source.h"
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_MISSING_FILES_SOURCE         (rb_missing_files_source_get_type ())
+#define RB_MISSING_FILES_SOURCE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_MISSING_FILES_SOURCE, RBMissingFilesSource))
+#define RB_MISSING_FILES_SOURCE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_MISSING_FILES_SOURCE, RBMissingFilesSourceClass))
+#define RB_IS_MISSING_FILES_SOURCE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_MISSING_FILES_SOURCE))
+#define RB_IS_MISSING_FILES_SOURCE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_MISSING_FILES_SOURCE))
+#define RB_MISSING_FILES_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_MISSING_FILES_SOURCE, RBMissingFilesSourceClass))
+
+typedef struct RBMissingFilesSourcePrivate RBMissingFilesSourcePrivate;
+
+typedef struct
+{
+	RBSource parent;
+
+	RBMissingFilesSourcePrivate *priv;
+} RBMissingFilesSource;
+
+typedef struct
+{
+	RBSourceClass parent;
+} RBMissingFilesSourceClass;
+
+GType		rb_missing_files_source_get_type		(void);
+
+RBSource *      rb_missing_files_source_new			(RBShell *shell,
+								 RBLibrarySource *library_source);
+
+G_END_DECLS
+
+#endif /* __RB_MISSING_FILES_SOURCE_H */
+
diff -rN -u -p old-hidden/sources/rb-playlist-source.c new-hidden/sources/rb-playlist-source.c
--- old-hidden/sources/rb-playlist-source.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/sources/rb-playlist-source.c	2006-02-19 19:20:39.000000000 +1000
@@ -255,17 +255,17 @@ rb_playlist_source_constructor (GType ty
 						    "PlaylistTrack", NULL, 0);
 	}
 
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TITLE);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_GENRE);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ARTIST);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ALBUM);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_YEAR);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_DURATION);
- 	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_QUALITY);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_RATING);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED);
-	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_FIRST_SEEN);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TITLE, TRUE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_GENRE, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_YEAR, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_DURATION, FALSE);
+ 	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_QUALITY, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_RATING, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
+	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_FIRST_SEEN, FALSE);
 	rb_entry_view_set_columns_clickable (source->priv->songs, FALSE);
 
 	rb_playlist_source_setup_entry_view (source, source->priv->songs);
diff -rN -u -p old-hidden/sources/rb-podcast-source.c new-hidden/sources/rb-podcast-source.c
--- old-hidden/sources/rb-podcast-source.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/sources/rb-podcast-source.c	2006-02-19 19:20:39.000000000 +1000
@@ -521,7 +521,7 @@ rb_podcast_source_constructor (GType typ
 					    (GCompareDataFunc) rb_podcast_source_post_date_cell_sort_func, 0);
 						    
 
-	rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_TITLE);
+	rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_TITLE, TRUE);
 	
 	/* COLUMN FEED */
 	column = gtk_tree_view_column_new ();
@@ -543,10 +543,10 @@ rb_podcast_source_constructor (GType typ
 					    (GCompareDataFunc) rb_podcast_source_post_feed_cell_sort_func, 0);
 
 	
-	rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_DURATION);
-	rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_RATING);
-	rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_PLAY_COUNT);
-	rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_LAST_PLAYED);
+	rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_DURATION, FALSE);
+	rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_RATING, FALSE);
+	rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);
+	rb_entry_view_append_column (source->priv->posts, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
 
 
 	/* Status column */
diff -rN -u -p old-hidden/sources/rb-source.c new-hidden/sources/rb-source.c
--- old-hidden/sources/rb-source.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/sources/rb-source.c	2006-02-19 19:20:39.000000000 +1000
@@ -967,3 +967,42 @@ _rb_source_register_action_group (RBSour
 
 	return group;
 }
+
+static void
+_autohide_update_visibility (RBSource *source, GtkTreeModel *model, int adjust)
+{
+	gint count = gtk_tree_model_iter_n_children (model, NULL) + adjust;
+	/* only update the property if it will have actually changed */
+	if (count < 2)
+		g_object_set (G_OBJECT (source), "visibility", (count > 0), NULL);
+}
+
+static void
+_rb_autohide_source_row_deleted_cb (GtkTreeModel *model,
+				    GtkTreePath *path,
+				    RBSource *source)
+{
+	_autohide_update_visibility (source, model, -1);
+}
+
+static void
+_rb_autohide_source_row_inserted_cb (GtkTreeModel *model,
+				     GtkTreePath *path,
+				     GtkTreeIter *iter,
+				     RBSource *source)
+{
+	_autohide_update_visibility (source, model, 0);
+}
+
+void
+_rb_source_hide_when_empty (RBSource *source,
+			    RhythmDBQueryModel *model)
+{
+	g_signal_connect_object (G_OBJECT (model), "row-deleted",
+				 G_CALLBACK (_rb_autohide_source_row_deleted_cb),
+				 source, 0);
+	g_signal_connect_object (G_OBJECT (model), "row-inserted",
+				 G_CALLBACK (_rb_autohide_source_row_inserted_cb),
+				 source, 0);
+}
+
diff -rN -u -p old-hidden/sources/rb-source.h new-hidden/sources/rb-source.h
--- old-hidden/sources/rb-source.h	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/sources/rb-source.h	2006-02-19 19:20:39.000000000 +1000
@@ -192,6 +192,8 @@ GtkActionGroup *_rb_source_register_acti
 						  GtkActionEntry *actions,
 						  int num_actions,
 						  gpointer user_data);
+void		_rb_source_hide_when_empty	(RBSource *source,
+						 RhythmDBQueryModel *model);
 
 G_END_DECLS
 
diff -rN -u -p old-hidden/sources/rb-sourcelist.c new-hidden/sources/rb-sourcelist.c
--- old-hidden/sources/rb-sourcelist.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/sources/rb-sourcelist.c	2006-02-19 19:20:39.000000000 +1000
@@ -345,21 +345,24 @@ rb_sourcelist_append (RBSourceList *sour
 	PangoAttrList *attrs = pango_attr_list_new ();
 	const char *name;
 	GdkPixbuf *pixbuf;
+	gboolean visible;
 
 	g_return_if_fail (RB_IS_SOURCELIST (sourcelist));
 	g_return_if_fail (RB_IS_SOURCE (source));
 
 	g_object_get (G_OBJECT (source), "name", &name, NULL);
 	g_object_get (G_OBJECT (source), "icon", &pixbuf, NULL);
+	g_object_get (G_OBJECT (source), "visibility", &visible, NULL);
 
 	if (parent) {
 		GtkTreeIter parent_iter;
 		g_assert (rb_sourcelist_source_to_iter (sourcelist, parent, &parent_iter));
 		gtk_tree_store_append (GTK_TREE_STORE (sourcelist->priv->real_model), &iter, &parent_iter);
 
-		/* !!! make the expander column visible */
-		sourcelist->priv->child_source_count++;
-		rb_sourcelist_update_expander_visibility (sourcelist);
+		if (visible) {
+			sourcelist->priv->child_source_count++;
+			rb_sourcelist_update_expander_visibility (sourcelist);
+		}
 	} else {
 		gtk_tree_store_append (GTK_TREE_STORE (sourcelist->priv->real_model), &iter, NULL);
 	}
@@ -369,6 +372,7 @@ rb_sourcelist_append (RBSourceList *sour
 			    RB_SOURCELIST_MODEL_COLUMN_NAME, name,
 			    RB_SOURCELIST_MODEL_COLUMN_SOURCE, source,
 			    RB_SOURCELIST_MODEL_COLUMN_ATTRIBUTES, attrs,
+			    RB_SOURCELIST_MODEL_COLUMN_VISIBILITY, visible,
 			    -1);
 
 	if (pixbuf != NULL) {
@@ -395,7 +399,7 @@ match_source_to_iter (GtkTreeModel *mode
 	
 	gtk_tree_model_get (model, iter, RB_SOURCELIST_MODEL_COLUMN_SOURCE, &target, -1);
 	if (target == sp->source) {
-		sp->path = gtk_tree_path_copy(path);
+		sp->path = gtk_tree_path_copy (path);
 		return TRUE;
 	}
 
@@ -406,7 +410,7 @@ static gboolean
 rb_sourcelist_source_to_iter (RBSourceList *sourcelist, RBSource *source,
 			      GtkTreeIter *iter)
 {
-	SourcePath *sp = g_new0(SourcePath,1);
+	SourcePath *sp = g_new0 (SourcePath,1);
 	gboolean ret = FALSE;
 	
 	sp->source = source;
@@ -418,8 +422,8 @@ rb_sourcelist_source_to_iter (RBSourceLi
 		ret = TRUE;
 	}
 
-	gtk_tree_path_free(sp->path);
-	g_free(sp);
+	gtk_tree_path_free (sp->path);
+	g_free (sp);
 	sp = NULL;
 
 	return ret;
@@ -429,7 +433,7 @@ static gboolean
 rb_sourcelist_visible_source_to_iter (RBSourceList *sourcelist, RBSource *source,
 				      GtkTreeIter *iter)
 {
-	SourcePath *sp = g_new0(SourcePath,1);
+	SourcePath *sp = g_new0 (SourcePath,1);
 	gboolean ret = FALSE;
 	
 	sp->source = source;
@@ -441,8 +445,8 @@ rb_sourcelist_visible_source_to_iter (RB
 		ret = TRUE;
 	}
 
-	gtk_tree_path_free(sp->path);
-	g_free(sp);
+	gtk_tree_path_free (sp->path);
+	g_free (sp);
 	sp = NULL;
 
 	return ret;
@@ -519,7 +523,11 @@ rb_sourcelist_remove (RBSourceList *sour
 	g_assert (rb_sourcelist_source_to_iter (sourcelist, source, &iter));
 
 	if (gtk_tree_store_iter_depth (GTK_TREE_STORE (sourcelist->priv->real_model), &iter) > 0) {
-		sourcelist->priv->child_source_count--;
+		gboolean visible;
+
+		g_object_get (G_OBJECT (source), "visibility", &visible, NULL);
+		if (visible)
+			sourcelist->priv->child_source_count--;
 		rb_sourcelist_update_expander_visibility (sourcelist);
 	}
 
@@ -667,16 +675,27 @@ visibility_notify_cb (GObject *obj, GPar
 	RBSourceList *sourcelist = RB_SOURCELIST (data);
 	RBSource *source = RB_SOURCE (obj);
 	GtkTreeIter iter;
+	gboolean old_visibility;
+	gboolean new_visibility;
 
 	gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sourcelist->priv->filter_model));
 
 	g_assert (rb_sourcelist_source_to_iter (sourcelist, source, &iter));
 	if (gtk_tree_store_iter_depth (GTK_TREE_STORE (sourcelist->priv->real_model), &iter) > 0) {
-		gboolean visible;
 
-		g_object_get (obj, "visibility", &visible, NULL);
-		sourcelist->priv->child_source_count += visible ? 1 : -1;
-		rb_sourcelist_update_expander_visibility (sourcelist);
+		g_object_get (obj, "visibility", &new_visibility, NULL);
+		gtk_tree_model_get (GTK_TREE_MODEL (sourcelist->priv->real_model), &iter,
+				    RB_SOURCELIST_MODEL_COLUMN_VISIBILITY, &old_visibility,
+				    -1);
+
+		if (old_visibility != new_visibility) {
+			sourcelist->priv->child_source_count += new_visibility ? 1 : -1;
+			rb_sourcelist_update_expander_visibility (sourcelist);
+
+			gtk_tree_store_set (GTK_TREE_STORE (sourcelist->priv->real_model), &iter,
+					    RB_SOURCELIST_MODEL_COLUMN_VISIBILITY, new_visibility,
+					    -1);
+		}
 	}
 
 	gtk_tree_view_columns_autosize (GTK_TREE_VIEW (sourcelist->priv->treeview));
diff -rN -u -p old-hidden/sources/rb-sourcelist-model.c new-hidden/sources/rb-sourcelist-model.c
--- old-hidden/sources/rb-sourcelist-model.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/sources/rb-sourcelist-model.c	2006-02-19 19:20:39.000000000 +1000
@@ -216,6 +216,7 @@ rb_sourcelist_model_new (void)
 	column_types[RB_SOURCELIST_MODEL_COLUMN_NAME] = G_TYPE_STRING;
 	column_types[RB_SOURCELIST_MODEL_COLUMN_SOURCE] = G_TYPE_POINTER;
 	column_types[RB_SOURCELIST_MODEL_COLUMN_ATTRIBUTES] = PANGO_TYPE_ATTR_LIST;
+	column_types[RB_SOURCELIST_MODEL_COLUMN_VISIBILITY] = G_TYPE_BOOLEAN;
 	store = gtk_tree_store_newv (RB_SOURCELIST_MODEL_N_COLUMNS, 
 				     column_types);
 
diff -rN -u -p old-hidden/sources/rb-sourcelist-model.h new-hidden/sources/rb-sourcelist-model.h
--- old-hidden/sources/rb-sourcelist-model.h	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/sources/rb-sourcelist-model.h	2006-02-19 19:20:39.000000000 +1000
@@ -60,6 +60,7 @@ enum
 	RB_SOURCELIST_MODEL_COLUMN_NAME,
 	RB_SOURCELIST_MODEL_COLUMN_SOURCE,
 	RB_SOURCELIST_MODEL_COLUMN_ATTRIBUTES,
+	RB_SOURCELIST_MODEL_COLUMN_VISIBILITY,
 	RB_SOURCELIST_MODEL_N_COLUMNS
 };
 
diff -rN -u -p old-hidden/widgets/Makefile.am new-hidden/widgets/Makefile.am
--- old-hidden/widgets/Makefile.am	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/widgets/Makefile.am	2006-02-19 19:20:39.000000000 +1000
@@ -33,8 +33,6 @@ librbwidgets_la_SOURCES =				\
 	rb-cell-renderer-rating.h			\
 	rb-song-info.c					\
 	rb-song-info.h					\
-	rb-load-failure-dialog.c			\
-	rb-load-failure-dialog.h			\
 	rb-rating.c					\
 	rb-rating.h					\
 	rb-rating-helper.c				\
diff -rN -u -p old-hidden/widgets/rb-entry-view.c new-hidden/widgets/rb-entry-view.c
--- old-hidden/widgets/rb-entry-view.c	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/widgets/rb-entry-view.c	2006-02-19 19:20:39.000000000 +1000
@@ -187,6 +187,8 @@ G_DEFINE_TYPE (RBEntryView, rb_entry_vie
 
 static guint rb_entry_view_signals[LAST_SIGNAL] = { 0 };
 
+static GQuark rb_entry_view_column_always_visible;
+
 static gboolean
 type_ahead_search_func (GtkTreeModel *model, gint column,
 			const gchar *key, GtkTreeIter *iter,
@@ -350,6 +352,8 @@ rb_entry_view_class_init (RBEntryViewCla
 			      0);
 
 	g_type_class_add_private (klass, sizeof (RBEntryViewPrivate));
+
+	rb_entry_view_column_always_visible = g_quark_from_static_string ("rb_entry_view_column_always_visible");
 }
 
 static void
@@ -876,7 +880,7 @@ rb_entry_view_column_clicked_cb (GtkTree
 }
 
 void
-rb_entry_view_append_column (RBEntryView *view, RBEntryViewColumn coltype)
+rb_entry_view_append_column (RBEntryView *view, RBEntryViewColumn coltype, gboolean always_visible)
 {
 	GtkTreeViewColumn *column;
 	GtkCellRenderer *renderer = NULL;
@@ -1043,6 +1047,35 @@ rb_entry_view_append_column (RBEntryView
 		strings[0] = title;
 		strings[1] = rb_entry_view_get_time_date_column_sample ();
 		break;
+	case RB_ENTRY_VIEW_COL_LAST_SEEN:
+		propid = RHYTHMDB_PROP_LAST_SEEN;
+		cell_data->propid = RHYTHMDB_PROP_LAST_SEEN_STR;
+		cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
+		sort_propid = RHYTHMDB_PROP_LAST_SEEN;
+		sort_func = (GCompareDataFunc) rhythmdb_query_model_ulong_sort_func;
+		title = _("Last _Seen");
+		key = "LastSeen";
+		strings[0] = title;
+		strings[1] = rb_entry_view_get_time_date_column_sample ();
+		break;
+	case RB_ENTRY_VIEW_COL_LOCATION:
+		propid = RHYTHMDB_PROP_LOCATION;
+		cell_data->propid = RHYTHMDB_PROP_LOCATION;
+		cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
+		sort_propid = RHYTHMDB_PROP_LOCATION;
+		sort_func = (GCompareDataFunc) rhythmdb_query_model_location_sort_func;
+		title = _("L_ocation");
+		key = "Location";
+		ellipsize = TRUE;
+		break;
+	case RB_ENTRY_VIEW_COL_ERROR:
+		propid = RHYTHMDB_PROP_PLAYBACK_ERROR;
+		cell_data->propid = RHYTHMDB_PROP_PLAYBACK_ERROR;
+		cell_data_func = (GtkTreeCellDataFunc) rb_entry_view_string_cell_data_func;
+		title = _("Error");
+		key = "Error";
+		ellipsize = TRUE;
+		break;
 	default:
 		g_assert_not_reached ();
 		propid = -1;
@@ -1079,7 +1112,13 @@ rb_entry_view_append_column (RBEntryView
 	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
 	gtk_tree_view_column_set_clickable (column, TRUE);
 
+	if (always_visible)
+		g_object_set_qdata (G_OBJECT (column), 
+				    rb_entry_view_column_always_visible, 
+				    GINT_TO_POINTER (1));
+
 	g_hash_table_insert (view->priv->propid_column_map, GINT_TO_POINTER (propid), column);
+
 	rb_entry_view_append_column_custom (view, column, title, key, sort_func, sort_propid);
 }
 
@@ -1699,11 +1738,12 @@ set_column_visibility (guint propid, Gtk
 {
 	gboolean visible;
 
-	/* title is always visible */
-	if (propid == RHYTHMDB_PROP_TITLE)
-		return;
+	if (g_object_get_qdata (G_OBJECT (column), 
+				rb_entry_view_column_always_visible) == GINT_TO_POINTER (1))
+		visible = TRUE;
+	else
+		visible = (g_list_find (visible_props, GINT_TO_POINTER (propid)) != NULL);
 
-	visible = (g_list_find (visible_props, GINT_TO_POINTER (propid)) != NULL);
 	gtk_tree_view_column_set_visible (column, visible);
 }
 
diff -rN -u -p old-hidden/widgets/rb-entry-view.h new-hidden/widgets/rb-entry-view.h
--- old-hidden/widgets/rb-entry-view.h	2006-02-19 19:20:38.000000000 +1000
+++ new-hidden/widgets/rb-entry-view.h	2006-02-19 19:20:39.000000000 +1000
@@ -52,7 +52,10 @@ typedef enum {
 	RB_ENTRY_VIEW_COL_PLAY_COUNT,
 	RB_ENTRY_VIEW_COL_YEAR,
 	RB_ENTRY_VIEW_COL_LAST_PLAYED,
-	RB_ENTRY_VIEW_COL_FIRST_SEEN
+	RB_ENTRY_VIEW_COL_FIRST_SEEN,
+	RB_ENTRY_VIEW_COL_LAST_SEEN,
+	RB_ENTRY_VIEW_COL_LOCATION,
+	RB_ENTRY_VIEW_COL_ERROR
 } RBEntryViewColumn;
 
 typedef enum {
@@ -93,7 +96,8 @@ RBEntryView *	rb_entry_view_new			(Rhyth
 							 const char *sort_key, gboolean drag_source, 
 							 gboolean drag_dest);
 
-void		rb_entry_view_append_column		(RBEntryView *view, RBEntryViewColumn coltype);
+void		rb_entry_view_append_column		(RBEntryView *view, 
+							 RBEntryViewColumn coltype, gboolean always_visible);
 
 void		rb_entry_view_append_column_custom	(RBEntryView *view, GtkTreeViewColumn *column,
 							 const char *title, const char *key,

