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 GDK_THREADS_ENTER ();
255 rb_library_source_sync_child_sources (source
);
256 GDK_THREADS_LEAVE ();
262 rb_library_source_constructor (GType type
,
263 guint n_construct_properties
,
264 GObjectConstructParam
*construct_properties
)
266 RBLibrarySource
*source
;
270 source
= RB_LIBRARY_SOURCE (G_OBJECT_CLASS (rb_library_source_parent_class
)
271 ->constructor (type
, n_construct_properties
, construct_properties
));
273 g_object_get (source
, "shell", &shell
, NULL
);
274 g_object_get (shell
, "db", &source
->priv
->db
, NULL
);
276 rb_library_source_ui_prefs_sync (source
);
278 source
->priv
->library_location_notify_id
=
279 eel_gconf_notification_add (CONF_LIBRARY_LOCATION
,
280 (GConfClientNotifyFunc
) rb_library_source_library_location_changed
, source
);
282 source
->priv
->ui_dir_notify_id
=
283 eel_gconf_notification_add (CONF_UI_LIBRARY_DIR
,
284 (GConfClientNotifyFunc
) rb_library_source_ui_pref_changed
, source
);
286 songs
= rb_source_get_entry_view (RB_SOURCE (source
));
288 rb_entry_view_append_column (songs
, RB_ENTRY_VIEW_COL_RATING
, FALSE
);
289 rb_entry_view_append_column (songs
, RB_ENTRY_VIEW_COL_LAST_PLAYED
, FALSE
);
290 rb_entry_view_append_column (songs
, RB_ENTRY_VIEW_COL_FIRST_SEEN
, FALSE
);
292 g_idle_add ((GSourceFunc
)add_child_sources_idle
, source
);
294 g_object_unref (shell
);
296 return G_OBJECT (source
);
300 rb_library_source_new (RBShell
*shell
)
305 RhythmDBEntryType entry_type
;
307 entry_type
= RHYTHMDB_ENTRY_TYPE_SONG
;
309 gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR
, &size
, NULL
);
310 icon
= gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
311 "stock_music-library",
314 source
= RB_SOURCE (g_object_new (RB_TYPE_LIBRARY_SOURCE
,
315 "name", _("Library"),
316 "entry-type", entry_type
,
317 "sorting-key", CONF_STATE_LIBRARY_SORTING
,
322 g_object_unref (icon
);
325 rb_shell_register_entry_type_for_source (shell
, source
, entry_type
);
330 #ifdef ENABLE_TRACK_TRANSFER
332 rb_library_source_edit_profile_clicked_cb (GtkButton
*button
, RBLibrarySource
*source
)
336 dialog
= gm_audio_profiles_edit_new (eel_gconf_client_get_global (),
337 GTK_WINDOW (source
->priv
->shell_prefs
));
338 gtk_dialog_set_has_separator (GTK_DIALOG (dialog
), FALSE
);
339 gtk_widget_show_all (dialog
);
340 gtk_dialog_run (GTK_DIALOG (dialog
));
345 rb_library_source_location_button_clicked_cb (GtkButton
*button
, RBLibrarySource
*source
)
349 dialog
= rb_file_chooser_new (_("Choose Library Location"), GTK_WINDOW (source
->priv
->shell_prefs
),
350 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
, FALSE
);
351 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
) {
355 uri
= gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog
));
357 uri
= gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog
));
360 path
= gnome_vfs_format_uri_for_display (uri
);
362 gtk_entry_set_text (GTK_ENTRY (source
->priv
->library_location_entry
), path
);
363 rb_library_source_library_location_cb (GTK_ENTRY (source
->priv
->library_location_entry
),
369 gtk_widget_destroy (GTK_WIDGET (dialog
));
373 impl_get_config_widget (RBSource
*asource
, RBShellPreferences
*prefs
)
375 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
378 #ifdef ENABLE_TRACK_TRANSFER
383 if (source
->priv
->config_widget
)
384 return source
->priv
->config_widget
;
386 g_object_ref (G_OBJECT (prefs
));
387 source
->priv
->shell_prefs
= prefs
;
389 xml
= rb_glade_xml_new ("library-prefs.glade", "library_vbox", source
);
390 source
->priv
->config_widget
=
391 glade_xml_get_widget (xml
, "library_vbox");
393 rb_glade_boldify_label (xml
, "library_location_label");
395 source
->priv
->library_location_entry
= glade_xml_get_widget (xml
, "library_location_entry");
396 tmp
= glade_xml_get_widget (xml
, "library_location_button");
397 g_signal_connect (G_OBJECT (tmp
),
399 G_CALLBACK (rb_library_source_location_button_clicked_cb
),
401 g_signal_connect (G_OBJECT (source
->priv
->library_location_entry
),
403 G_CALLBACK (rb_library_source_library_location_cb
),
406 source
->priv
->watch_library_check
= glade_xml_get_widget (xml
, "watch_library_check");
407 g_signal_connect (G_OBJECT (source
->priv
->watch_library_check
),
409 G_CALLBACK (rb_library_source_watch_toggled_cb
),
412 #ifdef ENABLE_TRACK_TRANSFER
413 rb_glade_boldify_label (xml
, "library_structure_label");
415 tmp
= glade_xml_get_widget (xml
, "layout_path_menu_box");
416 label
= glade_xml_get_widget (xml
, "layout_path_menu_label");
417 source
->priv
->layout_path_menu
= gtk_combo_box_new_text ();
418 gtk_box_pack_start_defaults (GTK_BOX (tmp
), source
->priv
->layout_path_menu
);
419 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), source
->priv
->layout_path_menu
);
420 g_signal_connect (G_OBJECT (source
->priv
->layout_path_menu
),
422 G_CALLBACK (rb_library_source_path_changed_cb
),
424 for (i
= 0; i
< num_library_layout_paths
; i
++) {
425 gtk_combo_box_append_text (GTK_COMBO_BOX (source
->priv
->layout_path_menu
),
426 _(library_layout_paths
[i
].title
));
429 tmp
= glade_xml_get_widget (xml
, "layout_filename_menu_box");
430 label
= glade_xml_get_widget (xml
, "layout_filename_menu_label");
431 source
->priv
->layout_filename_menu
= gtk_combo_box_new_text ();
432 gtk_box_pack_start_defaults (GTK_BOX (tmp
), source
->priv
->layout_filename_menu
);
433 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), source
->priv
->layout_filename_menu
);
434 g_signal_connect (G_OBJECT (source
->priv
->layout_filename_menu
),
436 G_CALLBACK (rb_library_source_filename_changed_cb
),
438 for (i
= 0; i
< num_library_layout_filenames
; i
++) {
439 gtk_combo_box_append_text (GTK_COMBO_BOX (source
->priv
->layout_filename_menu
),
440 _(library_layout_filenames
[i
].title
));
443 tmp
= glade_xml_get_widget (xml
, "edit_profile_button");
444 g_signal_connect (G_OBJECT (tmp
),
446 G_CALLBACK (rb_library_source_edit_profile_clicked_cb
),
449 tmp
= glade_xml_get_widget (xml
, "preferred_format_menu_box");
450 label
= glade_xml_get_widget (xml
, "preferred_format_menu_label");
451 source
->priv
->preferred_format_menu
= gm_audio_profile_choose_new ();
452 gtk_box_pack_start_defaults (GTK_BOX (tmp
), source
->priv
->preferred_format_menu
);
453 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), source
->priv
->preferred_format_menu
);
454 g_signal_connect (G_OBJECT (source
->priv
->preferred_format_menu
),
456 G_CALLBACK (rb_library_source_format_changed_cb
),
459 source
->priv
->layout_example_label
= glade_xml_get_widget (xml
, "layout_example_label");
461 tmp
= glade_xml_get_widget (xml
, "library_structure_vbox");
462 gtk_widget_set_no_show_all (tmp
, TRUE
);
463 gtk_widget_hide (tmp
);
466 g_object_unref (G_OBJECT (xml
));
468 rb_library_source_preferences_sync (source
);
470 return source
->priv
->config_widget
;
474 rb_library_source_library_location_changed (GConfClient
*client
,
477 RBLibrarySource
*source
)
479 if (source
->priv
->config_widget
)
480 rb_library_source_preferences_sync (source
);
482 rb_library_source_sync_child_sources (source
);
486 rb_library_source_ui_prefs_sync (RBLibrarySource
*source
)
488 if (source
->priv
->config_widget
)
489 rb_library_source_preferences_sync (source
);
493 rb_library_source_ui_pref_changed (GConfClient
*client
,
496 RBLibrarySource
*source
)
498 rb_debug ("ui pref changed");
499 rb_library_source_ui_prefs_sync (source
);
503 rb_library_source_preferences_sync (RBLibrarySource
*source
)
506 #ifdef ENABLE_TRACK_TRANSFER
508 GConfClient
*gconf_client
;
511 rb_debug ("syncing pref dialog state");
513 /* library location */
514 list
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
516 /* don't trigger the change notification */
517 g_signal_handlers_block_by_func (G_OBJECT (source
->priv
->library_location_entry
),
518 G_CALLBACK (rb_library_source_library_location_cb
),
521 if (g_slist_length (list
) == 1) {
524 gtk_widget_set_sensitive (source
->priv
->library_location_entry
, TRUE
);
526 path
= gnome_vfs_format_uri_for_display (list
->data
);
527 gtk_entry_set_text (GTK_ENTRY (source
->priv
->library_location_entry
), path
);
529 } else if (g_slist_length (list
) == 0) {
530 /* no library directories */
531 gtk_widget_set_sensitive (source
->priv
->library_location_entry
, TRUE
);
532 gtk_entry_set_text (GTK_ENTRY (source
->priv
->library_location_entry
), "");
534 /* multiple library directories */
535 gtk_widget_set_sensitive (source
->priv
->library_location_entry
, FALSE
);
536 gtk_entry_set_text (GTK_ENTRY (source
->priv
->library_location_entry
), _("Multiple locations set"));
539 g_signal_handlers_unblock_by_func (G_OBJECT (source
->priv
->library_location_entry
),
540 G_CALLBACK (rb_library_source_library_location_cb
),
543 g_slist_foreach (list
, (GFunc
) g_free
, NULL
);
547 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (source
->priv
->watch_library_check
),
548 eel_gconf_get_boolean (CONF_MONITOR_LIBRARY
));
550 #ifdef ENABLE_TRACK_TRANSFER
551 /* preferred format */
552 str
= eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT
);
554 gm_audio_profile_choose_set_active (source
->priv
->preferred_format_menu
, str
);
556 source
->priv
->layout_path_notify_id
=
557 eel_gconf_notification_add (CONF_LIBRARY_LAYOUT_PATH
,
558 (GConfClientNotifyFunc
) rb_library_source_layout_path_changed
, source
);
559 source
->priv
->layout_filename_notify_id
=
560 eel_gconf_notification_add (CONF_LIBRARY_LAYOUT_FILENAME
,
561 (GConfClientNotifyFunc
) rb_library_source_layout_filename_changed
, source
);
563 gconf_client
= eel_gconf_client_get_global ();
565 rb_library_source_layout_path_changed (gconf_client
, -1,
566 gconf_client_get_entry (gconf_client
, CONF_LIBRARY_LAYOUT_PATH
, NULL
, TRUE
, NULL
),
568 /* layout filename */
569 rb_library_source_layout_filename_changed (gconf_client
, -1,
570 gconf_client_get_entry (gconf_client
, CONF_LIBRARY_LAYOUT_FILENAME
, NULL
, TRUE
, NULL
),
576 rb_library_source_library_location_cb (GtkEntry
*entry
,
577 GdkEventFocus
*event
,
578 RBLibrarySource
*source
)
584 path
= gtk_entry_get_text (entry
);
585 uri
= gnome_vfs_make_uri_from_input (path
);
588 list
= g_slist_prepend (NULL
, (gpointer
)uri
);
590 eel_gconf_set_string_list (CONF_LIBRARY_LOCATION
, list
);
596 /* don't do the first-run druid if the user sets the library location */
598 eel_gconf_set_boolean (CONF_FIRST_TIME
, TRUE
);
604 rb_library_source_watch_toggled_cb (GtkToggleButton
*button
, RBLibrarySource
*source
)
608 active
= gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (source
->priv
->watch_library_check
));
609 eel_gconf_set_boolean (CONF_MONITOR_LIBRARY
, active
);
613 impl_get_browser_key (RBSource
*source
)
615 return g_strdup (CONF_STATE_SHOW_BROWSER
);
619 impl_get_paned_key (RBBrowserSource
*status
)
621 return CONF_STATE_PANED_POSITION
;
625 rb_library_source_add_location_entry_changed_cb (GtkEntry
*entry
,
628 gtk_widget_set_sensitive (target
, g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (entry
)), -1) > 0);
632 rb_library_source_add_location (RBLibrarySource
*source
, GtkWindow
*win
)
634 GladeXML
*xml
= rb_glade_xml_new ("uri.glade",
635 "open_uri_dialog_content",
637 GtkWidget
*content
, *uri_widget
, *open_button
;
638 GtkWidget
*dialog
= gtk_dialog_new_with_buttons (_("Add Location"),
640 GTK_DIALOG_MODAL
| GTK_DIALOG_NO_SEPARATOR
,
645 g_return_if_fail (dialog
!= NULL
);
647 open_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
650 gtk_widget_set_sensitive (open_button
, FALSE
);
652 gtk_dialog_set_default_response (GTK_DIALOG (dialog
),
654 gtk_container_set_border_width (GTK_CONTAINER (dialog
), 5);
655 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), 2);
657 gtk_window_set_resizable (GTK_WINDOW (dialog
), FALSE
);
659 content
= glade_xml_get_widget (xml
, "open_uri_dialog_content");
661 g_return_if_fail (content
!= NULL
);
663 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
),
664 content
, FALSE
, FALSE
, 0);
665 gtk_container_set_border_width (GTK_CONTAINER (content
), 5);
667 uri_widget
= glade_xml_get_widget (xml
, "uri");
669 g_return_if_fail (uri_widget
!= NULL
);
671 g_signal_connect_object (G_OBJECT (uri_widget
),
673 G_CALLBACK (rb_library_source_add_location_entry_changed_cb
),
676 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_OK
) {
677 char *uri
= gtk_editable_get_chars (GTK_EDITABLE (uri_widget
), 0, -1);
679 GnomeVFSURI
*vfsuri
= gnome_vfs_uri_new (uri
);
680 if (vfsuri
!= NULL
) {
681 rhythmdb_add_uri (source
->priv
->db
, uri
);
682 gnome_vfs_uri_unref (vfsuri
);
684 rb_debug ("invalid uri: \"%s\"", uri
);
689 gtk_widget_destroy (GTK_WIDGET (dialog
));
694 impl_receive_drag (RBSource
*asource
, GtkSelectionData
*data
)
696 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
698 GList
*entries
= NULL
;
701 rb_debug ("parsing uri list");
702 list
= rb_uri_list_parse ((const char *) data
->data
);
703 is_id
= (data
->type
== gdk_atom_intern ("application/x-rhythmbox-entry", TRUE
));
705 for (i
= list
; i
!= NULL
; i
= g_list_next (i
)) {
706 if (i
->data
!= NULL
) {
708 RhythmDBEntry
*entry
;
710 entry
= rhythmdb_entry_lookup_from_string (source
->priv
->db
, uri
, is_id
);
713 /* add to the library */
714 rhythmdb_add_uri (source
->priv
->db
, uri
);
716 /* add to list of entries to copy */
717 entries
= g_list_prepend (entries
, entry
);
725 entries
= g_list_reverse (entries
);
726 if (rb_source_can_paste (asource
))
727 rb_source_paste (asource
, entries
);
728 g_list_free (entries
);
736 impl_show_popup (RBSource
*source
)
738 _rb_source_show_popup (source
, "/LibrarySourcePopup");
742 #ifdef ENABLE_TRACK_TRANSFER
744 rb_library_source_path_changed_cb (GtkComboBox
*box
, RBLibrarySource
*source
)
749 index
= gtk_combo_box_get_active (box
);
750 path
= (index
>= 0) ? library_layout_paths
[index
].path
: "";
751 eel_gconf_set_string (CONF_LIBRARY_LAYOUT_PATH
, path
);
755 rb_library_source_filename_changed_cb (GtkComboBox
*box
, RBLibrarySource
*source
)
757 const char *filename
;
760 index
= gtk_combo_box_get_active (box
);
761 filename
= (index
>= 0) ? library_layout_filenames
[index
].path
: "";
762 eel_gconf_set_string (CONF_LIBRARY_LAYOUT_FILENAME
, filename
);
766 rb_library_source_format_changed_cb (GtkWidget
*widget
, RBLibrarySource
*source
)
768 GMAudioProfile
*profile
;
770 profile
= gm_audio_profile_choose_get_active (widget
);
771 eel_gconf_set_string (CONF_LIBRARY_PREFERRED_FORMAT
, gm_audio_profile_get_id (profile
));
775 * Perform magic on a path to make it safe.
777 * This will always replace '/' with ' ', and optionally make the file name
778 * shell-friendly. This involves removing [?*\ ] and replacing with '_'. Also
779 * any leading periods are removed so that the files don't end up being hidden.
782 sanitize_path (const char *str
)
787 /* Skip leading periods, otherwise files disappear... */
792 /* Replace path seperators with a hyphen */
793 g_strdelimit (s
, "/", '-');
794 if (eel_gconf_get_boolean (CONF_LIBRARY_STRIP_CHARS
)) {
795 /* Replace separators with a hyphen */
796 g_strdelimit (s
, "\\:|", '-');
797 /* Replace all other weird characters to whitespace */
798 g_strdelimit (s
, "*?&!\'\"$()`>{}", ' ');
799 /* Replace all whitespace with underscores */
800 /* TODO: I'd like this to compress whitespace aswell */
801 g_strdelimit (s
, "\t ", '_');
803 res
= g_filename_from_utf8(s
, -1, NULL
, NULL
, NULL
);
805 return res
? res
: g_strdup(str
);
809 * Parse a filename pattern and replace markers with values from a TrackDetails
812 * Valid markers so far are:
814 * %aa -- album artist
815 * %aA -- album artist (lowercase)
816 * %as -- album artist sortname
817 * %aS -- album artist sortname (lowercase)
818 * %tn -- track number (i.e 8)
819 * %tN -- track number, zero padded (i.e 08)
821 * %ta -- track artist
822 * %tA -- track artist (lowercase)
823 * %ts -- track artist sortname
824 * %tS -- track artist sortname (lowercase)
827 filepath_parse_pattern (const char *pattern
,
828 RhythmDBEntry
*entry
)
830 /* p is the pattern iterator, i is a general purpose iterator */
835 if (pattern
== NULL
|| pattern
[0] == 0)
836 return g_strdup (" ");
838 s
= g_string_new (NULL
);
844 /* If not a % marker, copy and continue */
846 g_string_append_c (s
, *p
++);
847 /* Explicit increment as we continue past the increment */
851 /* Is a % marker, go to next and see what to do */
857 g_string_append_c (s
, '%');
865 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ALBUM
));
868 temp
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ALBUM_FOLDED
));
871 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ARTIST
));
874 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ARTIST_FOLDED
));
877 string = sanitize_path (album sort name);
880 char *t = g_utf8_strdown (album sort name)
881 string = sanitize_path (t);
885 string
= g_strdup_printf ("%%a%c", *p
);
896 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_TITLE
));
899 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_TITLE_FOLDED
));
902 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ARTIST
));
905 string
= sanitize_path (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_ARTIST_FOLDED
));
908 string = sanitize_path (artist sort name);
911 char *t = g_utf8_strdown (artist sort name)
912 string = sanitize_path (t);
917 string
= g_strdup_printf ("%u", (guint
)rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_TRACK_NUMBER
));
920 /* Track number, zero-padded */
921 string
= g_strdup_printf ("%02u", (guint
)rhythmdb_entry_get_ulong (entry
, RHYTHMDB_PROP_TRACK_NUMBER
));
924 string
= g_strdup_printf ("%%t%c", *p
);
930 string
= g_strdup_printf ("%%%c", *p
);
934 g_string_append (s
, string
);
941 g_string_free (s
, FALSE
);
946 layout_example_label_update (RBLibrarySource
*source
)
954 GMAudioProfile
*profile
;
955 RhythmDBEntryType entry_type
;
956 RhythmDBEntry
*sample_entry
;
958 profile
= gm_audio_profile_choose_get_active (source
->priv
->preferred_format_menu
);
960 /* TODO: sucky. Replace with get-gconf-key-with-default mojo */
961 file_pattern
= eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME
);
962 if (file_pattern
== NULL
) {
963 file_pattern
= g_strdup (library_layout_filenames
[0].path
);
965 path_pattern
= eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH
);
966 if (path_pattern
== NULL
) {
967 path_pattern
= g_strdup (library_layout_paths
[0].path
);
970 g_object_get (source
, "entry-type", &entry_type
, NULL
);
971 sample_entry
= rhythmdb_entry_example_new (source
->priv
->db
, entry_type
, NULL
);
972 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
974 file_value
= filepath_parse_pattern (file_pattern
, sample_entry
);
975 path_value
= filepath_parse_pattern (path_pattern
, sample_entry
);
976 rhythmdb_entry_unref (sample_entry
);
978 example
= g_build_filename (G_DIR_SEPARATOR_S
, path_value
, file_value
, NULL
);
980 g_free (file_pattern
);
982 g_free (path_pattern
);
984 format
= g_strconcat ("<small><i><b>Example Path:</b> ",
987 profile
? gm_audio_profile_get_extension (profile
) : "ogg",
988 "</i></small>", NULL
);
991 gtk_label_set_markup (GTK_LABEL (source
->priv
->layout_example_label
), format
);
996 rb_library_source_layout_path_changed (GConfClient
*client
,
999 RBLibrarySource
*source
)
1004 g_return_if_fail (strcmp (entry
->key
, CONF_LIBRARY_LAYOUT_PATH
) == 0);
1006 rb_debug ("layout path changed");
1008 if (entry
->value
== NULL
) {
1009 value
= g_strdup (library_layout_paths
[0].path
);
1010 } else if (entry
->value
->type
== GCONF_VALUE_STRING
) {
1011 value
= g_strdup (gconf_value_get_string (entry
->value
));
1016 while (library_layout_paths
[i
].path
&& strcmp (library_layout_paths
[i
].path
, value
) != 0) {
1021 gtk_combo_box_set_active (GTK_COMBO_BOX (source
->priv
->layout_path_menu
), i
);
1023 layout_example_label_update (source
);
1027 rb_library_source_layout_filename_changed (GConfClient
*client
,
1030 RBLibrarySource
*source
)
1035 g_return_if_fail (strcmp (entry
->key
, CONF_LIBRARY_LAYOUT_FILENAME
) == 0);
1037 rb_debug ("layout filename changed");
1039 if (entry
->value
== NULL
) {
1040 value
= g_strdup (library_layout_filenames
[0].path
);
1041 } else if (entry
->value
->type
== GCONF_VALUE_STRING
) {
1042 value
= g_strdup (gconf_value_get_string (entry
->value
));
1047 while (library_layout_filenames
[i
].path
&& strcmp (library_layout_filenames
[i
].path
, value
) != 0) {
1052 gtk_combo_box_set_active (GTK_COMBO_BOX (source
->priv
->layout_filename_menu
), i
);
1054 layout_example_label_update (source
);
1058 * Build the absolute filename for the specified track.
1060 * The base path is the extern variable 'base_path', the format to use
1061 * is the extern variable 'file_pattern'. Free the result when you
1062 * have finished with it.
1064 * Stolen from Sound-Juicer
1067 build_filename (RBLibrarySource
*source
, RhythmDBEntry
*entry
)
1075 char *extension
= NULL
;
1077 const char *layout_path
;
1078 const char *layout_filename
;
1079 const char *preferred_format
;
1081 list
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
1082 layout_path
= eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH
);
1083 layout_filename
= eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME
);
1084 preferred_format
= eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT
);
1086 if (list
== NULL
|| layout_path
== NULL
|| layout_filename
== NULL
|| preferred_format
== NULL
) {
1088 rb_debug ("Could not retrieve settings from GConf");
1089 g_slist_free (list
);
1093 uri
= gnome_vfs_uri_new ((const char *)list
->data
);
1095 /* do something.. */
1096 g_slist_free (list
);
1100 g_slist_free (list
);
1102 realpath
= filepath_parse_pattern (layout_path
, entry
);
1103 new = gnome_vfs_uri_append_path (uri
, realpath
);
1104 gnome_vfs_uri_unref (uri
);
1108 if (g_str_has_prefix (rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_MIMETYPE
), "audio/x-raw")) {
1109 GMAudioProfile
*profile
;
1110 profile
= gm_audio_profile_lookup (preferred_format
);
1112 extension
= g_strdup (gm_audio_profile_get_extension (profile
));
1115 if (extension
== NULL
) {
1120 /* use the old extension. strip anything after a '?' for http/daap/etc */
1121 uri
= rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
);
1122 loc
= g_utf8_strrchr (uri
, -1, '.');
1124 loc
= g_utf8_strrchr (uri
, -1, '/');
1128 extension
= g_strdup (loc
+ 1);
1130 tmp
= g_utf8_strchr (extension
, -1, '?');
1135 realfile
= filepath_parse_pattern (layout_filename
, entry
);
1137 filename
= g_strdup_printf ("%s.%s", realfile
, extension
);
1140 filename
= realfile
;
1143 new = gnome_vfs_uri_append_file_name (uri
, filename
);
1144 gnome_vfs_uri_unref (uri
);
1149 string
= gnome_vfs_uri_to_string (uri
, 0);
1150 gnome_vfs_uri_unref (uri
);
1156 impl_can_paste (RBSource
*asource
)
1158 #ifdef ENABLE_TRACK_TRANSFER
1160 gboolean can_paste
= TRUE
;
1162 list
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
1163 can_paste
= ((list
!= NULL
) &&
1164 (eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH
) != NULL
) &&
1165 (eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME
) != NULL
) &&
1166 (eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT
) != NULL
));
1168 g_slist_free (list
);
1175 #ifdef ENABLE_TRACK_TRANSFER
1177 completed_cb (RhythmDBEntry
*entry
, const char *dest
, RBLibrarySource
*source
)
1179 rhythmdb_add_uri (source
->priv
->db
, dest
);
1183 impl_paste (RBSource
*asource
, GList
*entries
)
1185 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
1186 RBRemovableMediaManager
*rm_mgr
;
1190 RhythmDBEntryType source_entry_type
;
1192 sl
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
1194 (eel_gconf_get_string (CONF_LIBRARY_LAYOUT_PATH
) == NULL
)||
1195 (eel_gconf_get_string (CONF_LIBRARY_LAYOUT_FILENAME
) == NULL
) ||
1196 (eel_gconf_get_string (CONF_LIBRARY_PREFERRED_FORMAT
) == NULL
)) {
1198 g_warning ("RBLibrarySource impl_paste called when gconf keys unset");
1202 g_object_get (source
,
1204 "entry-type", &source_entry_type
,
1206 g_object_get (shell
, "removable-media-manager", &rm_mgr
, NULL
);
1207 g_object_unref (shell
);
1209 for (l
= entries
; l
!= NULL
; l
= g_list_next (l
)) {
1210 RhythmDBEntry
*entry
= (RhythmDBEntry
*)l
->data
;
1211 RhythmDBEntryType entry_type
;
1212 RBSource
*source_source
;
1215 rb_debug ("pasting entry %s", rhythmdb_entry_get_string (entry
, RHYTHMDB_PROP_LOCATION
));
1217 entry_type
= rhythmdb_entry_get_entry_type (entry
);
1218 if (entry_type
== source_entry_type
)
1219 /* copying to ourselves would be silly */
1222 /* see if the responsible source lets us copy */
1223 source_source
= rb_shell_get_source_by_entry_type (shell
, entry_type
);
1224 if ((source_source
!= NULL
) && !rb_source_can_copy (source_source
))
1227 dest
= build_filename (source
, entry
);
1229 rb_debug ("could not create destination path for entry");
1233 rb_removable_media_manager_queue_transfer (rm_mgr
, entry
,
1235 (RBTranferCompleteCallback
)completed_cb
, source
);
1237 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, source_entry_type
);
1239 g_object_unref (rm_mgr
);
1244 impl_want_uri (RBSource
*source
, const char *uri
)
1246 /* assume anything local, on smb, or on sftp is a song */
1247 if (rb_uri_is_local (uri
) ||
1248 g_str_has_prefix (uri
, "smb://") ||
1249 g_str_has_prefix (uri
, "sftp://"))
1256 impl_add_uri (RBSource
*asource
, const char *uri
, const char *title
, const char *genre
)
1258 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
1259 /* FIXME should be synchronous */
1260 rb_debug ("adding uri %s to library", uri
);
1261 rhythmdb_add_uri (source
->priv
->db
, uri
);
1266 rb_library_source_add_child_source (const char *path
, RBLibrarySource
*library_source
)
1274 RhythmDBEntryType entry_type
;
1278 g_object_get (library_source
,
1280 "entry-type", &entry_type
,
1282 uri
= gnome_vfs_uri_new (path
);
1284 g_object_unref (shell
);
1285 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
1289 name
= gnome_vfs_uri_extract_short_name (uri
);
1290 gnome_vfs_uri_unref (uri
);
1292 rb_entry_view_get_sorting_order (rb_source_get_entry_view (RB_SOURCE (library_source
)),
1293 &sort_column
, &sort_order
);
1295 source
= rb_auto_playlist_source_new (shell
, name
, FALSE
);
1296 query
= rhythmdb_query_parse (library_source
->priv
->db
,
1297 RHYTHMDB_QUERY_PROP_EQUALS
, RHYTHMDB_PROP_TYPE
, entry_type
,
1298 RHYTHMDB_QUERY_PROP_PREFIX
, RHYTHMDB_PROP_LOCATION
, path
,
1299 RHYTHMDB_QUERY_END
);
1300 rb_auto_playlist_source_set_query (RB_AUTO_PLAYLIST_SOURCE (source
), query
,
1301 RHYTHMDB_QUERY_MODEL_LIMIT_NONE
, NULL
,
1302 sort_column
, sort_order
);
1303 rhythmdb_query_free (query
);
1304 g_free (sort_column
);
1306 g_object_get (library_source
, "icon", &icon
, NULL
);
1307 g_object_set (source
, "icon", icon
, NULL
);
1309 g_object_unref (icon
);
1312 rb_shell_append_source (shell
, source
, RB_SOURCE (library_source
));
1313 library_source
->priv
->child_sources
= g_list_prepend (library_source
->priv
->child_sources
, source
);
1315 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
1316 g_object_unref (shell
);
1321 rb_library_source_sync_child_sources (RBLibrarySource
*source
)
1325 list
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
1327 /* FIXME: don't delete and re-create sources that are still there */
1328 g_list_foreach (source
->priv
->child_sources
, (GFunc
)rb_source_delete_thyself
, NULL
);
1329 g_list_free (source
->priv
->child_sources
);
1330 source
->priv
->child_sources
= NULL
;
1332 if (g_slist_length (list
) > 1)
1333 g_slist_foreach (list
, (GFunc
)rb_library_source_add_child_source
, source
);
1334 g_slist_foreach (list
, (GFunc
) g_free
, NULL
);
1335 g_slist_free (list
);