commit 27b95cb8252dc7d14c0d212164c14bdae98e727e
Author: Christophe Fergeau <teuf@gnome.org>
Date:   Sun Oct 26 21:48:12 2008 +0100

    Apply patch from bug #358029

diff --git a/plugins/ipod/Makefile.am b/plugins/ipod/Makefile.am
index e8361f4..57252a0 100644
--- a/plugins/ipod/Makefile.am
+++ b/plugins/ipod/Makefile.am
@@ -47,7 +47,7 @@ uixml_DATA = ipod-ui.xml
 plugin_DATA = $(plugin_in_files:.rb-plugin.in=.rb-plugin)
 
 gladedir = $(plugindir)
-glade_DATA = ipod-info.glade
+glade_DATA = ipod-info.glade ipod-init.glade
 
 EXTRA_DIST = $(uixml_DATA) $(plugin_in_files) $(glade_DATA)
 
diff --git a/plugins/ipod/rb-ipod-plugin.c b/plugins/ipod/rb-ipod-plugin.c
index c330a84..f8b1a09 100644
--- a/plugins/ipod/rb-ipod-plugin.c
+++ b/plugins/ipod/rb-ipod-plugin.c
@@ -230,9 +230,11 @@ rb_ipod_plugin_source_deleted (RBiPodSource *source, RBIpodPlugin *plugin)
 static RBSource *
 create_source_cb (RBRemovableMediaManager *rmm, GMount *mount, RBIpodPlugin *plugin)
 {
-	if (rb_ipod_is_mount_ipod (mount)) {
+	if (rb_ipod_is_mount_ipod (mount, RB_PLUGIN (plugin))) {
 		RBSource *src;
-		src = RB_SOURCE (rb_ipod_source_new (plugin->shell, mount));
+		src = RB_SOURCE (rb_ipod_source_new (plugin->shell,
+                                                     RB_PLUGIN (plugin),
+                                                     mount));
 
 		plugin->ipod_sources = g_list_prepend (plugin->ipod_sources, src);
 		g_signal_connect_object (G_OBJECT (src),
diff --git a/plugins/ipod/rb-ipod-source.c b/plugins/ipod/rb-ipod-source.c
index 306044b..a346dd4 100644
--- a/plugins/ipod/rb-ipod-source.c
+++ b/plugins/ipod/rb-ipod-source.c
@@ -31,6 +31,7 @@
 
 #include <errno.h>
 #include <string.h>
+#include <sys/statvfs.h>
 
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
@@ -52,6 +53,8 @@
 #include "rb-ipod-static-playlist-source.h"
 #include "rb-util.h"
 #include "rhythmdb.h"
+#include "rb-dialog.h"
+#include "rb-glade-helpers.h"
 #include "rb-cut-and-paste-code.h"
 
 #define CONF_STATE_PANED_POSITION CONF_PREFIX "/state/ipod/paned_position"
@@ -272,6 +275,7 @@ rb_ipod_source_dispose (GObject *object)
 
 RBRemovableMediaSource *
 rb_ipod_source_new (RBShell *shell,
+                    RBPlugin *plugin,
 		    GMount *mount)
 {
 	RBiPodSource *source;
@@ -281,8 +285,6 @@ rb_ipod_source_new (RBShell *shell,
 	char *name;
 	char *path;
 
-	g_assert (rb_ipod_is_mount_ipod (mount));
-
 	volume = g_mount_get_volume (mount);
 	path = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
 	g_object_unref (volume);
@@ -969,7 +971,7 @@ rb_ipod_get_itunesdb_path (GMount *mount)
 	if (root != NULL) {
 		mount_point = g_file_get_path (root);
 		if (mount_point != NULL) {
-			result = itdb_get_itunes_dir (mount_point);
+			result = itdb_get_itunesdb_path (mount_point);
 		}
 
 		g_free (mount_point);
@@ -979,6 +981,322 @@ rb_ipod_get_itunesdb_path (GMount *mount)
 	return result;
 }
 
+static guint64 get_fs_property (const char *mountpoint, const char *attr)
+{
+        GFile *root;
+        GFileInfo *info;
+        guint64 value;
+
+        root = g_file_new_for_path (mountpoint);
+        info = g_file_query_filesystem_info (root, attr, NULL, NULL);
+        g_object_unref (G_OBJECT (root));
+        if (info == NULL) {
+                return 0;
+        }
+        if (!g_file_info_has_attribute (info, attr)) {
+                g_object_unref (G_OBJECT (info));
+                return 0;
+        }
+        value = g_file_info_get_attribute_uint64 (info, attr);
+        g_object_unref (G_OBJECT (info));
+
+        return value;
+}
+
+static guint64
+get_ipod_capacity (const char *mountpoint)
+{
+        return  get_fs_property (mountpoint, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
+}
+
+
+static double 
+get_rounded_ipod_capacity (const char *mountpoint)
+{
+	guint64 capacity;
+
+	capacity = get_ipod_capacity (mountpoint);
+	capacity += (1000*1000*500 - 1);
+	capacity -= (capacity % (1000*1000*500));
+	return (double)capacity/(1000.0*1000*1000);
+}
+
+static guint64
+get_ipod_free_space (const char *mountpoint)
+{
+	return get_fs_property (mountpoint, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
+}
+
+static char *
+get_ipod_device (RBiPodSource *source)
+{
+        GMount *mount;
+        GVolume *volume;
+        char *device;
+
+        g_object_get (RB_SOURCE (source), "mount", &mount, NULL);
+        volume = g_mount_get_volume (mount);
+        device = g_volume_get_identifier (volume,
+                                          G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+        g_object_unref (G_OBJECT (volume));
+        g_object_unref (G_OBJECT (mount));
+
+        return device;
+}
+
+enum {
+      COL_INFO = 0,
+};
+
+static gchar *ipod_info_to_string (const Itdb_IpodInfo *info)
+{
+	if (info->capacity >= 1) {   /* size in GB */
+		return g_strdup_printf ("%2.0f GB %s", info->capacity,
+					itdb_info_get_ipod_model_name_string (info->ipod_model));
+	} else if (info->capacity > 0) {   /* size in MB */
+		return g_strdup_printf ("%3.0f MB %s", info->capacity * 1024,
+					itdb_info_get_ipod_model_name_string (info->ipod_model));
+	} else {   /* no capacity information available */
+		return g_strdup_printf ("%s",
+					itdb_info_get_ipod_model_name_string (info->ipod_model));
+	}
+}
+
+static void
+set_cell (GtkCellLayout   *cell_layout,
+	  GtkCellRenderer *cell,
+	  GtkTreeModel    *tree_model,
+	  GtkTreeIter     *iter,
+	  gpointer         data)
+{
+	gboolean header;
+	gchar *text;
+	Itdb_IpodInfo *info;
+
+	gtk_tree_model_get (tree_model, iter, COL_INFO, &info, -1);
+	g_return_if_fail (info);
+
+	header = gtk_tree_model_iter_has_child (tree_model, iter);
+
+	if (header) {
+		text = g_strdup (
+				 itdb_info_get_ipod_generation_string (info->ipod_generation));
+	} else {
+		text = ipod_info_to_string (info);
+	}
+
+	g_object_set (cell,
+		      "sensitive", !header,
+		      "text", text,
+		      NULL);
+	g_free (text);
+}
+
+static gboolean
+model_equals (gconstpointer a, gconstpointer b)
+{
+	const Itdb_IpodInfo *lhs = (const Itdb_IpodInfo *)a;
+	const Itdb_IpodInfo *rhs = (const Itdb_IpodInfo *)b;
+
+	return !((lhs->capacity == rhs->capacity) 
+		 && (lhs->ipod_model == rhs->ipod_model) 
+		 && (lhs->ipod_generation == rhs->ipod_generation));
+}
+
+static GHashTable *
+build_model_table (const char *mount_path)
+{
+	const Itdb_IpodInfo *table;
+	const Itdb_IpodInfo *model_info;
+	GHashTable *models;
+	gdouble ipod_capacity;
+
+	ipod_capacity = get_rounded_ipod_capacity (mount_path);
+	models = g_hash_table_new_full (g_int_hash, g_int_equal,
+					NULL, (GDestroyNotify)g_list_free);
+	table = itdb_info_get_ipod_info_table ();
+	for (model_info = table;
+	     model_info->model_number != NULL;
+	     model_info++) {
+		GList *infos;
+
+		infos = g_hash_table_lookup (models,
+					     &model_info->ipod_generation);
+		if (g_list_find_custom (infos, model_info, model_equals)) {
+			continue;
+		}
+		if (model_info->capacity == ipod_capacity) {
+			/* Steal the key from the hash table since we don't 
+			 * want 'infos' to be g_list_freed by the hash
+			 * table destroy notify function
+			 */
+			g_hash_table_steal (models,
+					    &model_info->ipod_generation);
+			infos = g_list_prepend (infos, (gpointer)model_info);
+
+			g_hash_table_insert (models, 
+					     (gpointer)&model_info->ipod_generation, 
+					     infos);
+		}
+	}
+
+	return models;
+}
+
+struct FillModelContext {
+	GtkWidget *combo;
+	GtkTreeStore *store;
+	const Itdb_IpodInfo *ipod_info;
+};
+
+static void 
+fill_one_generation (gpointer key, gpointer value, gpointer data)
+{
+	GList *infos;
+	GList *it;
+	Itdb_IpodGeneration generation;
+	gboolean first;
+	GtkTreeIter iter;
+	struct FillModelContext *ctx;
+
+	ctx = (struct FillModelContext *)data;
+	infos = (GList *)value;
+	generation = *(Itdb_IpodGeneration*)key;
+
+	first = TRUE;
+	for (it = infos; it != NULL; it = it->next) {
+		const Itdb_IpodInfo *info;
+		GtkTreeIter iter_child;
+
+		info = (const Itdb_IpodInfo *)it->data;
+		g_assert (info->ipod_generation == generation);
+
+		if (first) {
+			gtk_tree_store_append (ctx->store, &iter, NULL);
+			gtk_tree_store_set (ctx->store, &iter, 
+					    COL_INFO, info, -1);
+			first = FALSE;
+		}
+		gtk_tree_store_append (ctx->store, &iter_child, &iter);
+		gtk_tree_store_set (ctx->store, &iter_child, 
+				    COL_INFO, info, -1);
+		if (info == ctx->ipod_info) {
+			gtk_combo_box_set_active_iter (GTK_COMBO_BOX (ctx->combo), 
+						       &iter_child);
+		}
+	}
+}
+
+static void
+fill_model_combo (GtkWidget *combo, const char *mount_path)
+{
+	GHashTable *models;
+	Itdb_Device *device;
+	GtkTreeStore *store;
+	const Itdb_IpodInfo *ipod_info;
+	GtkCellRenderer *renderer;
+	struct FillModelContext ctx;
+
+	device = itdb_device_new ();
+	itdb_device_set_mountpoint (device, mount_path);
+	itdb_device_read_sysinfo (device);
+	ipod_info = itdb_device_get_ipod_info (device);
+	itdb_device_free (device);
+
+	store = gtk_tree_store_new (1, G_TYPE_POINTER);
+	gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (store));
+
+	ctx.combo = combo;
+	ctx.store = store;
+	ctx.ipod_info = ipod_info;
+	models = build_model_table (mount_path);
+	g_hash_table_foreach (models, fill_one_generation, &ctx);
+	g_hash_table_destroy (models);
+	g_object_unref (store);
+
+	gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
+	gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo),
+					    renderer,
+					    set_cell,
+					    NULL, NULL);
+}
+
+static gboolean
+show_ipod_first_time_dialog (GMount *mount, RBPlugin *plugin)
+{
+	/* could be an uninitialised iPod, ask the user */
+	GladeXML *xml;
+	GtkWidget *dialog;
+	GtkWidget *widget;
+	GtkTreeModel *tree_model;
+	GtkTreeIter iter;
+	int response;
+	char *glade_file;
+	char *mountpoint;
+	const Itdb_IpodInfo *info;
+	char *ipod_name;
+	GFile *root;
+	GError *error = NULL;
+
+	root = g_mount_get_root (mount);
+	if (root == NULL) {
+		return FALSE;      
+	}
+	mountpoint = g_file_get_path (root);
+	g_object_unref (G_OBJECT (root));
+
+	if (mountpoint == NULL) {
+		return FALSE;
+	}
+
+	/* create message dialog with model-number combo box
+	 * and asking whether they want to initialise the iPod
+	 */
+	glade_file = rb_plugin_find_file (plugin, "ipod_init.glade");
+	xml = glade_xml_new (glade_file, NULL, NULL);
+	g_free (glade_file);
+	dialog = glade_xml_get_widget (xml, "ipod_init");
+	widget = glade_xml_get_widget (xml, "model_combo");
+	fill_model_combo (widget, mountpoint);
+
+	rb_debug ("showing init dialog for ipod mount on '%s'", mountpoint);
+
+	response = gtk_dialog_run (GTK_DIALOG (dialog));
+	if (response != GTK_RESPONSE_ACCEPT) {
+		gtk_widget_destroy (dialog);
+		g_free (mountpoint);
+		return FALSE;
+	}
+
+	/* get model number and name */
+	tree_model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+	gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
+	gtk_tree_model_get (tree_model, &iter, COL_INFO, &info, -1);
+	widget = glade_xml_get_widget (xml, "name_entry");
+	ipod_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
+
+	gtk_widget_destroy (dialog);
+
+	rb_debug ("attempting to init ipod on '%s', with model '%s' and name '%s'",
+		  mountpoint, info->model_number, ipod_name);
+	if (!itdb_init_ipod (mountpoint, info->model_number, ipod_name, &error)) {
+		rb_error_dialog (NULL, _("Unable to initialise new iPod"), "%s", error->message);
+
+		g_free (mountpoint);
+		g_free (ipod_name);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	g_free (mountpoint);
+	g_free (ipod_name);
+
+	return TRUE;
+}
+
 static gboolean
 rb_ipod_mount_has_ipod_db (GMount *mount)
 {
@@ -998,10 +1316,11 @@ rb_ipod_mount_has_ipod_db (GMount *mount)
 }
 
 gboolean
-rb_ipod_is_mount_ipod (GMount *mount)
+rb_ipod_is_mount_ipod (GMount *mount, RBPlugin *plugin)
 {
 #ifdef HAVE_HAL
 	gchar *udi;
+        gboolean result;
 	GVolume *volume;
 
 	volume = g_mount_get_volume (mount);
@@ -1011,18 +1330,24 @@ rb_ipod_is_mount_ipod (GMount *mount)
 	udi = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_HAL_UDI);
 	g_object_unref (volume);
 
-	if (udi != NULL) {
-		gboolean result;
+        if (udi == NULL) {
+                return FALSE;
+        }
 
-		result = hal_udi_is_ipod (udi);
-		g_free (udi);
-		if (result == FALSE) {
-			return FALSE;
-		}
-	}
+        result = hal_udi_is_ipod (udi);
+        g_free (udi);
+        if (result == FALSE) {
+            return FALSE;
+        }
 #endif
 
-	return rb_ipod_mount_has_ipod_db (mount);
+        if (!rb_ipod_mount_has_ipod_db (mount)) {
+                if (!show_ipod_first_time_dialog (mount, plugin)) {
+                        return FALSE;
+                }
+        }
+
+        return TRUE;
 }
 
 #ifdef HAVE_HAL
@@ -1786,57 +2111,6 @@ ipod_name_changed_cb (GtkWidget     *widget,
 	return FALSE;
 }
 
-static guint64 get_fs_property (const char *mountpoint, const char *attr)
-{
-        GFile *root;
-        GFileInfo *info;
-        guint64 value;
-
-        root = g_file_new_for_path (mountpoint);
-        info = g_file_query_filesystem_info (root, attr, NULL, NULL);
-        g_object_unref (G_OBJECT (root));
-        if (info == NULL) {
-                return 0;
-        }
-        if (!g_file_info_has_attribute (info, attr)) {
-                g_object_unref (G_OBJECT (info));
-                return 0;
-        }
-        value = g_file_info_get_attribute_uint64 (info, attr);
-        g_object_unref (G_OBJECT (info));
-
-        return value;
-}
-
-static guint64
-get_ipod_capacity (const char *mountpoint)
-{
-        return  get_fs_property (mountpoint, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
-}
-
-static guint64
-get_ipod_free_space (const char *mountpoint)
-{
-	return get_fs_property (mountpoint, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
-}
-
-static char *
-get_ipod_device (RBiPodSource *source)
-{
-        GMount *mount;
-        GVolume *volume;
-        char *device;
-
-        g_object_get (RB_SOURCE (source), "mount", &mount, NULL);
-        volume = g_mount_get_volume (mount);
-        device = g_volume_get_identifier (volume,
-                                          G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
-        g_object_unref (G_OBJECT (volume));
-        g_object_unref (G_OBJECT (mount));
-
-        return device;
-}
-
 void
 rb_ipod_source_show_properties (RBiPodSource *source)
 {
diff --git a/plugins/ipod/rb-ipod-source.h b/plugins/ipod/rb-ipod-source.h
index 505a4e6..58a037d 100644
--- a/plugins/ipod/rb-ipod-source.h
+++ b/plugins/ipod/rb-ipod-source.h
@@ -32,6 +32,7 @@
 
 #include "rb-shell.h"
 #include "rb-removable-media-source.h"
+#include "rb-plugin.h"
 #include "rhythmdb.h"
 
 G_BEGIN_DECLS
@@ -53,11 +54,14 @@ typedef struct
 	RBRemovableMediaSourceClass parent;
 } RBiPodSourceClass;
 
-RBRemovableMediaSource *	rb_ipod_source_new		(RBShell *shell, GMount *mount);
+RBRemovableMediaSource *rb_ipod_source_new		(RBShell *shell,
+                                                         RBPlugin *plugin,
+                                                         GMount *mount);
 GType			rb_ipod_source_get_type		(void);
 GType                   rb_ipod_source_register_type    (GTypeModule *module);
 
-gboolean		rb_ipod_is_mount_ipod		(GMount *mount);
+gboolean		rb_ipod_is_mount_ipod		(GMount *mount,
+                                                         RBPlugin*plugin);
 
 void			rb_ipod_source_new_playlist	(RBiPodSource *source);
 void			rb_ipod_source_remove_playlist	(RBiPodSource *ipod_source,
