Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
rb-generic-player-source.c:831:3 | clang-analyzer | Value stored to 'is_song' is never read |
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2004 James Livingston <doclivingston@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * The Rhythmbox authors hereby grant permission for non-GPL compatible
11 * GStreamer plugins to be used and distributed together with GStreamer
12 * and Rhythmbox. This permission is above and beyond the permissions granted
13 * by the GPL license by which Rhythmbox is covered. If you modify this code
14 * you may extend this exception to your version of the code, but you are not
15 * obligated to do so. If you do not wish to do so, delete this exception
16 * statement from your version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 */
28
29 #define __EXTENSIONS__
30
31 #include "config.h"
32
33 #include <string.h>
34
35 #include <gtk/gtk.h>
36 #include <glib/gi18n.h>
37
38 #include <totem-pl-parser.h>
39
40 #include "mediaplayerid.h"
41
42 #include "rb-generic-player-source.h"
43 #include "rb-generic-player-playlist-source.h"
44 #include "rb-removable-media-manager.h"
45 #include "rb-transfer-target.h"
46 #include "rb-device-source.h"
47 #include "rb-debug.h"
48 #include "rb-util.h"
49 #include "rb-file-helpers.h"
50 #include "rhythmdb.h"
51 #include "rb-dialog.h"
52 #include "rhythmdb-import-job.h"
53 #include "rb-import-errors-source.h"
54 #include "rb-builder-helpers.h"
55 #include "rb-gst-media-types.h"
56 #include "rb-sync-settings.h"
57 #include "rb-missing-plugins.h"
58
59 static void rb_generic_player_device_source_init (RBDeviceSourceInterface *interface);
60 static void rb_generic_player_source_transfer_target_init (RBTransferTargetInterface *interface);
61
62 static void impl_constructed (GObject *object);
63 static void impl_dispose (GObject *object);
64 static void impl_set_property (GObject *object,
65 guint prop_id,
66 const GValue *value,
67 GParamSpec *pspec);
68 static void impl_get_property (GObject *object,
69 guint prop_id,
70 GValue *value,
71 GParamSpec *pspec);
72
73 static void load_songs (RBGenericPlayerSource *source);
74
75 static gboolean impl_show_popup (RBDisplayPage *page);
76 static void impl_delete_thyself (RBDisplayPage *page);
77 static void impl_get_status (RBDisplayPage *page, char **text, char **progress_text, float *progress);
78 static void impl_selected (RBDisplayPage *page);
79
80 static gboolean impl_can_paste (RBSource *source);
81 static RBTrackTransferBatch *impl_paste (RBSource *source, GList *entries);
82 static gboolean impl_can_delete (RBSource *source);
83 static void impl_delete (RBSource *source);
84
85 static char* impl_build_dest_uri (RBTransferTarget *target,
86 RhythmDBEntry *entry,
87 const char *media_type,
88 const char *extension);
89 static guint64 impl_get_capacity (RBMediaPlayerSource *source);
90 static guint64 impl_get_free_space (RBMediaPlayerSource *source);
91 static void impl_get_entries (RBMediaPlayerSource *source, const char *category, GHashTable *map);
92 static void impl_delete_entries (RBMediaPlayerSource *source,
93 GList *entries,
94 RBMediaPlayerSourceDeleteCallback callback,
95 gpointer data,
96 GDestroyNotify destroy_data);
97 static void impl_show_properties (RBMediaPlayerSource *source, GtkWidget *info_box, GtkWidget *notebook);
98 static void impl_add_playlist (RBMediaPlayerSource *source, char *name, GList *entries);
99 static void impl_remove_playlists (RBMediaPlayerSource *source);
100
101 static char *default_get_mount_path (RBGenericPlayerSource *source);
102 static void default_load_playlists (RBGenericPlayerSource *source);
103 static char * default_uri_from_playlist_uri (RBGenericPlayerSource *source,
104 const char *uri);
105 static char * default_uri_to_playlist_uri (RBGenericPlayerSource *source,
106 const char *uri,
107 TotemPlParserType playlist_type);
108
109 enum
110 {
111 PROP_0,
112 PROP_MOUNT,
113 PROP_IGNORE_ENTRY_TYPE,
114 PROP_ERROR_ENTRY_TYPE,
115 PROP_DEVICE_INFO
116 };
117
118 typedef struct
119 {
120 RhythmDB *db;
121
122 gboolean loaded;
123 RhythmDBImportJob *import_job;
124 gint load_playlists_id;
125 GList *playlists;
126 RBSource *import_errors;
127
128 char *mount_path;
129
130 /* entry types */
131 RhythmDBEntryType *ignore_type;
132 RhythmDBEntryType *error_type;
133
134 /* information derived from volume */
135 gboolean read_only;
136
137 MPIDDevice *device_info;
138 GMount *mount;
139
140 } RBGenericPlayerSourcePrivate;
141
142 G_DEFINE_DYNAMIC_TYPE_EXTENDED (
143 RBGenericPlayerSource,
144 rb_generic_player_source,
145 RB_TYPE_MEDIA_PLAYER_SOURCE,
146 0,
147 G_IMPLEMENT_INTERFACE_DYNAMIC (RB_TYPE_DEVICE_SOURCE, rb_generic_player_device_source_init)
148 G_IMPLEMENT_INTERFACE_DYNAMIC (RB_TYPE_TRANSFER_TARGET, rb_generic_player_source_transfer_target_init))
149
150 #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_GENERIC_PLAYER_SOURCE, RBGenericPlayerSourcePrivate))
151
152 static void
153 rb_generic_player_source_class_init (RBGenericPlayerSourceClass *klass)
154 {
155 GObjectClass *object_class = G_OBJECT_CLASS (klass);
156 RBDisplayPageClass *page_class = RB_DISPLAY_PAGE_CLASS (klass);
157 RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
158 RBMediaPlayerSourceClass *mps_class = RB_MEDIA_PLAYER_SOURCE_CLASS (klass);
159
160 object_class->set_property = impl_set_property;
161 object_class->get_property = impl_get_property;
162 object_class->constructed = impl_constructed;
163 object_class->dispose = impl_dispose;
164
165 page_class->show_popup = impl_show_popup;
166 page_class->delete_thyself = impl_delete_thyself;
167 page_class->get_status = impl_get_status;
168 page_class->selected = impl_selected;
169
170 source_class->impl_can_delete = impl_can_delete;
171 source_class->impl_delete = impl_delete;
172 source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_false_function;
173 source_class->impl_can_paste = impl_can_paste;
174 source_class->impl_paste = impl_paste;
175 source_class->impl_want_uri = rb_device_source_want_uri;
176 source_class->impl_uri_is_source = rb_device_source_uri_is_source;
177
178 mps_class->impl_get_entries = impl_get_entries;
179 mps_class->impl_get_capacity = impl_get_capacity;
180 mps_class->impl_get_free_space = impl_get_free_space;
181 mps_class->impl_delete_entries = impl_delete_entries;
182 mps_class->impl_show_properties = impl_show_properties;
183 mps_class->impl_add_playlist = impl_add_playlist;
184 mps_class->impl_remove_playlists = impl_remove_playlists;
185
186 klass->impl_get_mount_path = default_get_mount_path;
187 klass->impl_load_playlists = default_load_playlists;
188 klass->impl_uri_from_playlist_uri = default_uri_from_playlist_uri;
189 klass->impl_uri_to_playlist_uri = default_uri_to_playlist_uri;
190
191 g_object_class_install_property (object_class,
192 PROP_ERROR_ENTRY_TYPE,
193 g_param_spec_object ("error-entry-type",
194 "Error entry type",
195 "Entry type to use for import error entries added by this source",
196 RHYTHMDB_TYPE_ENTRY_TYPE,
197 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
198 g_object_class_install_property (object_class,
199 PROP_IGNORE_ENTRY_TYPE,
200 g_param_spec_object ("ignore-entry-type",
201 "Ignore entry type",
202 "Entry type to use for ignore entries added by this source",
203 RHYTHMDB_TYPE_ENTRY_TYPE,
204 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
205 g_object_class_install_property (object_class,
206 PROP_DEVICE_INFO,
207 g_param_spec_object ("device-info",
208 "device info",
209 "device information object",
210 MPID_TYPE_DEVICE,
211 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
212 g_object_class_install_property (object_class,
213 PROP_MOUNT,
214 g_param_spec_object ("mount",
215 "mount",
216 "GMount object",
217 G_TYPE_MOUNT,
218 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
219
220 g_type_class_add_private (klass, sizeof (RBGenericPlayerSourcePrivate));
221 }
222
223 static void
224 rb_generic_player_device_source_init (RBDeviceSourceInterface *interface)
225 {
226 /* nothing */
227 }
228
229 static void
230 rb_generic_player_source_transfer_target_init (RBTransferTargetInterface *interface)
231 {
232 interface->build_dest_uri = impl_build_dest_uri;
233 }
234
235 static void
236 rb_generic_player_source_class_finalize (RBGenericPlayerSourceClass *klass)
237 {
238 }
239
240 static void
241 rb_generic_player_source_init (RBGenericPlayerSource *source)
242 {
243
244 }
245
246 static void
247 impl_constructed (GObject *object)
248 {
249 RBGenericPlayerSource *source;
250 RBGenericPlayerSourcePrivate *priv;
251 RhythmDBEntryType *entry_type;
252 char **playlist_formats;
253 char **output_formats;
254 char *mount_name;
255 RBShell *shell;
256 GFile *root;
257 GFileInfo *info;
258 GError *error = NULL;
259
260 RB_CHAIN_GOBJECT_METHOD (rb_generic_player_source_parent_class, constructed, object);
261 source = RB_GENERIC_PLAYER_SOURCE (object);
262
263 priv = GET_PRIVATE (source);
264
265 rb_device_source_set_display_details (RB_DEVICE_SOURCE (source));
266
267 g_object_get (source,
268 "shell", &shell,
269 "entry-type", &entry_type,
270 NULL);
271
272 g_object_get (shell, "db", &priv->db, NULL);
273
274 priv->import_errors = rb_import_errors_source_new (shell,
275 priv->error_type,
276 entry_type,
277 priv->ignore_type);
278
279 g_object_unref (shell);
280
281 root = g_mount_get_root (priv->mount);
282 mount_name = g_mount_get_name (priv->mount);
283
284 info = g_file_query_filesystem_info (root, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, NULL, &error);
285 if (error != NULL) {
286 rb_debug ("error querying filesystem info for %s: %s", mount_name, error->message);
287 g_error_free (error);
288 priv->read_only = FALSE;
289 } else {
290 priv->read_only = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY);
291 g_object_unref (info);
292 }
293
294 g_free (mount_name);
295 g_object_unref (root);
296
297 g_object_get (priv->device_info, "playlist-formats", &playlist_formats, NULL);
298 if (playlist_formats != NULL && g_strv_length (playlist_formats) > 0) {
299 g_object_set (entry_type, "has-playlists", TRUE, NULL);
300 }
301 g_strfreev (playlist_formats);
302 g_object_unref (entry_type);
303
304 g_object_get (priv->device_info, "output-formats", &output_formats, NULL);
305 if (output_formats != NULL) {
306 GstEncodingTarget *target;
307 int i;
308
309 target = gst_encoding_target_new ("generic-player", "device", "", NULL);
310 for (i = 0; output_formats[i] != NULL; i++) {
311 const char *media_type = rb_gst_mime_type_to_media_type (output_formats[i]);
312 if (media_type != NULL) {
313 GstEncodingProfile *profile;
314 profile = rb_gst_get_encoding_profile (media_type);
315 if (profile != NULL) {
316 gst_encoding_target_add_profile (target, profile);
317 }
318 }
319 }
320 g_object_set (source, "encoding-target", target, NULL);
321 }
322 g_strfreev (output_formats);
323
324 }
325
326 static void
327 impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
328 {
329 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (object);
330
331 switch (prop_id) {
332 case PROP_IGNORE_ENTRY_TYPE:
333 priv->ignore_type = g_value_get_object (value);
334 break;
335 case PROP_ERROR_ENTRY_TYPE:
336 priv->error_type = g_value_get_object (value);
337 break;
338 case PROP_DEVICE_INFO:
339 priv->device_info = g_value_dup_object (value);
340 break;
341 case PROP_MOUNT:
342 priv->mount = g_value_dup_object (value);
343 break;
344 default:
345 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346 break;
347 }
348 }
349
350 static void
351 impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
352 {
353 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (object);
354
355 switch (prop_id) {
356 case PROP_IGNORE_ENTRY_TYPE:
357 g_value_set_object (value, priv->ignore_type);
358 break;
359 case PROP_ERROR_ENTRY_TYPE:
360 g_value_set_object (value, priv->error_type);
361 break;
362 case PROP_DEVICE_INFO:
363 g_value_set_object (value, priv->device_info);
364 break;
365 case PROP_MOUNT:
366 g_value_set_object (value, priv->mount);
367 break;
368 default:
369 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
370 break;
371 }
372 }
373
374 static void
375 impl_dispose (GObject *object)
376 {
377 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (object);
378
379 if (priv->load_playlists_id != 0) {
380 g_source_remove (priv->load_playlists_id);
381 priv->load_playlists_id = 0;
382 }
383
384 if (priv->db != NULL) {
385 if (priv->ignore_type != NULL) {
386 rhythmdb_entry_delete_by_type (priv->db, priv->ignore_type);
387 g_object_unref (priv->ignore_type);
388 priv->ignore_type = NULL;
389 }
390 if (priv->error_type != NULL) {
391 rhythmdb_entry_delete_by_type (priv->db, priv->error_type);
392 g_object_unref (priv->error_type);
393 priv->error_type = NULL;
394 }
395
396 g_object_unref (priv->db);
397 priv->db = NULL;
398 }
399
400 if (priv->import_job != NULL) {
401 rhythmdb_import_job_cancel (priv->import_job);
402 g_object_unref (priv->import_job);
403 priv->import_job = NULL;
404 }
405
406 if (priv->device_info != NULL) {
407 g_object_unref (priv->device_info);
408 priv->device_info = NULL;
409 }
410
411 if (priv->mount != NULL) {
412 g_object_unref (priv->mount);
413 priv->mount = NULL;
414 }
415
416 G_OBJECT_CLASS (rb_generic_player_source_parent_class)->dispose (object);
417 }
418
419 RBSource *
420 rb_generic_player_source_new (GObject *plugin, RBShell *shell, GMount *mount, MPIDDevice *device_info)
421 {
422 RBGenericPlayerSource *source;
423 RhythmDBEntryType *entry_type;
424 RhythmDBEntryType *error_type;
425 RhythmDBEntryType *ignore_type;
426 RhythmDB *db;
427 GVolume *volume;
428 GSettings *settings;
429 char *name;
430 char *path;
431
432 volume = g_mount_get_volume (mount);
433
434 g_object_get (shell, "db", &db, NULL);
435 path = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
436
437 name = g_strdup_printf ("generic audio player: %s", path);
438 entry_type = g_object_new (RHYTHMDB_TYPE_ENTRY_TYPE,
439 "db", db,
440 "name", name,
441 "save-to-disk", FALSE,
442 "category", RHYTHMDB_ENTRY_NORMAL,
443 NULL);
444 rhythmdb_register_entry_type (db, entry_type);
445 g_free (name);
446
447 name = g_strdup_printf ("generic audio player (ignore): %s", path);
448 ignore_type = g_object_new (RHYTHMDB_TYPE_ENTRY_TYPE,
449 "db", db,
450 "name", name,
451 "save-to-disk", FALSE,
452 "category", RHYTHMDB_ENTRY_VIRTUAL,
453 NULL);
454 rhythmdb_register_entry_type (db, ignore_type);
455 g_free (name);
456
457 name = g_strdup_printf ("generic audio player (errors): %s", path);
458 error_type = g_object_new (RHYTHMDB_TYPE_ENTRY_TYPE,
459 "db", db,
460 "name", name,
461 "save-to-disk", FALSE,
462 "category", RHYTHMDB_ENTRY_VIRTUAL,
463 NULL);
464 rhythmdb_register_entry_type (db, error_type);
465 g_free (name);
466
467 g_object_unref (db);
468 g_object_unref (volume);
469 g_free (path);
470
471 settings = g_settings_new ("org.gnome.rhythmbox.plugins.generic-player");
472 source = RB_GENERIC_PLAYER_SOURCE (g_object_new (RB_TYPE_GENERIC_PLAYER_SOURCE,
473 "plugin", plugin,
474 "entry-type", entry_type,
475 "ignore-entry-type", ignore_type,
476 "error-entry-type", error_type,
477 "mount", mount,
478 "shell", shell,
479 "device-info", device_info,
480 "load-status", RB_SOURCE_LOAD_STATUS_LOADING,
481 "settings", g_settings_get_child (settings, "source"),
482 "toolbar-path", "/GenericPlayerSourceToolBar",
483 NULL));
484 g_object_unref (settings);
485
486 rb_shell_register_entry_type_for_source (shell, RB_SOURCE (source), entry_type);
487
488 return RB_SOURCE (source);
489 }
490
491 static void
492 impl_delete_thyself (RBDisplayPage *page)
493 {
494 GList *pl;
495 GList *p;
496 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (page);
497
498 /* take a copy of the list first, as playlist_deleted_cb modifies priv->playlists */
499 pl = g_list_copy (priv->playlists);
500 for (p = pl; p != NULL; p = p->next) {
501 RBDisplayPage *playlist_page = RB_DISPLAY_PAGE (p->data);
502 rb_display_page_delete_thyself (playlist_page);
503 }
504 g_list_free (priv->playlists);
505 g_list_free (pl);
506 priv->playlists = NULL;
507
508 if (priv->import_errors != NULL) {
509 rb_display_page_delete_thyself (RB_DISPLAY_PAGE (priv->import_errors));
510 priv->import_errors = NULL;
511 }
512
513 RB_DISPLAY_PAGE_CLASS (rb_generic_player_source_parent_class)->delete_thyself (page);
514 }
515
516 static gboolean
517 ensure_loaded (RBGenericPlayerSource *source)
518 {
519 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
520 RBSourceLoadStatus status;
521
522 if (priv->loaded) {
523 g_object_get (source, "load-status", &status, NULL);
524 return (status == RB_SOURCE_LOAD_STATUS_LOADED);
525 } else {
526 priv->loaded = TRUE;
527 rb_media_player_source_load (RB_MEDIA_PLAYER_SOURCE (source));
528 load_songs (source);
529 return FALSE;
530 }
531 }
532
533 static void
534 impl_selected (RBDisplayPage *page)
535 {
536 ensure_loaded (RB_GENERIC_PLAYER_SOURCE (page));
537 }
538
539 static void
540 import_complete_cb (RhythmDBImportJob *job, int total, RBGenericPlayerSource *source)
541 {
542 RBGenericPlayerSourceClass *klass = RB_GENERIC_PLAYER_SOURCE_GET_CLASS (source);
543 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
544 RBShell *shell;
545
546 GDK_THREADS_ENTER ();
547
548 g_object_get (source, "shell", &shell, NULL);
549 rb_shell_append_display_page (shell, RB_DISPLAY_PAGE (priv->import_errors), RB_DISPLAY_PAGE (source));
550 g_object_unref (shell);
551
552 if (klass->impl_load_playlists)
553 klass->impl_load_playlists (source);
554
555 g_object_unref (priv->import_job);
556 priv->import_job = NULL;
557
558 rb_display_page_notify_status_changed (RB_DISPLAY_PAGE (source));
559 g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
560
561 rb_transfer_target_transfer (RB_TRANSFER_TARGET (source), NULL, FALSE);
562
563 GDK_THREADS_LEAVE ();
564 }
565
566 static void
567 import_status_changed_cb (RhythmDBImportJob *job, int total, int imported, RBGenericPlayerSource *source)
568 {
569 rb_display_page_notify_status_changed (RB_DISPLAY_PAGE (source));
570 }
571
572 static void
573 load_songs (RBGenericPlayerSource *source)
574 {
575 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
576 RhythmDBEntryType *entry_type;
577 char **audio_folders;
578 char *mount_path;
579
580 mount_path = rb_generic_player_source_get_mount_path (source);
581 g_object_get (source, "entry-type", &entry_type, NULL);
582
583 /* if we have a set of folders on the device containing audio files,
584 * load only those folders, otherwise add the whole volume.
585 */
586 priv->import_job = rhythmdb_import_job_new (priv->db, entry_type, priv->ignore_type, priv->error_type);
587
588 g_signal_connect_object (priv->import_job, "complete", G_CALLBACK (import_complete_cb), source, 0);
589 g_signal_connect_object (priv->import_job, "status-changed", G_CALLBACK (import_status_changed_cb), source, 0);
590
591 g_object_get (priv->device_info, "audio-folders", &audio_folders, NULL);
592 if (audio_folders != NULL && g_strv_length (audio_folders) > 0) {
593 int af;
594 for (af=0; audio_folders[af] != NULL; af++) {
595 char *path;
596 path = rb_uri_append_path (mount_path, audio_folders[af]);
597 rb_debug ("loading songs from device audio folder %s", path);
598 rhythmdb_import_job_add_uri (priv->import_job, path);
599 g_free (path);
600 }
601 } else {
602 rb_debug ("loading songs from device mount path %s", mount_path);
603 rhythmdb_import_job_add_uri (priv->import_job, mount_path);
604 }
605 g_strfreev (audio_folders);
606
607 rhythmdb_import_job_start (priv->import_job);
608
609 g_object_unref (entry_type);
610 g_free (mount_path);
611 }
612
613 char *
614 rb_generic_player_source_get_mount_path (RBGenericPlayerSource *source)
615 {
616 RBGenericPlayerSourceClass *klass = RB_GENERIC_PLAYER_SOURCE_GET_CLASS (source);
617
618 return klass->impl_get_mount_path (source);
619 }
620
621 static char *
622 default_get_mount_path (RBGenericPlayerSource *source)
623 {
624 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
625
626 if (priv->mount_path == NULL) {
627 GFile *root;
628
629 root = g_mount_get_root (priv->mount);
630 if (root != NULL) {
631 priv->mount_path = g_file_get_uri (root);
632 g_object_unref (root);
633 }
634 }
635
636 return g_strdup (priv->mount_path);
637 }
638
639 gboolean
640 rb_generic_player_is_mount_player (GMount *mount, MPIDDevice *device_info)
641 {
642 char **protocols;
643 gboolean result = FALSE;
644 int i;
645
646 /* claim anything with 'storage' as an access protocol */
647 g_object_get (device_info, "access-protocols", &protocols, NULL);
648 if (protocols != NULL) {
649 for (i = 0; protocols[i] != NULL; i++) {
650 if (g_str_equal (protocols[i], "storage")) {
651 result = TRUE;
652 break;
653 }
654 }
655 g_strfreev (protocols);
656 }
657
658 return result;
659 }
660
661 static gboolean
662 impl_show_popup (RBDisplayPage *page)
663 {
664 _rb_display_page_show_popup (page, "/GenericPlayerSourcePopup");
665 return TRUE;
666 }
667
668 static void
669 impl_get_status (RBDisplayPage *page, char **text, char **progress_text, float *progress)
670 {
671 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (page);
672
673 /* get default status text first */
674 RB_DISPLAY_PAGE_CLASS (rb_generic_player_source_parent_class)->get_status (page, text, progress_text, progress);
675
676 /* override with bits of import status */
677 if (priv->import_job != NULL) {
678 _rb_source_set_import_status (RB_SOURCE (page), priv->import_job, progress_text, progress);
679 }
680 }
681
682 /* code for playlist loading */
683
684 static void
685 playlist_deleted_cb (RBSource *playlist, RBGenericPlayerSource *source)
686 {
687 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
688 GList *p;
689
690 p = g_list_find (priv->playlists, playlist);
691 if (p != NULL) {
692 priv->playlists = g_list_delete_link (priv->playlists, p);
693 g_object_unref (playlist);
694 }
695 }
696
697 void
698 rb_generic_player_source_add_playlist (RBGenericPlayerSource *source,
699 RBShell *shell,
700 RBSource *playlist)
701 {
702 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
703 g_object_ref (playlist);
704 priv->playlists = g_list_prepend (priv->playlists, playlist);
705
706 g_signal_connect_object (playlist, "deleted", G_CALLBACK (playlist_deleted_cb), source, 0);
707
708 rb_shell_append_display_page (shell, RB_DISPLAY_PAGE (playlist), RB_DISPLAY_PAGE (source));
709 }
710
711
712
713 static char *
714 default_uri_from_playlist_uri (RBGenericPlayerSource *source, const char *uri)
715 {
716 char *mount_uri;
717 char *full_uri;
718
719 mount_uri = rb_generic_player_source_get_mount_path (source);
720 if (g_str_has_prefix (uri, mount_uri)) {
721 return g_strdup (uri);
722 }
723
724 full_uri = rb_uri_append_uri (mount_uri, uri);
725 g_free (mount_uri);
726
727 rb_debug ("%s => %s", uri, full_uri);
728 return full_uri;
729 }
730
731 static char *
732 default_uri_to_playlist_uri (RBGenericPlayerSource *source, const char *uri, TotemPlParserType playlist_type)
733 {
734 char *mount_uri;
735 char *playlist_uri;
736
737 switch (playlist_type) {
738 case TOTEM_PL_PARSER_IRIVER_PLA:
739 /* we need absolute paths within the device filesystem for this format */
740 mount_uri = rb_generic_player_source_get_mount_path (source);
741 if (g_str_has_prefix (uri, mount_uri) == FALSE) {
742 rb_debug ("uri %s is not under device mount uri %s", uri, mount_uri);
743 return NULL;
744 }
745
746 playlist_uri = g_strdup_printf ("file://%s", uri + strlen (mount_uri));
747 return playlist_uri;
748
749 case TOTEM_PL_PARSER_M3U_DOS:
750 case TOTEM_PL_PARSER_M3U:
751 case TOTEM_PL_PARSER_PLS:
752 default:
753 /* leave the URI as-is, so we end up with relative paths in the playlist file */
754 return g_strdup (uri);
755 }
756 }
757
758 char *
759 rb_generic_player_source_uri_from_playlist_uri (RBGenericPlayerSource *source, const char *uri)
760 {
761 RBGenericPlayerSourceClass *klass = RB_GENERIC_PLAYER_SOURCE_GET_CLASS (source);
762
763 return klass->impl_uri_from_playlist_uri (source, uri);
764 }
765
766 char *
767 rb_generic_player_source_uri_to_playlist_uri (RBGenericPlayerSource *source, const char *uri, TotemPlParserType playlist_type)
768 {
769 RBGenericPlayerSourceClass *klass = RB_GENERIC_PLAYER_SOURCE_GET_CLASS (source);
770
771 return klass->impl_uri_to_playlist_uri (source, uri, playlist_type);
772 }
773
774 static void
775 load_playlist_file (RBGenericPlayerSource *source,
776 const char *playlist_path,
777 const char *rel_path)
778 {
779 RhythmDBEntryType *entry_type;
780 RBGenericPlayerPlaylistSource *playlist;
781 RBShell *shell;
782 char *mount_path;
783
784 g_object_get (source,
785 "shell", &shell,
786 "entry-type", &entry_type,
787 NULL);
788
789 mount_path = rb_generic_player_source_get_mount_path (source);
790 rb_debug ("loading playlist %s", playlist_path);
791 playlist = RB_GENERIC_PLAYER_PLAYLIST_SOURCE (
792 rb_generic_player_playlist_source_new (shell,
793 source,
794 playlist_path,
795 mount_path,
796 entry_type));
797
798 if (playlist != NULL) {
799 rb_generic_player_source_add_playlist (source, shell, RB_SOURCE (playlist));
800 }
801
802 g_object_unref (entry_type);
803 g_object_unref (shell);
804 g_free (mount_path);
805 }
806
807 static gboolean
808 visit_playlist_dirs (GFile *file,
809 gboolean dir,
810 RBGenericPlayerSource *source)
811 {
812 char *basename;
813 char *uri;
814 RhythmDBEntry *entry;
815 RhythmDBEntryType *entry_type;
816 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
817
818 if (dir) {
819 return TRUE;
820 }
821
822 /* check if we've already got an entry
823 * for this file, just to save some i/o.
824 */
825 uri = g_file_get_uri (file);
826 entry = rhythmdb_entry_lookup_by_location (priv->db, uri);
827 g_free (uri);
828 if (entry != NULL) {
829 gboolean is_song;
830
831 is_song = FALSE;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
832
833 g_object_get (source, "entry-type", &entry_type, NULL);
834 is_song = (rhythmdb_entry_get_entry_type (entry) == entry_type);
835 g_object_unref (entry_type);
836
837 if (is_song) {
838 rb_debug ("%s was loaded as a song",
839 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
840 return TRUE;
841 }
842 }
843
844 basename = g_file_get_basename (file);
845 if (strcmp (basename, ".is_audio_player") != 0) {
846 char *playlist_path;
847 playlist_path = g_file_get_path (file);
848 load_playlist_file (source, playlist_path, basename);
849 g_free (playlist_path);
850 }
851
852 g_free (basename);
853
854 return TRUE;
855 }
856
857 static void
858 default_load_playlists (RBGenericPlayerSource *source)
859 {
860 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
861 char *mount_path;
862 char *playlist_path;
863 char *full_playlist_path;
864 char **playlist_formats;
865
866 mount_path = rb_generic_player_source_get_mount_path (source);
867
868 playlist_path = rb_generic_player_source_get_playlist_path (RB_GENERIC_PLAYER_SOURCE (source));
869 if (playlist_path) {
870
871 /* If the device only supports a single playlist, just load that */
872 if (g_str_has_suffix (playlist_path, ".m3u") ||
873 g_str_has_suffix (playlist_path, ".pls")) {
874 full_playlist_path = rb_uri_append_path (mount_path, playlist_path);
875 if (rb_uri_exists (full_playlist_path)) {
876 load_playlist_file (source, full_playlist_path, playlist_path);
877 }
878
879 g_free (full_playlist_path);
880 g_free (playlist_path);
881 return;
882 }
883
884 /* Otherwise, limit the search to the device's playlist folder */
885 full_playlist_path = rb_uri_append_path (mount_path, playlist_path);
886 rb_debug ("constructed playlist search path %s", full_playlist_path);
887 } else {
888 full_playlist_path = g_strdup (mount_path);
889 }
890
891 /* only try to load playlists if the device has at least one playlist format */
892 g_object_get (priv->device_info, "playlist-formats", &playlist_formats, NULL);
893 if (playlist_formats != NULL && g_strv_length (playlist_formats) > 0) {
894 rb_debug ("searching for playlists in %s", playlist_path);
895 rb_uri_handle_recursively (full_playlist_path,
896 NULL,
897 (RBUriRecurseFunc) visit_playlist_dirs,
898 source);
899 }
900 g_strfreev (playlist_formats);
901
902 g_free (playlist_path);
903 g_free (full_playlist_path);
904 g_free (mount_path);
905 }
906
907 static gboolean
908 impl_can_paste (RBSource *source)
909 {
910 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
911
912 return (priv->read_only == FALSE);
913 }
914
915 static RBTrackTransferBatch *
916 impl_paste (RBSource *source, GList *entries)
917 {
918 gboolean defer;
919
920 defer = (ensure_loaded (RB_GENERIC_PLAYER_SOURCE (source)) == FALSE);
921 return rb_transfer_target_transfer (RB_TRANSFER_TARGET (source), entries, defer);
922 }
923
924 static gboolean
925 impl_can_delete (RBSource *source)
926 {
927 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
928
929 return (priv->read_only == FALSE);
930 }
931
932 static gboolean
933 can_delete_directory (RBGenericPlayerSource *source, GFile *dir)
934 {
935 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
936 gboolean result;
937 GMount *mount;
938 GFile *root;
939 char **audio_folders;
940 int i;
941
942 g_object_get (source, "mount", &mount, NULL);
943 root = g_mount_get_root (mount);
944 g_object_unref (mount);
945
946 /* can't delete the root dir */
947 if (g_file_equal (dir, root)) {
948 rb_debug ("refusing to delete device root dir");
949 g_object_unref (root);
950 return FALSE;
951 }
952
953 /* can't delete the device's audio folders */
954 result = TRUE;
955 g_object_get (priv->device_info, "audio-folders", &audio_folders, NULL);
956 if (audio_folders != NULL && g_strv_length (audio_folders) > 0) {
957 for (i = 0; audio_folders[i] != NULL; i++) {
958 GFile *check;
959
960 check = g_file_resolve_relative_path (root, audio_folders[i]);
961 if (g_file_equal (dir, check)) {
962 rb_debug ("refusing to delete device audio folder %s", audio_folders[i]);
963 result = FALSE;
964 }
965 g_object_unref (check);
966 }
967 }
968 g_strfreev (audio_folders);
969
970 /* can delete anything else */
971 g_object_unref (root);
972 return result;
973 }
974
975 void
976 rb_generic_player_source_delete_entries (RBGenericPlayerSource *source, GList *entries)
977 {
978 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
979 GList *tem;
980
981 if (priv->read_only != FALSE)
982 return;
983
984 for (tem = entries; tem != NULL; tem = tem->next) {
985 RhythmDBEntry *entry;
986 const char *uri;
987 GFile *file;
988 GFile *dir;
989
990 entry = tem->data;
991 uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
992 file = g_file_new_for_uri (uri);
993 g_file_delete (file, NULL, NULL);
994
995 /* now walk up the directory structure and delete empty dirs
996 * until we reach the root or one of the device's audio folders.
997 */
998 dir = g_file_get_parent (file);
999 while (can_delete_directory (source, dir)) {
1000 GFile *parent;
1001 char *path;
1002
1003 path = g_file_get_path (dir);
1004 rb_debug ("trying to delete %s", path);
1005 g_free (path);
1006
1007 if (g_file_delete (dir, NULL, NULL) == FALSE) {
1008 break;
1009 }
1010
1011 parent = g_file_get_parent (dir);
1012 if (parent == NULL) {
1013 break;
1014 }
1015 g_object_unref (dir);
1016 dir = parent;
1017 }
1018
1019 g_object_unref (dir);
1020 g_object_unref (file);
1021
1022 rhythmdb_entry_delete (priv->db, entry);
1023 }
1024
1025 rhythmdb_commit (priv->db);
1026 }
1027
1028 static void
1029 impl_delete (RBSource *source)
1030 {
1031 RBEntryView *view;
1032 GList *sel;
1033
1034 view = rb_source_get_entry_view (source);
1035 sel = rb_entry_view_get_selected_entries (view);
1036
1037 rb_generic_player_source_delete_entries (RB_GENERIC_PLAYER_SOURCE (source), sel);
1038 g_list_foreach (sel, (GFunc)rhythmdb_entry_unref, NULL);
1039 g_list_free (sel);
1040 }
1041
1042 static char *
1043 sanitize_path (const char *str)
1044 {
1045 char *res = NULL;
1046 char *s;
1047
1048 /* Skip leading periods, otherwise files disappear... */
1049 while (*str == '.')
1050 str++;
1051
1052 s = g_strdup (str);
1053 g_strdelimit (s, "/", '-');
1054 res = g_uri_escape_string (s, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT, TRUE);
1055 g_free (s);
1056 return res;
1057 }
1058
1059 static char *
1060 impl_build_dest_uri (RBTransferTarget *target,
1061 RhythmDBEntry *entry,
1062 const char *media_type,
1063 const char *extension)
1064 {
1065 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (target);
1066 const char *in_artist;
1067 char *artist, *album, *title;
1068 gulong track_number, disc_number;
1069 const char *folders;
1070 char **audio_folders;
1071 char *mount_path;
1072 char *number;
1073 char *file = NULL;
1074 char *path;
1075 char *ext;
1076
1077 rb_debug ("building dest uri for entry at %s", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
1078
1079 if (extension != NULL) {
1080 ext = g_strconcat (".", extension, NULL);
1081 } else {
1082 ext = g_strdup ("");
1083 }
1084
1085 in_artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM_ARTIST);
1086 if (in_artist[0] == '\0') {
1087 in_artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
1088 }
1089 artist = sanitize_path (in_artist);
1090 album = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
1091 title = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE));
1092
1093 /* we really do need to fix this so untagged entries actually have NULL rather than
1094 * a translated string.
1095 */
1096 if (strcmp (artist, _("Unknown")) == 0 && strcmp (album, _("Unknown")) == 0 &&
1097 g_str_has_suffix (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION), title)) {
1098 /* file isn't tagged, so just use the filename as-is, replacing the extension */
1099 char *p;
1100
1101 p = g_utf8_strrchr (title, -1, '.');
1102 if (p != NULL) {
1103 *p = '\0';
1104 }
1105 file = g_strdup_printf ("%s%s", title, ext);
1106 }
1107
1108 if (file == NULL) {
1109 int folder_depth;
1110
1111 track_number = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER);
1112 disc_number = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER);
1113 if (disc_number > 0)
1114 number = g_strdup_printf ("%.02u.%.02u", (guint)disc_number, (guint)track_number);
1115 else
1116 number = g_strdup_printf ("%.02u", (guint)track_number);
1117
1118 g_object_get (priv->device_info, "folder-depth", &folder_depth, NULL);
1119 switch (folder_depth) {
1120 case 0:
1121 /* artist - album - number - title */
1122 file = g_strdup_printf ("%s - %s - %s - %s%s",
1123 artist, album, number, title, ext);
1124 break;
1125
1126 case 1:
1127 /* artist - album/number - title */
1128 file = g_strdup_printf ("%s - %s" G_DIR_SEPARATOR_S "%s - %s%s",
1129 artist, album, number, title, ext);
1130 break;
1131
1132 default: /* use this for players that don't care */
1133 case 2:
1134 /* artist/album/number - title */
1135 file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s" G_DIR_SEPARATOR_S "%s - %s%s",
1136 artist, album, number, title, ext);
1137 break;
1138 }
1139 g_free (number);
1140 }
1141
1142 g_free (artist);
1143 g_free (album);
1144 g_free (title);
1145 g_free (ext);
1146
1147 if (file == NULL)
1148 return NULL;
1149
1150 g_object_get (priv->device_info, "audio-folders", &audio_folders, NULL);
1151 if (audio_folders != NULL && g_strv_length (audio_folders) > 0) {
1152 folders = g_strdup (audio_folders[0]);
1153 } else {
1154 folders = "";
1155 }
1156 g_strfreev (audio_folders);
1157
1158 mount_path = rb_generic_player_source_get_mount_path (RB_GENERIC_PLAYER_SOURCE (target));
1159 path = g_build_filename (mount_path, folders, file, NULL);
1160 g_free (file);
1161 g_free (mount_path);
1162
1163 /* TODO: check for duplicates, or just overwrite by default? */
1164 rb_debug ("dest file is %s", path);
1165 return path;
1166 }
1167
1168 static gboolean
1169 strv_contains (char **strv, const char *s)
1170 {
1171 int i;
1172 for (i = 0; strv[i] != NULL; i++) {
1173 if (g_str_equal (strv[i], s))
1174 return TRUE;
1175 }
1176 return FALSE;
1177 }
1178
1179 void
1180 rb_generic_player_source_set_supported_formats (RBGenericPlayerSource *source, TotemPlParser *parser)
1181 {
1182 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
1183 char **playlist_formats;
1184 const char *check[] = { "audio/x-mpegurl", "audio/x-scpls", "audio/x-iriver-pla" };
1185
1186 g_object_get (priv->device_info, "playlist-formats", &playlist_formats, NULL);
1187 if (playlist_formats != NULL && g_strv_length (playlist_formats) > 0) {
1188 int i;
1189 for (i = 0; i < G_N_ELEMENTS (check); i++) {
1190 if (strv_contains (playlist_formats, check[i]) == FALSE) {
1191 totem_pl_parser_add_ignored_mimetype (parser, check[i]);
1192 }
1193 }
1194 }
1195 g_strfreev (playlist_formats);
1196
1197 totem_pl_parser_add_ignored_mimetype (parser, "x-directory/normal");
1198 }
1199
1200 TotemPlParserType
1201 rb_generic_player_source_get_playlist_format (RBGenericPlayerSource *source)
1202 {
1203 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
1204 TotemPlParserType result;
1205 char **playlist_formats;
1206
1207 g_object_get (priv->device_info, "playlist-formats", &playlist_formats, NULL);
1208
1209 if (playlist_formats == NULL || g_strv_length (playlist_formats) == 0 || strv_contains (playlist_formats, "audio/x-scpls")) {
1210 result = TOTEM_PL_PARSER_PLS;
1211 } else if (strv_contains (playlist_formats, "audio/x-mpegurl")) {
1212 result = TOTEM_PL_PARSER_M3U_DOS;
1213 } else if (strv_contains (playlist_formats, "audio/x-iriver-pla")) {
1214 result = TOTEM_PL_PARSER_IRIVER_PLA;
1215 } else {
1216 /* now what? */
1217 result = TOTEM_PL_PARSER_PLS;
1218 }
1219
1220 g_strfreev (playlist_formats);
1221 return result;
1222 }
1223
1224 char *
1225 rb_generic_player_source_get_playlist_path (RBGenericPlayerSource *source)
1226 {
1227 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
1228 char *path;
1229
1230 g_object_get (priv->device_info, "playlist-path", &path, NULL);
1231 if (g_str_has_suffix (path, "%File")) {
1232 path[strlen (path) - strlen("%File")] = '\0';
1233 }
1234 return path;
1235 }
1236
1237 static guint64
1238 get_fs_property (RBGenericPlayerSource *source, const char *attr)
1239 {
1240 char *mountpoint;
1241 GFile *root;
1242 GFileInfo *info;
1243 guint64 value = 0;
1244
1245 mountpoint = rb_generic_player_source_get_mount_path (source);
1246 root = g_file_new_for_uri (mountpoint);
1247 g_free (mountpoint);
1248
1249 info = g_file_query_filesystem_info (root, attr, NULL, NULL);
1250 g_object_unref (root);
1251 if (info != NULL) {
1252 if (g_file_info_has_attribute (info, attr)) {
1253 value = g_file_info_get_attribute_uint64 (info, attr);
1254 }
1255 g_object_unref (info);
1256 }
1257 return value;
1258 }
1259 static guint64
1260 impl_get_capacity (RBMediaPlayerSource *source)
1261 {
1262 return get_fs_property (RB_GENERIC_PLAYER_SOURCE (source), G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
1263 }
1264
1265 static guint64
1266 impl_get_free_space (RBMediaPlayerSource *source)
1267 {
1268 return get_fs_property (RB_GENERIC_PLAYER_SOURCE (source), G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
1269 }
1270
1271 static void
1272 impl_get_entries (RBMediaPlayerSource *source,
1273 const char *category,
1274 GHashTable *map)
1275 {
1276 RhythmDBQueryModel *model;
1277 GtkTreeIter iter;
1278 gboolean podcast;
1279
1280 /* we don't have anything else to distinguish podcasts from regular
1281 * tracks, so just use the genre.
1282 */
1283 podcast = (g_str_equal (category, SYNC_CATEGORY_PODCAST));
1284
1285 g_object_get (source, "base-query-model", &model, NULL);
1286 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter) == FALSE) {
1287 g_object_unref (model);
1288 return;
1289 }
1290
1291 do {
1292 RhythmDBEntry *entry;
1293 const char *genre;
1294 entry = rhythmdb_query_model_iter_to_entry (model, &iter);
1295 genre = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE);
1296 if (g_str_equal (genre, "Podcast") == podcast) {
1297 _rb_media_player_source_add_to_map (map, entry);
1298 }
1299 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter));
1300
1301 g_object_unref (model);
1302 }
1303
1304 static void
1305 impl_delete_entries (RBMediaPlayerSource *source,
1306 GList *entries,
1307 RBMediaPlayerSourceDeleteCallback callback,
1308 gpointer callback_data,
1309 GDestroyNotify destroy_data)
1310 {
1311 rb_generic_player_source_delete_entries (RB_GENERIC_PLAYER_SOURCE (source), entries);
1312
1313 if (callback) {
1314 callback (source, callback_data);
1315 }
1316 if (destroy_data) {
1317 destroy_data (callback_data);
1318 }
1319 }
1320
1321
1322 static void
1323 impl_show_properties (RBMediaPlayerSource *source, GtkWidget *info_box, GtkWidget *notebook)
1324 {
1325 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
1326 RhythmDBQueryModel *model;
1327 GtkBuilder *builder;
1328 GtkWidget *widget;
1329 GString *str;
1330 char *device_name;
1331 char *builder_file;
1332 char *vendor_name;
1333 char *model_name;
1334 char *serial_id;
1335 GObject *plugin;
1336 char *text;
1337 GList *output_formats;
1338 GList *t;
1339
1340 g_object_get (source, "plugin", &plugin, NULL);
1341 builder_file = rb_find_plugin_data_file (plugin, "generic-player-info.ui");
1342 g_object_unref (plugin);
1343
1344 if (builder_file == NULL) {
1345 g_warning ("Couldn't find generic-player-info.ui");
1346 return;
1347 }
1348
1349 builder = rb_builder_load (builder_file, NULL);
1350 g_free (builder_file);
1351
1352 if (builder == NULL) {
1353 rb_debug ("Couldn't load generic-player-info.ui");
1354 return;
1355 }
1356
1357 /* 'basic' tab stuff */
1358
1359 widget = GTK_WIDGET (gtk_builder_get_object (builder, "generic-player-basic-info"));
1360 gtk_box_pack_start (GTK_BOX (info_box), widget, TRUE, TRUE, 0);
1361
1362 widget = GTK_WIDGET (gtk_builder_get_object (builder, "entry-device-name"));
1363 g_object_get (source, "name", &device_name, NULL);
1364 gtk_entry_set_text (GTK_ENTRY (widget), device_name);
1365 g_free (device_name);
1366 /* don't think we can support this..
1367 g_signal_connect (widget, "focus-out-event",
1368 (GCallback)rb_mtp_source_name_changed_cb, source);
1369 */
1370
1371 g_object_get (source, "base-query-model", &model, NULL);
1372 widget = GTK_WIDGET (gtk_builder_get_object (builder, "num-tracks"));
1373 text = g_strdup_printf ("%d", gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL));
1374 gtk_label_set_text (GTK_LABEL (widget), text);
1375 g_free (text);
1376 g_object_unref (model);
1377
1378 widget = GTK_WIDGET (gtk_builder_get_object (builder, "num-playlists"));
1379 text = g_strdup_printf ("%d", g_list_length (priv->playlists));
1380 gtk_label_set_text (GTK_LABEL (widget), text);
1381 g_free (text);
1382
1383 /* 'advanced' tab stuff */
1384 widget = GTK_WIDGET (gtk_builder_get_object (builder, "generic-player-advanced-tab"));
1385 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), widget, gtk_label_new (_("Advanced")));
1386
1387 g_object_get (priv->device_info,
1388 "model", &model_name,
1389 "vendor", &vendor_name,
1390 "serial", &serial_id,
1391 NULL);
1392
1393 widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-model-value"));
1394 gtk_label_set_text (GTK_LABEL (widget), model_name);
1395
1396 widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-manufacturer-value"));
1397 gtk_label_set_text (GTK_LABEL (widget), vendor_name);
1398
1399 widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-serial-number-value"));
1400 gtk_label_set_text (GTK_LABEL (widget), serial_id);
1401
1402 g_free (model_name);
1403 g_free (vendor_name);
1404 g_free (serial_id);
1405
1406 str = g_string_new ("");
1407 output_formats = rb_transfer_target_get_format_descriptions (RB_TRANSFER_TARGET (source));
1408 for (t = output_formats; t != NULL; t = t->next) {
1409 if (t != output_formats) {
1410 g_string_append (str, "\n");
1411 }
1412 g_string_append (str, t->data);
1413 }
1414 rb_list_deep_free (output_formats);
1415
1416 widget = GTK_WIDGET (gtk_builder_get_object (builder, "audio-format-list"));
1417 gtk_label_set_text (GTK_LABEL (widget), str->str);
1418 g_string_free (str, TRUE);
1419
1420 g_object_unref (builder);
1421 }
1422
1423 static void
1424 impl_add_playlist (RBMediaPlayerSource *source, char *name, GList *entries)
1425 {
1426 RBSource *playlist;
1427 RhythmDBEntryType *entry_type;
1428 RBShell *shell;
1429 GList *i;
1430
1431 g_object_get (source,
1432 "shell", &shell,
1433 "entry-type", &entry_type,
1434 NULL);
1435
1436 playlist = rb_generic_player_playlist_source_new (shell, RB_GENERIC_PLAYER_SOURCE (source), NULL, NULL, entry_type);
1437 g_object_unref (entry_type);
1438
1439 rb_generic_player_source_add_playlist (RB_GENERIC_PLAYER_SOURCE (source),
1440 shell,
1441 playlist);
1442 g_object_set (playlist, "name", name, NULL);
1443
1444 for (i = entries; i != NULL; i = i->next) {
1445 rb_static_playlist_source_add_entry (RB_STATIC_PLAYLIST_SOURCE (playlist),
1446 i->data,
1447 -1);
1448 }
1449
1450 g_object_unref (shell);
1451 }
1452
1453 static void
1454 impl_remove_playlists (RBMediaPlayerSource *source)
1455 {
1456 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
1457 GList *playlists;
1458 GList *t;
1459
1460 playlists = g_list_copy (priv->playlists);
1461 for (t = playlists; t != NULL; t = t->next) {
1462 RBGenericPlayerPlaylistSource *p = RB_GENERIC_PLAYER_PLAYLIST_SOURCE (t->data);
1463 rb_generic_player_playlist_delete_from_player (p);
1464 rb_display_page_delete_thyself (RB_DISPLAY_PAGE (p));
1465 }
1466
1467 g_list_free (playlists);
1468 }
1469
1470 void
1471 _rb_generic_player_source_register_type (GTypeModule *module)
1472 {
1473 rb_generic_player_source_register_type (module);
1474 }