1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of local file source object
5 * Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
6 * Copyright (C) 2003,2004 Colin Walters <walters@verbum.org>
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 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <glib/gi18n.h>
30 #include <glib-object.h>
32 #ifdef ENABLE_TRACK_TRANSFER
33 #include <profiles/gnome-media-profiles.h>
34 #include <profiles/audio-profile-choose.h>
39 #include "rb-dialog.h"
40 #include "rb-glade-helpers.h"
41 #include "rb-file-helpers.h"
43 #include "eel-gconf-extensions.h"
44 #include "rb-library-source.h"
45 #include "rb-removable-media-manager.h"
46 #include "rb-auto-playlist-source.h"
48 static void rb_library_source_class_init (RBLibrarySourceClass
*klass
);
49 static void rb_library_source_init (RBLibrarySource
*source
);
50 static GObject
*rb_library_source_constructor (GType type
,
51 guint n_construct_properties
,
52 GObjectConstructParam
*construct_properties
);
53 static void rb_library_source_dispose (GObject
*object
);
54 static void rb_library_source_finalize (GObject
*object
);
56 /* RBSource implementations */
57 static gboolean
impl_show_popup (RBSource
*source
);
58 static GtkWidget
*impl_get_config_widget (RBSource
*source
, RBShellPreferences
*prefs
);
59 static char *impl_get_browser_key (RBSource
*source
);
60 static const char *impl_get_paned_key (RBBrowserSource
*source
);
61 static gboolean
impl_receive_drag (RBSource
*source
, GtkSelectionData
*data
);
62 static gboolean
impl_can_paste (RBSource
*asource
);
63 #ifdef ENABLE_TRACK_TRANSFER
64 static void impl_paste (RBSource
*source
, GList
*entries
);
66 static guint
impl_want_uri (RBSource
*source
, const char *uri
);
67 static gboolean
impl_add_uri (RBSource
*source
, const char *uri
, const char *title
, const char *genre
);
69 static void rb_library_source_ui_prefs_sync (RBLibrarySource
*source
);
70 static void rb_library_source_preferences_sync (RBLibrarySource
*source
);
72 static void rb_library_source_library_location_changed (GConfClient
*client
,
75 RBLibrarySource
*source
);
76 #ifdef ENABLE_TRACK_TRANSFER
77 static void rb_library_source_layout_path_changed (GConfClient
*client
,
80 RBLibrarySource
*source
);
81 static void rb_library_source_layout_filename_changed (GConfClient
*client
,
84 RBLibrarySource
*source
);
85 static void rb_library_source_edit_profile_clicked_cb (GtkButton
*button
,
86 RBLibrarySource
*source
);
88 static void rb_library_source_ui_pref_changed (GConfClient
*client
,
91 RBLibrarySource
*source
);
92 static gboolean
rb_library_source_library_location_cb (GtkEntry
*entry
,
94 RBLibrarySource
*source
);
95 static void rb_library_source_watch_toggled_cb (GtkToggleButton
*button
,
96 RBLibrarySource
*source
);
97 static void rb_library_source_sync_child_sources (RBLibrarySource
*source
);
98 #ifdef ENABLE_TRACK_TRANSFER
99 static void rb_library_source_path_changed_cb (GtkComboBox
*box
,
100 RBLibrarySource
*source
);
101 static void rb_library_source_filename_changed_cb (GtkComboBox
*box
,
102 RBLibrarySource
*source
);
103 static void rb_library_source_format_changed_cb (GtkWidget
*widget
,
104 RBLibrarySource
*source
);
107 #define CONF_UI_LIBRARY_DIR CONF_PREFIX "/ui/library"
108 #define CONF_STATE_LIBRARY_DIR CONF_PREFIX "/state/library"
109 #define CONF_STATE_LIBRARY_SORTING CONF_PREFIX "/state/library/sorting"
110 #define CONF_STATE_PANED_POSITION CONF_PREFIX "/state/library/paned_position"
111 #define CONF_STATE_SHOW_BROWSER CONF_PREFIX "/state/library/show_browser"
113 #ifdef ENABLE_TRACK_TRANSFER
117 } LibraryPathElement
;
119 const LibraryPathElement library_layout_paths
[] = {
120 {N_("Artist/Artist - Album"), "%aa/%aa - %at"},
121 {N_("Artist/Album"), "%aa/%at"},
122 {N_("Artist - Album"), "%aa - %at"},
123 {N_("Album"), "%at"},
124 {N_("Artist"), "%aa"},
126 const int num_library_layout_paths
= G_N_ELEMENTS (library_layout_paths
);
128 const LibraryPathElement library_layout_filenames
[] = {
129 {N_("Number - Title"), "%tN - %tt"},
130 {N_("Artist - Title"), "%ta - %tt"},
131 {N_("Artist - Number - Title"), "%ta - %tN - %tt"},
132 {N_("Artist (Album) - Number - Title"), "%ta (%at) - %tN - %tt"},
133 {N_("Title"), "%tt"},
134 {N_("Number. Artist - Title"), "%tN. %ta - %tt"},
136 const int num_library_layout_filenames
= G_N_ELEMENTS (library_layout_filenames
);
139 struct RBLibrarySourcePrivate
143 gboolean loading_prefs
;
144 RBShellPreferences
*shell_prefs
;
146 GtkWidget
*config_widget
;
148 GList
*child_sources
;
150 GtkWidget
*library_location_entry
;
151 GtkWidget
*watch_library_check
;
152 #ifdef ENABLE_TRACK_TRANSFER
153 GtkWidget
*layout_path_menu
;
154 GtkWidget
*layout_filename_menu
;
155 GtkWidget
*preferred_format_menu
;
156 GtkWidget
*layout_example_label
;
159 guint library_location_notify_id
;
160 guint ui_dir_notify_id
;
161 #ifdef ENABLE_TRACK_TRANSFER
162 guint layout_path_notify_id
;
163 guint layout_filename_notify_id
;
167 #define RB_LIBRARY_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_LIBRARY_SOURCE, RBLibrarySourcePrivate))
168 G_DEFINE_TYPE (RBLibrarySource
, rb_library_source
, RB_TYPE_BROWSER_SOURCE
)
171 rb_library_source_class_init (RBLibrarySourceClass
*klass
)
173 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
174 RBSourceClass
*source_class
= RB_SOURCE_CLASS (klass
);
175 RBBrowserSourceClass
*browser_source_class
= RB_BROWSER_SOURCE_CLASS (klass
);
177 object_class
->dispose
= rb_library_source_dispose
;
178 object_class
->finalize
= rb_library_source_finalize
;
179 object_class
->constructor
= rb_library_source_constructor
;
181 source_class
->impl_show_popup
= impl_show_popup
;
182 source_class
->impl_get_config_widget
= impl_get_config_widget
;
183 source_class
->impl_get_browser_key
= impl_get_browser_key
;
184 source_class
->impl_receive_drag
= impl_receive_drag
;
185 source_class
->impl_can_copy
= (RBSourceFeatureFunc
) rb_true_function
;
186 source_class
->impl_can_paste
= (RBSourceFeatureFunc
) impl_can_paste
;
187 #ifdef ENABLE_TRACK_TRANSFER
188 source_class
->impl_paste
= impl_paste
;
190 source_class
->impl_want_uri
= impl_want_uri
;
191 source_class
->impl_add_uri
= impl_add_uri
;
193 browser_source_class
->impl_get_paned_key
= impl_get_paned_key
;
194 browser_source_class
->impl_has_drop_support
= (RBBrowserSourceFeatureFunc
) rb_true_function
;
196 g_type_class_add_private (klass
, sizeof (RBLibrarySourcePrivate
));
198 #ifdef ENABLE_TRACK_TRANSFER
199 gnome_media_profiles_init (eel_gconf_client_get_global ());
204 rb_library_source_init (RBLibrarySource
*source
)
206 source
->priv
= RB_LIBRARY_SOURCE_GET_PRIVATE (source
);
210 rb_library_source_dispose (GObject
*object
)
212 RBLibrarySource
*source
;
213 source
= RB_LIBRARY_SOURCE (object
);
215 if (source
->priv
->shell_prefs
) {
216 g_object_unref (source
->priv
->shell_prefs
);
217 source
->priv
->shell_prefs
= NULL
;
220 if (source
->priv
->db
) {
221 g_object_unref (source
->priv
->db
);
222 source
->priv
->db
= NULL
;
225 G_OBJECT_CLASS (rb_library_source_parent_class
)->dispose (object
);
229 rb_library_source_finalize (GObject
*object
)
231 RBLibrarySource
*source
;
233 g_return_if_fail (object
!= NULL
);
234 g_return_if_fail (RB_IS_LIBRARY_SOURCE (object
));
236 source
= RB_LIBRARY_SOURCE (object
);
238 g_return_if_fail (source
->priv
!= NULL
);
240 rb_debug ("finalizing library source");
241 eel_gconf_notification_remove (source
->priv
->ui_dir_notify_id
);
242 eel_gconf_notification_remove (source
->priv
->library_location_notify_id
);
243 #ifdef ENABLE_TRACK_TRANSFER
244 eel_gconf_notification_remove (source
->priv
->layout_path_notify_id
);
245 eel_gconf_notification_remove (source
->priv
->layout_filename_notify_id
);
248 G_OBJECT_CLASS (rb_library_source_parent_class
)->finalize (object
);
252 add_child_sources_idle (RBLibrarySource
*source
)
254 rb_library_source_sync_child_sources (source
);
260 rb_library_source_constructor (GType type
,
261 guint n_construct_properties
,
262 GObjectConstructParam
*construct_properties
)
264 RBLibrarySource
*source
;
268 source
= RB_LIBRARY_SOURCE (G_OBJECT_CLASS (rb_library_source_parent_class
)
269 ->constructor (type
, n_construct_properties
, construct_properties
));
271 g_object_get (source
, "shell", &shell
, NULL
);
272 g_object_get (shell
, "db", &source
->priv
->db
, NULL
);
274 rb_library_source_ui_prefs_sync (source
);
276 source
->priv
->library_location_notify_id
=
277 eel_gconf_notification_add (CONF_LIBRARY_LOCATION
,
278 (GConfClientNotifyFunc
) rb_library_source_library_location_changed
, source
);
280 source
->priv
->ui_dir_notify_id
=
281 eel_gconf_notification_add (CONF_UI_LIBRARY_DIR
,
282 (GConfClientNotifyFunc
) rb_library_source_ui_pref_changed
, source
);
284 songs
= rb_source_get_entry_view (RB_SOURCE (source
));
286 rb_entry_view_append_column (songs
, RB_ENTRY_VIEW_COL_RATING
, FALSE
);
287 rb_entry_view_append_column (songs
, RB_ENTRY_VIEW_COL_LAST_PLAYED
, FALSE
);
288 rb_entry_view_append_column (songs
, RB_ENTRY_VIEW_COL_FIRST_SEEN
, FALSE
);
290 g_idle_add ((GSourceFunc
)add_child_sources_idle
, source
);
292 g_object_unref (shell
);
294 return G_OBJECT (source
);
298 rb_library_source_new (RBShell
*shell
)
304 gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR
, &size
, NULL
);
305 icon
= gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
306 "stock_music-library",
309 source
= RB_SOURCE (g_object_new (RB_TYPE_LIBRARY_SOURCE
,
310 "name", _("Library"),
311 "entry-type", RHYTHMDB_ENTRY_TYPE_SONG
,
312 "sorting-key", CONF_STATE_LIBRARY_SORTING
,
317 g_object_unref (icon
);
320 rb_shell_register_entry_type_for_source (shell
, source
,
321 RHYTHMDB_ENTRY_TYPE_SONG
);
326 #ifdef ENABLE_TRACK_TRANSFER
328 rb_library_source_edit_profile_clicked_cb (GtkButton
*button
, RBLibrarySource
*source
)
332 dialog
= gm_audio_profiles_edit_new (eel_gconf_client_get_global (),
333 GTK_WINDOW (source
->priv
->shell_prefs
));
334 gtk_dialog_set_has_separator (GTK_DIALOG (dialog
), FALSE
);
335 gtk_widget_show_all (dialog
);
336 gtk_dialog_run (GTK_DIALOG (dialog
));
341 rb_library_source_location_button_clicked_cb (GtkButton
*button
, RBLibrarySource
*source
)
345 dialog
= rb_file_chooser_new (_("Choose Library Location"), GTK_WINDOW (source
->priv
->shell_prefs
),
346 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
, FALSE
);
347 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
) {
351 uri
= gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog
));
353 uri
= gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog
));
356 path
= gnome_vfs_format_uri_for_display (uri
);
358 gtk_entry_set_text (GTK_ENTRY (source
->priv
->library_location_entry
), path
);
359 rb_library_source_library_location_cb (GTK_ENTRY (source
->priv
->library_location_entry
),
365 gtk_widget_destroy (GTK_WIDGET (dialog
));
369 impl_get_config_widget (RBSource
*asource
, RBShellPreferences
*prefs
)
371 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
374 #ifdef ENABLE_TRACK_TRANSFER
379 if (source
->priv
->config_widget
)
380 return source
->priv
->config_widget
;
382 g_object_ref (G_OBJECT (prefs
));
383 source
->priv
->shell_prefs
= prefs
;
385 xml
= rb_glade_xml_new ("library-prefs.glade", "library_vbox", source
);
386 source
->priv
->config_widget
=
387 glade_xml_get_widget (xml
, "library_vbox");
389 rb_glade_boldify_label (xml
, "library_location_label");
391 source
->priv
->library_location_entry
= glade_xml_get_widget (xml
, "library_location_entry");
392 tmp
= glade_xml_get_widget (xml
, "library_location_button");
393 g_signal_connect (G_OBJECT (tmp
),
395 G_CALLBACK (rb_library_source_location_button_clicked_cb
),
397 g_signal_connect (G_OBJECT (source
->priv
->library_location_entry
),
399 G_CALLBACK (rb_library_source_library_location_cb
),
402 source
->priv
->watch_library_check
= glade_xml_get_widget (xml
, "watch_library_check");
403 g_signal_connect (G_OBJECT (source
->priv
->watch_library_check
),
405 G_CALLBACK (rb_library_source_watch_toggled_cb
),
408 #ifdef ENABLE_TRACK_TRANSFER
409 rb_glade_boldify_label (xml
, "library_structure_label");
411 tmp
= glade_xml_get_widget (xml
, "layout_path_menu_box");
412 label
= glade_xml_get_widget (xml
, "layout_path_menu_label");
413 source
->priv
->layout_path_menu
= gtk_combo_box_new_text ();
414 gtk_box_pack_start_defaults (GTK_BOX (tmp
), source
->priv
->layout_path_menu
);
415 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), source
->priv
->layout_path_menu
);
416 g_signal_connect (G_OBJECT (source
->priv
->layout_path_menu
),
418 G_CALLBACK (rb_library_source_path_changed_cb
),
420 for (i
= 0; i
< num_library_layout_paths
; i
++) {
421 gtk_combo_box_append_text (GTK_COMBO_BOX (source
->priv
->layout_path_menu
),
422 _(library_layout_paths
[i
].title
));
425 tmp
= glade_xml_get_widget (xml
, "layout_filename_menu_box");
426 label
= glade_xml_get_widget (xml
, "layout_filename_menu_label");
427 source
->priv
->layout_filename_menu
= gtk_combo_box_new_text ();
428 gtk_box_pack_start_defaults (GTK_BOX (tmp
), source
->priv
->layout_filename_menu
);
429 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), source
->priv
->layout_filename_menu
);
430 g_signal_connect (G_OBJECT (source
->priv
->layout_filename_menu
),
432 G_CALLBACK (rb_library_source_filename_changed_cb
),
434 for (i
= 0; i
< num_library_layout_filenames
; i
++) {
435 gtk_combo_box_append_text (GTK_COMBO_BOX (source
->priv
->layout_filename_menu
),
436 _(library_layout_filenames
[i
].title
));
439 tmp
= glade_xml_get_widget (xml
, "edit_profile_button");
440 g_signal_connect (G_OBJECT (tmp
),
442 G_CALLBACK (rb_library_source_edit_profile_clicked_cb
),
445 tmp
= glade_xml_get_widget (xml
, "preferred_format_menu_box");
446 label
= glade_xml_get_widget (xml
, "preferred_format_menu_label");
447 source
->priv
->preferred_format_menu
= gm_audio_profile_choose_new ();
448 gtk_box_pack_start_defaults (GTK_BOX (tmp
), source
->priv
->preferred_format_menu
);
449 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), source
->priv
->preferred_format_menu
);
450 g_signal_connect (G_OBJECT (source
->priv
->preferred_format_menu
),
452 G_CALLBACK (rb_library_source_format_changed_cb
),
455 source
->priv
->layout_example_label
= glade_xml_get_widget (xml
, "layout_example_label");
457 tmp
= glade_xml_get_widget (xml
, "library_structure_vbox");
458 gtk_widget_set_no_show_all (tmp
, TRUE
);
459 gtk_widget_hide (tmp
);
462 g_object_unref (G_OBJECT (xml
));
464 rb_library_source_preferences_sync (source
);
466 return source
->priv
->config_widget
;
470 rb_library_source_library_location_changed (GConfClient
*client
,
473 RBLibrarySource
*source
)
475 if (source
->priv
->config_widget
)
476 rb_library_source_preferences_sync (source
);
478 rb_library_source_sync_child_sources (source
);
482 rb_library_source_ui_prefs_sync (RBLibrarySource
*source
)
484 if (source
->priv
->config_widget
)
485 rb_library_source_preferences_sync (source
);
489 rb_library_source_ui_pref_changed (GConfClient
*client
,
492 RBLibrarySource
*source
)
494 rb_debug ("ui pref changed");
495 rb_library_source_ui_prefs_sync (source
);
499 rb_library_source_preferences_sync (RBLibrarySource
*source
)
502 #ifdef ENABLE_TRACK_TRANSFER
504 GConfClient
*gconf_client
;
507 rb_debug ("syncing pref dialog state");
509 /* library location */
510 list
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
512 /* don't trigger the change notification */
513 g_signal_handlers_block_by_func (G_OBJECT (source
->priv
->library_location_entry
),
514 G_CALLBACK (rb_library_source_library_location_cb
),
517 if (g_slist_length (list
) == 1) {
520 gtk_widget_set_sensitive (source
->priv
->library_location_entry
, TRUE
);
522 path
= gnome_vfs_format_uri_for_display (list
->data
);
523 gtk_entry_set_text (GTK_ENTRY (source
->priv
->library_location_entry
), path
);
525 } else if (g_slist_length (list
) == 0) {
526 /* no library directories */
527 gtk_widget_set_sensitive (source
->priv
->library_location_entry
, TRUE
);
528 gtk_entry_set_text (GTK_ENTRY (source
->priv
->library_location_entry
), "");
530 /* multiple library directories */
531 gtk_widget_set_sensitive (source
->priv
->library_location_entry
, FALSE
);
532 gtk_entry_set_text (GTK_ENTRY (source
->priv
->library_location_entry
), _("Multiple locations set"));
535 g_signal_handlers_unblock_by_func (G_OBJECT (source
->priv
->library_location_entry
),
536 G_CALLBACK (rb_library_source_library_location_cb
),
539 g_slist_foreach (list
, (GFunc
) g_free
, NULL
);
543 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (source
->priv
->watch_library_check
),
544 eel_gconf_get_boolean (CONF_MONITOR_LIBRARY
));
546 #ifdef ENABLE_TRACK_TRANSFER
547 /* preferred format */
548 str
= eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT
);
550 gm_audio_profile_choose_set_active (source
->priv
->preferred_format_menu
, str
);
552 source
->priv
->layout_path_notify_id
=
553 eel_gconf_notification_add (CONF_LIBRARY_LAYOUT_PATH
,
554 (GConfClientNotifyFunc
) rb_library_source_layout_path_changed
, source
);
555 source
->priv
->layout_filename_notify_id
=
556 eel_gconf_notification_add (CONF_LIBRARY_LAYOUT_FILENAME
,
557 (GConfClientNotifyFunc
) rb_library_source_layout_filename_changed
, source
);
559 gconf_client
= eel_gconf_client_get_global ();
561 rb_library_source_layout_path_changed (gconf_client
, -1,
562 gconf_client_get_entry (gconf_client
, CONF_LIBRARY_LAYOUT_PATH
, NULL
, TRUE
, NULL
),
564 /* layout filename */
565 rb_library_source_layout_filename_changed (gconf_client
, -1,
566 gconf_client_get_entry (gconf_client
, CONF_LIBRARY_LAYOUT_FILENAME
, NULL
, TRUE
, NULL
),
572 rb_library_source_library_location_cb (GtkEntry
*entry
,
573 GdkEventFocus
*event
,
574 RBLibrarySource
*source
)
580 path
= gtk_entry_get_text (entry
);
581 uri
= gnome_vfs_make_uri_from_input (path
);
584 list
= g_slist_prepend (NULL
, (gpointer
)uri
);
586 eel_gconf_set_string_list (CONF_LIBRARY_LOCATION
, list
);
592 /* don't do the first-run druid if the user sets the library location */
594 eel_gconf_set_boolean (CONF_FIRST_TIME
, TRUE
);
600 rb_library_source_watch_toggled_cb (GtkToggleButton
*button
, RBLibrarySource
*source
)
604 active
= gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (source
->priv
->watch_library_check
));
605 eel_gconf_set_boolean (CONF_MONITOR_LIBRARY
, active
);
609 impl_get_browser_key (RBSource
*source
)
611 return g_strdup (CONF_STATE_SHOW_BROWSER
);
615 impl_get_paned_key (RBBrowserSource
*status
)
617 return CONF_STATE_PANED_POSITION
;
621 rb_library_source_add_location_entry_changed_cb (GtkEntry
*entry
,
624 gtk_widget_set_sensitive (target
, g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (entry
)), -1) > 0);
628 rb_library_source_add_location (RBLibrarySource
*source
, GtkWindow
*win
)
630 GladeXML
*xml
= rb_glade_xml_new ("uri.glade",
631 "open_uri_dialog_content",
633 GtkWidget
*content
, *uri_widget
, *open_button
;
634 GtkWidget
*dialog
= gtk_dialog_new_with_buttons (_("Add Location"),
636 GTK_DIALOG_MODAL
| GTK_DIALOG_NO_SEPARATOR
,
641 g_return_if_fail (dialog
!= NULL
);
643 open_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
646 gtk_widget_set_sensitive (open_button
, FALSE
);
648 gtk_dialog_set_default_response (GTK_DIALOG (dialog
),
650 gtk_container_set_border_width (GTK_CONTAINER (dialog
), 5);
651 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), 2);
653 gtk_window_set_resizable (GTK_WINDOW (dialog
), FALSE
);
655 content
= glade_xml_get_widget (xml
, "open_uri_dialog_content");
657 g_return_if_fail (content
!= NULL
);
659 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
),
660 content
, FALSE
, FALSE
, 0);
661 gtk_container_set_border_width (GTK_CONTAINER (content
), 5);
663 uri_widget
= glade_xml_get_widget (xml
, "uri");
665 g_return_if_fail (uri_widget
!= NULL
);
667 g_signal_connect_object (G_OBJECT (uri_widget
),
669 G_CALLBACK (rb_library_source_add_location_entry_changed_cb
),
672 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_OK
) {
673 char *uri
= gtk_editable_get_chars (GTK_EDITABLE (uri_widget
), 0, -1);
675 GnomeVFSURI
*vfsuri
= gnome_vfs_uri_new (uri
);
676 if (vfsuri
!= NULL
) {
677 rhythmdb_add_uri (source
->priv
->db
, uri
);
678 gnome_vfs_uri_unref (vfsuri
);
680 rb_debug ("invalid uri: \"%s\"", uri
);
685 gtk_widget_destroy (GTK_WIDGET (dialog
));
690 impl_receive_drag (RBSource
*asource
, GtkSelectionData
*data
)
692 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
694 GList
*entries
= NULL
;
696 rb_debug ("parsing uri list");
697 list
= rb_uri_list_parse ((const char *) data
->data
);
699 for (i
= list
; i
!= NULL
; i
= g_list_next (i
)) {
700 if (i
->data
!= NULL
) {
702 RhythmDBEntry
*entry
;
704 entry
= rhythmdb_entry_lookup_by_location (source
->priv
->db
, uri
);
707 /* add to the library */
708 rhythmdb_add_uri (source
->priv
->db
, uri
);
710 /* add to list of entries to copy */
711 entries
= g_list_prepend (entries
, entry
);
719 entries
= g_list_reverse (entries
);
720 if (rb_source_can_paste (asource
))
721 rb_source_paste (asource
, entries
);
722 g_list_free (entries
);
730 impl_show_popup (RBSource
*source
)
732 _rb_source_show_popup (source
, "/LibrarySourcePopup");
736 #ifdef ENABLE_TRACK_TRANSFER
738 rb_library_source_path_changed_cb (GtkComboBox
*box
, RBLibrarySource
*source
)
743 index
= gtk_combo_box_get_active (box
);
744 path
= (index
>= 0) ? library_layout_paths
[index
].path
: "";
745 eel_gconf_set_string (CONF_LIBRARY_LAYOUT_PATH
, path
);
749 rb_library_source_filename_changed_cb (GtkComboBox
*box
, RBLibrarySource
*source
)
751 const char *filename
;
754 index
= gtk_combo_box_get_active (box
);
755 filename
= (index
>= 0) ? library_layout_filenames
[index
].path
: "";
756 eel_gconf_set_string (CONF_LIBRARY_LAYOUT_FILENAME
, filename
);
760 rb_library_source_format_changed_cb (GtkWidget
*widget
, RBLibrarySource
*source
)
762 GMAudioProfile
*profile
;
764 profile
= gm_audio_profile_choose_get_active (widget
);
765 eel_gconf_set_string (CONF_LIBRARY_PREFERRED_FORMAT
, gm_audio_profile_get_id (profile
));
769 * Perform magic on a path to make it safe.
771 * This will always replace '/' with ' ', and optionally make the file name
772 * shell-friendly. This involves removing [?*\ ] and replacing with '_'. Also
773 * any leading periods are removed so that the files don't end up being hidden.
776 sanitize_path (const char *str
)
781 /* Skip leading periods, otherwise files disappear... */
786 /* Replace path seperators with a hyphen */
787 g_strdelimit (s
, "/", '-');
788 if (eel_gconf_get_boolean (CONF_LIBRARY_STRIP_CHARS
)) {
789 /* Replace separators with a hyphen */
790 g_strdelimit (s
, "\\:|", '-');
791 /* Replace all other weird characters to whitespace */
792 g_strdelimit (s
, "*?&!\'\"$()`>{}", ' ');
793 /* Replace all whitespace with underscores */
794 /* TODO: I'd like this to compress whitespace aswell */
795 g_strdelimit (s
, "\t ", '_');
797 res
= g_filename_from_utf8(s
, -1, NULL
, NULL
, NULL
);
799 return res
? res
: g_strdup(str
);
803 * Parse a filename pattern and replace markers with values from a TrackDetails
806 * Valid markers so far are:
808 * %aa -- album artist
809 * %aA -- album artist (lowercase)
810 * %as -- album artist sortname
811 * %aS -- album artist sortname (lowercase)
812 * %tn -- track number (i.e 8)
813 * %tN -- track number, zero padded (i.e 08)
815 * %ta -- track artist
816 * %tA -- track artist (lowercase)
817 * %ts -- track artist sortname
818 * %tS -- track artist sortname (lowercase)
821 filepath_parse_pattern (const char *pattern
,
822 RhythmDBEntry
*entry
)
824 /* p is the pattern iterator, i is a general purpose iterator */
829 if (pattern
== NULL
|| pattern
[0] == 0)
830 return g_strdup (" ");
832 s
= g_string_new (NULL
);
838 /* If not a % marker, copy and continue */
840 g_string_append_c (s
, *p
++);
841 /* Explicit increment as we continue past the increment */
845 /* Is a % marker, go to next and see what to do */
851 g_string_append_c (s
, '%');
859 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ALBUM
));
862 temp
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ALBUM_FOLDED
));
865 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ARTIST
));
868 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ARTIST_FOLDED
));
871 string = sanitize_path (album sort name);
874 char *t = g_utf8_strdown (album sort name)
875 string = sanitize_path (t);
879 string
= g_strdup_printf ("%%a%c", *p
);
890 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_TITLE
));
893 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_TITLE_FOLDED
));
896 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ARTIST
));
899 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ARTIST_FOLDED
));
902 string = sanitize_path (artist sort name);
905 char *t = g_utf8_strdown (artist sort name)
906 string = sanitize_path (t);
911 string
= g_strdup_printf ("%u", (guint
)rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_TRACK_NUMBER
));
914 /* Track number, zero-padded */
915 string
= g_strdup_printf ("%02u", (guint
)rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_TRACK_NUMBER
));
918 string
= g_strdup_printf ("%%t%c", *p
);
924 string
= g_strdup_printf ("%%%c", *p
);
928 g_string_append (s
, string
);
935 g_string_free (s
, FALSE
);
940 layout_example_label_update (RBLibrarySource
*source
)
948 GMAudioProfile
*profile
;
949 RhythmDBEntry
*sample_entry
;
951 profile
= gm_audio_profile_choose_get_active (source
->priv
->preferred_format_menu
);
953 /* TODO: sucky. Replace with get-gconf-key-with-default mojo */
954 file_pattern
= eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME
);
955 if (file_pattern
== NULL
) {
956 file_pattern
= g_strdup (library_layout_filenames
[0].path
);
958 path_pattern
= eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH
);
959 if (path_pattern
== NULL
) {
960 path_pattern
= g_strdup (library_layout_paths
[0].path
);
963 sample_entry
= rhythmdb_entry_example_new (source
->priv
->db
, RHYTHMDB_ENTRY_TYPE_SONG
, NULL
);
964 file_value
= filepath_parse_pattern (file_pattern
, sample_entry
);
965 path_value
= filepath_parse_pattern (path_pattern
, sample_entry
);
966 rhythmdb_entry_unref (sample_entry
);
968 example
= g_build_filename (G_DIR_SEPARATOR_S
, path_value
, file_value
, NULL
);
970 g_free (file_pattern
);
972 g_free (path_pattern
);
974 format
= g_strconcat ("<small><i><b>Example Path:</b> ",
977 profile
? gm_audio_profile_get_extension (profile
) : "ogg",
978 "</i></small>", NULL
);
981 gtk_label_set_markup (GTK_LABEL (source
->priv
->layout_example_label
), format
);
986 rb_library_source_layout_path_changed (GConfClient
*client
,
989 RBLibrarySource
*source
)
994 g_return_if_fail (strcmp (entry
->key
, CONF_LIBRARY_LAYOUT_PATH
) == 0);
996 rb_debug ("layout path changed");
998 if (entry
->value
== NULL
) {
999 value
= g_strdup (library_layout_paths
[0].path
);
1000 } else if (entry
->value
->type
== GCONF_VALUE_STRING
) {
1001 value
= g_strdup (gconf_value_get_string (entry
->value
));
1006 while (library_layout_paths
[i
].path
&& strcmp (library_layout_paths
[i
].path
, value
) != 0) {
1011 gtk_combo_box_set_active (GTK_COMBO_BOX (source
->priv
->layout_path_menu
), i
);
1013 layout_example_label_update (source
);
1017 rb_library_source_layout_filename_changed (GConfClient
*client
,
1020 RBLibrarySource
*source
)
1025 g_return_if_fail (strcmp (entry
->key
, CONF_LIBRARY_LAYOUT_FILENAME
) == 0);
1027 rb_debug ("layout filename changed");
1029 if (entry
->value
== NULL
) {
1030 value
= g_strdup (library_layout_filenames
[0].path
);
1031 } else if (entry
->value
->type
== GCONF_VALUE_STRING
) {
1032 value
= g_strdup (gconf_value_get_string (entry
->value
));
1037 while (library_layout_filenames
[i
].path
&& strcmp (library_layout_filenames
[i
].path
, value
) != 0) {
1042 gtk_combo_box_set_active (GTK_COMBO_BOX (source
->priv
->layout_filename_menu
), i
);
1044 layout_example_label_update (source
);
1048 * Build the absolute filename for the specified track.
1050 * The base path is the extern variable 'base_path', the format to use
1051 * is the extern variable 'file_pattern'. Free the result when you
1052 * have finished with it.
1054 * Stolen from Sound-Juicer
1057 build_filename (RBLibrarySource
*source
, RhythmDBEntry
*entry
)
1065 char *extension
= NULL
;
1067 const char *layout_path
;
1068 const char *layout_filename
;
1069 const char *preferred_format
;
1071 list
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
1072 layout_path
= eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH
);
1073 layout_filename
= eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME
);
1074 preferred_format
= eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT
);
1076 if (list
== NULL
|| layout_path
== NULL
|| layout_filename
== NULL
|| preferred_format
== NULL
) {
1078 rb_debug ("Could not retrieve settings from GConf");
1079 g_slist_free (list
);
1083 uri
= gnome_vfs_uri_new ((const char *)list
->data
);
1084 g_slist_free (list
);
1086 realpath
= filepath_parse_pattern (layout_path
, entry
);
1087 new = gnome_vfs_uri_append_path (uri
, realpath
);
1088 gnome_vfs_uri_unref (uri
);
1092 if (g_str_has_prefix (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_MIMETYPE
), "audio/x-raw")) {
1093 GMAudioProfile
*profile
;
1094 profile
= gm_audio_profile_lookup (preferred_format
);
1096 extension
= g_strdup (gm_audio_profile_get_extension (profile
));
1099 if (extension
== NULL
) {
1102 /* use the old extension. strip anything after a '?' for http/daap/etc */
1103 extension
= g_strdup (g_utf8_strrchr (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
), -1, '.') + 1);
1105 tmp
= g_utf8_strchr (extension
, -1, '?');
1110 realfile
= filepath_parse_pattern (layout_filename
, entry
);
1112 filename
= g_strdup_printf ("%s.%s", realfile
, extension
);
1115 filename
= realfile
;
1118 new = gnome_vfs_uri_append_file_name (uri
, filename
);
1119 gnome_vfs_uri_unref (uri
);
1124 string
= gnome_vfs_uri_to_string (uri
, 0);
1125 gnome_vfs_uri_unref (uri
);
1131 impl_can_paste (RBSource
*asource
)
1133 #ifdef ENABLE_TRACK_TRANSFER
1135 gboolean can_paste
= TRUE
;
1137 list
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
1138 can_paste
= ((list
!= NULL
) &&
1139 (eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH
) != NULL
) &&
1140 (eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME
) != NULL
) &&
1141 (eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT
) != NULL
));
1143 g_slist_free (list
);
1150 #ifdef ENABLE_TRACK_TRANSFER
1152 completed_cb (RhythmDBEntry
*entry
, const char *dest
, RBLibrarySource
*source
)
1154 rhythmdb_add_uri (source
->priv
->db
, dest
);
1158 impl_paste (RBSource
*asource
, GList
*entries
)
1160 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
1161 RBRemovableMediaManager
*rm_mgr
;
1166 sl
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
1168 (eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH
) == NULL
)||
1169 (eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME
) == NULL
) ||
1170 (eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT
) == NULL
)) {
1172 g_warning ("RBLibrarySource impl_paste called when gconf keys unset");
1176 g_object_get (source
, "shell", &shell
, NULL
);
1177 g_object_get (shell
, "removable-media-manager", &rm_mgr
, NULL
);
1178 g_object_unref (shell
);
1180 for (l
= entries
; l
!= NULL
; l
= g_list_next (l
)) {
1181 RhythmDBEntry
*entry
= (RhythmDBEntry
*)l
->data
;
1182 RhythmDBEntryType entry_type
;
1185 rb_debug ("pasting entry %s", rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
));
1187 entry_type
= rhythmdb_entry_get_entry_type (entry
);
1188 if (entry_type
== RHYTHMDB_ENTRY_TYPE_SONG
)
1189 /* copying to ourselves would be silly */
1192 /* see if the responsible source lets us copy */
1193 if (!rb_source_can_copy (rb_shell_get_source_by_entry_type (shell
, entry_type
)))
1196 dest
= build_filename (source
, entry
);
1198 rb_debug ("could not create destination path for entry");
1202 rb_removable_media_manager_queue_transfer (rm_mgr
, entry
,
1204 (RBTranferCompleteCallback
)completed_cb
, source
);
1207 g_object_unref (rm_mgr
);
1212 impl_want_uri (RBSource
*source
, const char *uri
)
1214 /* assume anything local or on smb is a song */
1215 if (rb_uri_is_local (uri
) ||
1216 g_str_has_prefix (uri
, "smb://"))
1223 impl_add_uri (RBSource
*asource
, const char *uri
, const char *title
, const char *genre
)
1225 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
1226 /* FIXME should be synchronous */
1227 rb_debug ("adding uri %s to library", uri
);
1228 rhythmdb_add_uri (source
->priv
->db
, uri
);
1233 rb_library_source_add_child_source (const char *path
, RBLibrarySource
*library_source
)
1242 g_object_get (library_source
, "shell", &shell
, NULL
);
1243 uri
= gnome_vfs_uri_new (path
);
1244 name
= gnome_vfs_uri_extract_short_name (uri
);
1245 gnome_vfs_uri_unref (uri
);
1247 source
= rb_auto_playlist_source_new (shell
, name
, FALSE
);
1248 query
= rhythmdb_query_parse (library_source
->priv
->db
,
1249 RHYTHMDB_QUERY_PROP_EQUALS
, RHYTHMDB_PROP_TYPE
, RHYTHMDB_ENTRY_TYPE_SONG
,
1250 RHYTHMDB_QUERY_PROP_PREFIX
, RHYTHMDB_PROP_LOCATION
, path
,
1251 RHYTHMDB_QUERY_END
);
1252 rb_auto_playlist_source_set_query (RB_AUTO_PLAYLIST_SOURCE (source
), query
,
1253 RHYTHMDB_QUERY_MODEL_LIMIT_NONE
, NULL
,
1255 rhythmdb_query_free (query
);
1257 g_object_get (library_source
, "icon", &icon
, NULL
);
1258 g_object_set (source
, "icon", icon
, NULL
);
1260 g_object_unref (icon
);
1263 rb_shell_append_source (shell
, source
, RB_SOURCE (library_source
));
1264 library_source
->priv
->child_sources
= g_list_prepend (library_source
->priv
->child_sources
, source
);
1266 g_object_unref (shell
);
1271 rb_library_source_sync_child_sources (RBLibrarySource
*source
)
1275 list
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
1277 /* FIXME: don't delete and re-create sources that are still there */
1278 g_list_foreach (source
->priv
->child_sources
, (GFunc
)rb_source_delete_thyself
, NULL
);
1279 g_list_free (source
->priv
->child_sources
);
1280 source
->priv
->child_sources
= NULL
;
1282 if (g_slist_length (list
) > 1)
1283 g_slist_foreach (list
, (GFunc
)rb_library_source_add_child_source
, source
);
1284 g_slist_foreach (list
, (GFunc
) g_free
, NULL
);
1285 g_slist_free (list
);