No issues found
1 /*
2 * rb-ipod-helpers.c
3 *
4 * Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
5 * Copyright (C) 2006 James "Doc" Livingston
6 * Copyright (C) 2008 Christophe Fergeau <teuf@gnome.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * The Rhythmbox authors hereby grant permission for non-GPL compatible
14 * GStreamer plugins to be used and distributed together with GStreamer
15 * and Rhythmbox. This permission is above and beyond the permissions granted
16 * by the GPL license by which Rhythmbox is covered. If you modify this code
17 * you may extend this exception to your version of the code, but you are not
18 * obligated to do so. If you do not wish to do so, delete this exception
19 * statement from your version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include <string.h>
36
37 #include <glib/gi18n.h>
38 #include <gtk/gtk.h>
39 #include <gpod/itdb.h>
40
41 #include "rb-ipod-helpers.h"
42 #include "rb-util.h"
43 #include "rb-builder-helpers.h"
44
45 #include "rb-debug.h"
46 #include "rb-dialog.h"
47
48
49 enum {
50 COL_INFO = 0,
51 };
52
53 static gchar *ipod_info_to_string (const Itdb_IpodInfo *info)
54 {
55 if (info->capacity >= 1) { /* size in GB */
56 return g_strdup_printf ("%2.0f GB %s", info->capacity,
57 itdb_info_get_ipod_model_name_string (info->ipod_model));
58 } else if (info->capacity > 0) { /* size in MB */
59 return g_strdup_printf ("%3.0f MB %s", info->capacity * 1024,
60 itdb_info_get_ipod_model_name_string (info->ipod_model));
61 } else { /* no capacity information available */
62 return g_strdup_printf ("%s",
63 itdb_info_get_ipod_model_name_string (info->ipod_model));
64 }
65 }
66
67 static double
68 get_rounded_ipod_capacity (const char *mountpoint)
69 {
70 guint64 capacity;
71
72 capacity = rb_ipod_helpers_get_capacity (mountpoint);
73 capacity += (1000*1000*500 - 1);
74 capacity -= (capacity % (1000*1000*500));
75 return (double)capacity/(1000.0*1000*1000);
76 }
77
78 static void
79 set_cell (GtkCellLayout *cell_layout,
80 GtkCellRenderer *cell,
81 GtkTreeModel *tree_model,
82 GtkTreeIter *iter,
83 gpointer data)
84 {
85 gboolean header;
86 gchar *text;
87 Itdb_IpodInfo *info;
88
89 gtk_tree_model_get (tree_model, iter, COL_INFO, &info, -1);
90 g_return_if_fail (info);
91
92 header = gtk_tree_model_iter_has_child (tree_model, iter);
93
94 if (header) {
95 text = g_strdup (
96 itdb_info_get_ipod_generation_string (info->ipod_generation));
97 } else {
98 text = ipod_info_to_string (info);
99 }
100
101 g_object_set (cell,
102 "sensitive", !header,
103 "text", text,
104 NULL);
105 g_free (text);
106 }
107
108 static gboolean
109 model_equals (gconstpointer a, gconstpointer b)
110 {
111 const Itdb_IpodInfo *lhs = (const Itdb_IpodInfo *)a;
112 const Itdb_IpodInfo *rhs = (const Itdb_IpodInfo *)b;
113
114 return !((lhs->capacity == rhs->capacity)
115 && (lhs->ipod_model == rhs->ipod_model)
116 && (lhs->ipod_generation == rhs->ipod_generation));
117 }
118
119 static GHashTable *
120 build_model_table (const char *mount_path)
121 {
122 const Itdb_IpodInfo *table;
123 const Itdb_IpodInfo *model_info;
124 GHashTable *models;
125 gdouble ipod_capacity;
126
127 ipod_capacity = get_rounded_ipod_capacity (mount_path);
128 models = g_hash_table_new_full (g_int_hash, g_int_equal,
129 NULL, (GDestroyNotify)g_list_free);
130 table = itdb_info_get_ipod_info_table ();
131 for (model_info = table;
132 model_info->model_number != NULL;
133 model_info++) {
134 GList *infos;
135
136 infos = g_hash_table_lookup (models,
137 &model_info->ipod_generation);
138 if (g_list_find_custom (infos, model_info, model_equals)) {
139 continue;
140 }
141 if (model_info->capacity == ipod_capacity) {
142 /* Steal the key from the hash table since we don't
143 * want 'infos' to be g_list_freed by the hash
144 * table destroy notify function
145 */
146 g_hash_table_steal (models,
147 &model_info->ipod_generation);
148 infos = g_list_prepend (infos, (gpointer)model_info);
149
150 g_hash_table_insert (models,
151 (gpointer)&model_info->ipod_generation,
152 infos);
153 }
154 }
155
156 return models;
157 }
158
159 struct FillModelContext {
160 GtkWidget *combo;
161 GtkTreeStore *store;
162 const Itdb_IpodInfo *ipod_info;
163 };
164
165 static void
166 fill_one_generation (gpointer key, gpointer value, gpointer data)
167 {
168 GList *infos;
169 GList *it;
170 Itdb_IpodGeneration generation;
171 gboolean first;
172 GtkTreeIter iter;
173 struct FillModelContext *ctx;
174
175 ctx = (struct FillModelContext *)data;
176 infos = (GList *)value;
177 generation = *(Itdb_IpodGeneration*)key;
178
179 first = TRUE;
180 for (it = infos; it != NULL; it = it->next) {
181 const Itdb_IpodInfo *info;
182 GtkTreeIter iter_child;
183
184 info = (const Itdb_IpodInfo *)it->data;
185 g_assert (info->ipod_generation == generation);
186
187 if (first) {
188 gtk_tree_store_append (ctx->store, &iter, NULL);
189 gtk_tree_store_set (ctx->store, &iter,
190 COL_INFO, info, -1);
191 first = FALSE;
192 }
193 gtk_tree_store_append (ctx->store, &iter_child, &iter);
194 gtk_tree_store_set (ctx->store, &iter_child,
195 COL_INFO, info, -1);
196 if (info == ctx->ipod_info) {
197 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (ctx->combo),
198 &iter_child);
199 }
200 }
201 }
202
203 void
204 rb_ipod_helpers_fill_model_combo (GtkWidget *combo, const char *mount_path)
205 {
206 GHashTable *models;
207 Itdb_Device *device;
208 GtkTreeStore *store;
209 const Itdb_IpodInfo *ipod_info;
210 GtkCellRenderer *renderer;
211 struct FillModelContext ctx;
212
213 device = itdb_device_new ();
214 itdb_device_set_mountpoint (device, mount_path);
215 itdb_device_read_sysinfo (device);
216 ipod_info = itdb_device_get_ipod_info (device);
217 itdb_device_free (device);
218
219 store = gtk_tree_store_new (1, G_TYPE_POINTER);
220 gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (store));
221
222 ctx.combo = combo;
223 ctx.store = store;
224 ctx.ipod_info = ipod_info;
225 models = build_model_table (mount_path);
226 g_hash_table_foreach (models, fill_one_generation, &ctx);
227 g_hash_table_destroy (models);
228 g_object_unref (store);
229
230 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
231
232 renderer = gtk_cell_renderer_text_new ();
233 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
234 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo),
235 renderer,
236 set_cell,
237 NULL, NULL);
238 }
239
240 static gchar *
241 rb_ipod_helpers_get_itunesdb_path (GMount *mount)
242 {
243 GFile *root;
244 gchar *mount_point;
245 gchar *result = NULL;
246
247 root = g_mount_get_root (mount);
248 if (root != NULL) {
249 mount_point = g_file_get_path (root);
250 if (mount_point != NULL) {
251 result = itdb_get_itunesdb_path (mount_point);
252 }
253
254 g_free (mount_point);
255 g_object_unref (root);
256 }
257
258 return result;
259 }
260
261 static guint64 get_fs_property (const char *mountpoint, const char *attr)
262 {
263 GFile *root;
264 GFileInfo *info;
265 guint64 value;
266
267 root = g_file_new_for_path (mountpoint);
268 info = g_file_query_filesystem_info (root, attr, NULL, NULL);
269 g_object_unref (G_OBJECT (root));
270 if (info == NULL) {
271 return 0;
272 }
273 if (!g_file_info_has_attribute (info, attr)) {
274 g_object_unref (G_OBJECT (info));
275 return 0;
276 }
277 value = g_file_info_get_attribute_uint64 (info, attr);
278 g_object_unref (G_OBJECT (info));
279
280 return value;
281 }
282
283 guint64
284 rb_ipod_helpers_get_capacity (const char *mountpoint)
285 {
286 return get_fs_property (mountpoint, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
287 }
288
289 guint64
290 rb_ipod_helpers_get_free_space (const char *mountpoint)
291 {
292 return get_fs_property (mountpoint, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
293 }
294
295 char *
296 rb_ipod_helpers_get_device (RBSource *source)
297 {
298 GMount *mount;
299 GVolume *volume;
300 char *device;
301
302 g_object_get (RB_SOURCE (source), "mount", &mount, NULL);
303 volume = g_mount_get_volume (mount);
304 device = g_volume_get_identifier (volume,
305 G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
306 g_object_unref (G_OBJECT (volume));
307 g_object_unref (G_OBJECT (mount));
308
309 return device;
310 }
311
312 static gboolean
313 rb_ipod_helpers_mount_has_ipod_db (GMount *mount)
314 {
315 char *itunesdb_path;
316 gboolean result;
317
318 itunesdb_path = rb_ipod_helpers_get_itunesdb_path (mount);
319
320 if (itunesdb_path != NULL) {
321 result = g_file_test (itunesdb_path, G_FILE_TEST_EXISTS);
322 } else {
323 result = FALSE;
324 }
325 g_free (itunesdb_path);
326
327 return result;
328 }
329
330 gboolean
331 rb_ipod_helpers_is_ipod (GMount *mount, MPIDDevice *device_info)
332 {
333 GFile *root;
334 gboolean result = FALSE;
335 char **protocols;
336
337 /* if we have specific information about the device, use it.
338 * otherwise, check if the device has an ipod device directory on it.
339 */
340 g_object_get (device_info, "access-protocols", &protocols, NULL);
341 if (protocols != NULL && g_strv_length (protocols) > 0) {
342 int i;
343
344 for (i = 0; protocols[i] != NULL; i++) {
345 if (g_str_equal (protocols[i], "ipod")) {
346 result = TRUE;
347 break;
348 }
349 }
350 } else {
351 root = g_mount_get_root (mount);
352 if (root != NULL) {
353 gchar *device_dir;
354
355 if (g_file_has_uri_scheme (root, "afc") != FALSE) {
356 gchar *uri;
357 uri = g_file_get_uri (root);
358 /* afc://<40 chars>:stuff */
359 g_assert (strlen (uri) >= 46);
360 if (uri[6 + 40] == ':' &&
361 uri[6 + 40 + 1] != '1') {
362 result = FALSE;
363 } else {
364 result = TRUE;
365 }
366 g_free (uri);
367 } else {
368 gchar *mount_point;
369 mount_point = g_file_get_path (root);
370 if (mount_point != NULL) {
371 device_dir = itdb_get_device_dir (mount_point);
372 if (device_dir != NULL) {
373 result = g_file_test (device_dir,
374 G_FILE_TEST_IS_DIR);
375 g_free (device_dir);
376 }
377 }
378
379 g_free (mount_point);
380 }
381 g_object_unref (root);
382 }
383 }
384
385 g_strfreev (protocols);
386 return result;
387 }
388
389 gboolean
390 rb_ipod_helpers_needs_init (GMount *mount)
391 {
392 /* This function is a useless one-liner for now, but it should check
393 * for the existence of the firsttime file on the ipod to tell if
394 * the ipod is new or not
395 */
396 return (!rb_ipod_helpers_mount_has_ipod_db (mount));
397 }