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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #include <glib/gi18n.h>
29 #include <libgnomevfs/gnome-vfs.h>
30 #include <glade/glade.h>
32 #include "rb-source.h"
33 #include "rb-library-source.h"
35 #include "rhythmdb-query-model.h"
36 #include "rb-property-view.h"
37 #include "rb-glade-helpers.h"
38 #include "rb-stock-icons.h"
39 #include "rb-entry-view.h"
40 #include "rb-library-browser.h"
42 #include "rb-file-helpers.h"
43 #include "rb-dialog.h"
45 #include "eel-gconf-extensions.h"
46 #include "rb-song-info.h"
47 #include "rb-search-entry.h"
48 #include "rb-preferences.h"
49 #include "rb-shell-preferences.h"
51 static void rb_library_source_class_init (RBLibrarySourceClass
*klass
);
52 static void rb_library_source_init (RBLibrarySource
*source
);
53 static GObject
*rb_library_source_constructor (GType type
, guint n_construct_properties
,
54 GObjectConstructParam
*construct_properties
);
55 static void rb_library_source_dispose (GObject
*object
);
56 static void rb_library_source_finalize (GObject
*object
);
57 static void rb_library_source_set_property (GObject
*object
,
61 static void rb_library_source_get_property (GObject
*object
,
65 static void rb_library_source_cmd_choose_genre (GtkAction
*action
,
67 static void rb_library_source_cmd_choose_artist (GtkAction
*action
,
69 static void rb_library_source_cmd_choose_album (GtkAction
*action
,
71 static void songs_view_sort_order_changed_cb (RBEntryView
*view
, RBLibrarySource
*source
);
72 static void rb_library_source_library_location_cb (GtkFileChooser
*chooser
,
73 RBLibrarySource
*source
);
74 static void rb_library_source_browser_changed_cb (RBLibraryBrowser
*entry
,
75 RBLibrarySource
*source
);
77 static void paned_size_allocate_cb (GtkWidget
*widget
,
78 GtkAllocation
*allocation
,
79 RBLibrarySource
*source
);
80 static void rb_library_source_state_pref_changed (GConfClient
*client
,
83 RBLibrarySource
*source
);
84 static void rb_library_source_ui_pref_changed (GConfClient
*client
,
87 RBLibrarySource
*source
);
88 static void rb_library_source_library_location_changed (GConfClient
*client
,
91 RBLibrarySource
*source
);
92 static void rb_library_source_prefs_update (RBShellPreferences
*prefs
,
93 RBLibrarySource
*source
);
94 static void rb_library_source_state_prefs_sync (RBLibrarySource
*source
);
95 static void rb_library_source_ui_prefs_sync (RBLibrarySource
*source
);
96 static void rb_library_source_preferences_sync (RBLibrarySource
*source
);
98 static const char *impl_get_browser_key (RBSource
*source
);
99 static RBEntryView
*impl_get_entry_view (RBSource
*source
);
100 static GList
*impl_get_property_views (RBSource
*source
);
101 static void impl_delete (RBSource
*source
);
102 static void impl_move_to_trash (RBSource
*source
);
103 static void impl_search (RBSource
*source
, const char *text
);
104 static void impl_reset_filters (RBSource
*source
);
105 static GtkWidget
*impl_get_config_widget (RBSource
*source
, RBShellPreferences
*prefs
);
106 static void impl_song_properties (RBSource
*source
);
107 static gboolean
impl_receive_drag (RBSource
*source
, GtkSelectionData
*data
);
108 static gboolean
impl_show_popup (RBSource
*source
);
109 static const char * impl_get_paned_key (RBLibrarySource
*source
);
110 static void rb_library_source_do_query (RBLibrarySource
*source
);
112 void rb_library_source_browser_views_activated_cb (GtkWidget
*widget
,
113 RBLibrarySource
*source
);
114 static GPtrArray
* construct_query_from_selection (RBLibrarySource
*source
);
115 static void songs_view_drag_data_received_cb (GtkWidget
*widget
,
118 GtkSelectionData
*data
,
119 guint info
, guint time
,
120 RBLibrarySource
*source
);
121 static void rb_library_source_watch_toggled_cb (GtkToggleButton
*button
,
122 RBLibrarySource
*source
);
125 #define CONF_UI_LIBRARY_DIR CONF_PREFIX "/ui/library"
126 #define CONF_STATE_LIBRARY_DIR CONF_PREFIX "/state/library"
127 #define CONF_STATE_LIBRARY_SORTING CONF_PREFIX "/state/library/sorting"
128 #define CONF_STATE_PANED_POSITION CONF_PREFIX "/state/library/paned_position"
129 #define CONF_STATE_SHOW_BROWSER CONF_PREFIX "/state/library/show_browser"
131 struct RBLibrarySourcePrivate
135 RBLibraryBrowser
*browser
;
137 GSList
*browser_views_group
;
147 RhythmDBQueryModel
*cached_all_query
;
150 gboolean loading_prefs
;
151 RBShellPreferences
*shell_prefs
;
153 GtkActionGroup
*action_group
;
154 GtkWidget
*config_widget
;
156 GtkFileChooser
*library_location_widget
;
157 GtkWidget
*watch_library_check
;
158 gboolean library_location_change_pending
, library_location_handle_pending
;
160 RhythmDBEntryType entry_type
;
163 guint ui_dir_notify_id
;
164 guint state_paned_notify_id
;
165 guint state_browser_notify_id
;
166 guint state_sorting_notify_id
;
167 guint library_location_notify_id
;
170 #define RB_LIBRARY_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_LIBRARY_SOURCE, RBLibrarySourcePrivate))
172 static GtkActionEntry rb_library_source_actions
[] =
174 { "LibrarySrcChooseGenre", NULL
, N_("Browse This _Genre"), NULL
,
175 N_("Set the browser to view only this genre"),
176 G_CALLBACK (rb_library_source_cmd_choose_genre
) },
177 { "LibrarySrcChooseArtist", NULL
, N_("Browse This _Artist"), NULL
,
178 N_("Set the browser to view only this artist"),
179 G_CALLBACK (rb_library_source_cmd_choose_artist
) },
180 { "LibrarySrcChooseAlbum", NULL
, N_("Browse This A_lbum"), NULL
,
181 N_("Set the browser to view only this album"),
182 G_CALLBACK (rb_library_source_cmd_choose_album
) }
185 static const GtkTargetEntry songs_view_drag_types
[] = {{ "text/uri-list", 0, 0 }};
195 G_DEFINE_TYPE (RBLibrarySource
, rb_library_source
, RB_TYPE_SOURCE
)
198 rb_library_source_class_init (RBLibrarySourceClass
*klass
)
200 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
201 RBSourceClass
*source_class
= RB_SOURCE_CLASS (klass
);
203 object_class
->dispose
= rb_library_source_dispose
;
204 object_class
->finalize
= rb_library_source_finalize
;
205 object_class
->constructor
= rb_library_source_constructor
;
207 object_class
->set_property
= rb_library_source_set_property
;
208 object_class
->get_property
= rb_library_source_get_property
;
210 source_class
->impl_can_browse
= (RBSourceFeatureFunc
) rb_true_function
;
211 source_class
->impl_get_browser_key
= impl_get_browser_key
;
212 source_class
->impl_can_search
= (RBSourceFeatureFunc
) rb_true_function
;
213 source_class
->impl_search
= impl_search
;
214 source_class
->impl_get_entry_view
= impl_get_entry_view
;
215 source_class
->impl_get_property_views
= impl_get_property_views
;
216 source_class
->impl_get_config_widget
= impl_get_config_widget
;
217 source_class
->impl_reset_filters
= impl_reset_filters
;
218 source_class
->impl_song_properties
= impl_song_properties
;
219 source_class
->impl_can_pause
= (RBSourceFeatureFunc
) rb_true_function
;
220 source_class
->impl_can_cut
= (RBSourceFeatureFunc
) rb_false_function
;
221 source_class
->impl_can_copy
= (RBSourceFeatureFunc
) rb_true_function
;
222 source_class
->impl_can_delete
= (RBSourceFeatureFunc
) rb_true_function
;
223 source_class
->impl_can_add_to_queue
= (RBSourceFeatureFunc
) rb_true_function
;
224 source_class
->impl_can_move_to_trash
= (RBSourceFeatureFunc
) rb_true_function
;
225 source_class
->impl_delete
= impl_delete
;
226 source_class
->impl_move_to_trash
= impl_move_to_trash
;
227 source_class
->impl_have_url
= (RBSourceFeatureFunc
) rb_false_function
;
228 source_class
->impl_receive_drag
= impl_receive_drag
;
229 source_class
->impl_show_popup
= impl_show_popup
;
231 klass
->impl_get_paned_key
= impl_get_paned_key
;
232 klass
->impl_has_first_added_column
= (RBLibrarySourceFeatureFunc
) rb_true_function
;
233 klass
->impl_has_drop_support
= (RBLibrarySourceFeatureFunc
) rb_true_function
;
235 g_object_class_install_property (object_class
,
237 g_param_spec_uint ("entry-type",
239 "Type of the entries which should be displayed by this source",
242 RHYTHMDB_ENTRY_TYPE_SONG
,
243 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
245 g_object_class_install_property (object_class
,
247 g_param_spec_string ("sorting-key",
249 "GConf key for storing sort-order",
250 CONF_STATE_LIBRARY_SORTING
,
251 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
253 g_type_class_add_private (klass
, sizeof (RBLibrarySourcePrivate
));
257 rb_library_source_library_location_changed (GConfClient
*client
,
260 RBLibrarySource
*source
)
262 if (source
->priv
->config_widget
)
263 rb_library_source_preferences_sync (source
);
267 rb_library_source_ui_prefs_sync (RBLibrarySource
*source
)
269 if (source
->priv
->config_widget
)
270 rb_library_source_preferences_sync (source
);
274 rb_library_source_ui_pref_changed (GConfClient
*client
,
277 RBLibrarySource
*source
)
279 rb_debug ("ui pref changed");
280 rb_library_source_ui_prefs_sync (source
);
284 rb_library_source_init (RBLibrarySource
*source
)
286 source
->priv
= RB_LIBRARY_SOURCE_GET_PRIVATE (source
);
290 source
->priv
->vbox
= gtk_vbox_new (FALSE
, 5);
292 gtk_container_add (GTK_CONTAINER (source
), source
->priv
->vbox
);
296 rb_library_source_dispose (GObject
*object
)
298 RBLibrarySource
*source
;
299 source
= RB_LIBRARY_SOURCE (object
);
301 if (source
->priv
->shell_prefs
) {
302 g_object_unref (source
->priv
->shell_prefs
);
303 source
->priv
->shell_prefs
= NULL
;
306 if (source
->priv
->db
) {
307 g_object_unref (source
->priv
->db
);
308 source
->priv
->db
= NULL
;
311 if (source
->priv
->search_text
) {
312 g_free (source
->priv
->search_text
);
313 source
->priv
->search_text
= NULL
;
318 rb_library_source_finalize (GObject
*object
)
320 RBLibrarySource
*source
;
322 g_return_if_fail (object
!= NULL
);
323 g_return_if_fail (RB_IS_LIBRARY_SOURCE (object
));
325 source
= RB_LIBRARY_SOURCE (object
);
327 g_return_if_fail (source
->priv
!= NULL
);
329 rb_debug ("finalizing library source");
331 eel_gconf_notification_remove (source
->priv
->ui_dir_notify_id
);
332 eel_gconf_notification_remove (source
->priv
->library_location_notify_id
);
333 eel_gconf_notification_remove (source
->priv
->state_browser_notify_id
);
334 eel_gconf_notification_remove (source
->priv
->state_paned_notify_id
);
335 eel_gconf_notification_remove (source
->priv
->state_sorting_notify_id
);
337 g_free (source
->priv
->sorting_key
);
339 if (source
->priv
->cached_all_query
)
340 g_object_unref (G_OBJECT (source
->priv
->cached_all_query
));
342 G_OBJECT_CLASS (rb_library_source_parent_class
)->finalize (object
);
346 rb_library_source_songs_show_popup_cb (RBEntryView
*view
,
348 RBLibrarySource
*source
)
351 _rb_source_show_popup (RB_SOURCE (source
), "/LibraryViewPopup");
353 _rb_source_show_popup (RB_SOURCE (source
), "/LibrarySourcePopup");
357 rb_library_source_constructor (GType type
, guint n_construct_properties
,
358 GObjectConstructParam
*construct_properties
)
360 RBLibrarySource
*source
;
361 RBLibrarySourceClass
*klass
;
363 GObject
*shell_player
;
365 klass
= RB_LIBRARY_SOURCE_CLASS (g_type_class_peek (RB_TYPE_LIBRARY_SOURCE
));
367 source
= RB_LIBRARY_SOURCE (G_OBJECT_CLASS (rb_library_source_parent_class
)
368 ->constructor (type
, n_construct_properties
, construct_properties
));
370 g_object_get (G_OBJECT (source
), "shell", &shell
, NULL
);
371 g_object_get (G_OBJECT (shell
), "db", &source
->priv
->db
, NULL
);
372 shell_player
= rb_shell_get_player (shell
);
374 source
->priv
->action_group
= _rb_source_register_action_group (RB_SOURCE (source
),
376 rb_library_source_actions
,
377 G_N_ELEMENTS (rb_library_source_actions
),
380 g_object_unref (G_OBJECT (shell
));
382 source
->priv
->paned
= gtk_vpaned_new ();
384 source
->priv
->browser
= rb_library_browser_new (source
->priv
->db
);
385 gtk_paned_pack1 (GTK_PANED (source
->priv
->paned
), GTK_WIDGET (source
->priv
->browser
), TRUE
, FALSE
);
386 g_signal_connect_object (G_OBJECT (source
->priv
->browser
), "changed",
387 G_CALLBACK (rb_library_source_browser_changed_cb
),
390 /* set up songs tree view */
391 source
->priv
->songs
= rb_entry_view_new (source
->priv
->db
, shell_player
,
392 source
->priv
->sorting_key
,
394 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_TRACK_NUMBER
);
395 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_TITLE
);
396 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_GENRE
);
397 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_ARTIST
);
398 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_ALBUM
);
399 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_YEAR
);
400 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_DURATION
);
401 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_QUALITY
);
402 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_RATING
);
403 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_PLAY_COUNT
);
404 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_LAST_PLAYED
);
405 if (rb_library_source_has_first_added_column (source
))
406 rb_entry_view_append_column (source
->priv
->songs
, RB_ENTRY_VIEW_COL_FIRST_SEEN
);
408 g_signal_connect_object (G_OBJECT (source
->priv
->songs
), "show_popup",
409 G_CALLBACK (rb_library_source_songs_show_popup_cb
), source
, 0);
410 g_signal_connect_object (G_OBJECT (source
->priv
->songs
),
411 "sort-order-changed",
412 G_CALLBACK (songs_view_sort_order_changed_cb
),
415 /* set up drag and drop for the song tree view.
416 * we don't use RBEntryView's DnD support because it does too much.
417 * we just want to be able to drop songs in to add them to the
420 g_signal_connect_object (G_OBJECT (source
->priv
->songs
),
421 "drag_data_received",
422 G_CALLBACK (songs_view_drag_data_received_cb
),
425 /* only add drop support for the Library, subclasses can add it themselves */
426 if (rb_library_source_has_drop_support (source
)) {
427 gtk_drag_dest_set (GTK_WIDGET (source
->priv
->songs
),
428 GTK_DEST_DEFAULT_ALL
,
429 songs_view_drag_types
, 1,
430 GDK_ACTION_COPY
| GDK_ACTION_MOVE
); /* really accept move actions? */
434 /* this gets emitted when the paned thingie is moved */
435 g_signal_connect_object (G_OBJECT (source
->priv
->songs
),
437 G_CALLBACK (paned_size_allocate_cb
),
440 gtk_paned_pack2 (GTK_PANED (source
->priv
->paned
), GTK_WIDGET (source
->priv
->songs
), TRUE
, FALSE
);
442 gtk_box_pack_start_defaults (GTK_BOX (source
->priv
->vbox
), source
->priv
->paned
);
444 gtk_widget_show_all (GTK_WIDGET (source
));
446 rb_library_source_state_prefs_sync (source
);
447 rb_library_source_ui_prefs_sync (source
);
449 if (source
->priv
->sorting_key
) {
450 source
->priv
->state_sorting_notify_id
=
451 eel_gconf_notification_add (source
->priv
->sorting_key
,
452 (GConfClientNotifyFunc
) rb_library_source_state_pref_changed
,
455 if (rb_library_source_get_paned_key (source
)) {
456 source
->priv
->state_paned_notify_id
=
457 eel_gconf_notification_add (rb_library_source_get_paned_key (source
),
458 (GConfClientNotifyFunc
) rb_library_source_state_pref_changed
,
461 if (rb_source_get_browser_key (RB_SOURCE (source
))) {
462 source
->priv
->state_browser_notify_id
=
463 eel_gconf_notification_add (rb_source_get_browser_key (RB_SOURCE (source
)),
464 (GConfClientNotifyFunc
) rb_library_source_state_pref_changed
,
468 source
->priv
->ui_dir_notify_id
=
469 eel_gconf_notification_add (CONF_UI_LIBRARY_DIR
,
470 (GConfClientNotifyFunc
) rb_library_source_ui_pref_changed
, source
);
471 source
->priv
->library_location_notify_id
=
472 eel_gconf_notification_add (CONF_LIBRARY_LOCATION
,
473 (GConfClientNotifyFunc
) rb_library_source_library_location_changed
, source
);
475 source
->priv
->cached_all_query
= rhythmdb_query_model_new_empty (source
->priv
->db
);
476 rhythmdb_do_full_query_async (source
->priv
->db
,
477 RHYTHMDB_QUERY_RESULTS (source
->priv
->cached_all_query
),
478 RHYTHMDB_QUERY_PROP_EQUALS
, RHYTHMDB_PROP_TYPE
, source
->priv
->entry_type
,
480 rb_library_browser_set_model (source
->priv
->browser
, source
->priv
->cached_all_query
);
481 rb_library_source_do_query (source
);
483 return G_OBJECT (source
);
488 rb_library_source_set_property (GObject
*object
,
493 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (object
);
496 case PROP_ENTRY_TYPE
:
497 source
->priv
->entry_type
= g_value_get_uint (value
);
499 case PROP_SORTING_KEY
:
500 g_free (source
->priv
->sorting_key
);
501 source
->priv
->sorting_key
= g_strdup (g_value_get_string (value
));
504 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
510 rb_library_source_get_property (GObject
*object
,
515 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (object
);
518 case PROP_ENTRY_TYPE
:
519 g_value_set_uint (value
, source
->priv
->entry_type
);
521 case PROP_SORTING_KEY
:
522 g_value_set_string (value
, source
->priv
->sorting_key
);
525 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
531 rb_library_source_new (RBShell
*shell
)
537 gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR
, &size
, NULL
);
538 icon
= gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
539 "stock_music-library",
543 source
= RB_SOURCE (g_object_new (RB_TYPE_LIBRARY_SOURCE
,
544 "name", _("Library"),
545 "entry-type", RHYTHMDB_ENTRY_TYPE_SONG
,
549 rb_shell_register_entry_type_for_source (shell
, source
,
550 RHYTHMDB_ENTRY_TYPE_SONG
);
555 rb_library_source_cmd_choose_genre (GtkAction
*action
,
559 RBLibrarySource
*source
;
561 rb_debug ("choosing genre");
563 g_object_get (G_OBJECT (shell
), "selected-source", &source
, NULL
);
564 props
= rb_source_gather_selected_properties (RB_SOURCE (source
), RHYTHMDB_PROP_GENRE
);
565 rb_library_browser_set_selection (source
->priv
->browser
, RHYTHMDB_PROP_GENRE
, props
);
566 rb_list_deep_free (props
);
567 g_object_unref (source
);
571 rb_library_source_cmd_choose_artist (GtkAction
*action
,
575 RBLibrarySource
*source
;
577 rb_debug ("choosing artist");
579 g_object_get (G_OBJECT (shell
), "selected-source", &source
, NULL
);
580 props
= rb_source_gather_selected_properties (RB_SOURCE (source
), RHYTHMDB_PROP_ARTIST
);
581 rb_library_browser_set_selection (source
->priv
->browser
, RHYTHMDB_PROP_ARTIST
, props
);
582 rb_list_deep_free (props
);
583 g_object_unref (source
);
587 rb_library_source_cmd_choose_album (GtkAction
*action
,
591 RBLibrarySource
*source
;
593 rb_debug ("choosing album");
595 g_object_get (G_OBJECT (shell
), "selected-source", &source
, NULL
);
596 props
= rb_source_gather_selected_properties (RB_SOURCE (source
), RHYTHMDB_PROP_ALBUM
);
597 rb_library_browser_set_selection (source
->priv
->browser
, RHYTHMDB_PROP_ALBUM
, props
);
598 rb_list_deep_free (props
);
599 g_object_unref (source
);
603 songs_view_sort_order_changed_cb (RBEntryView
*view
, RBLibrarySource
*source
)
605 rb_debug ("sort order changed");
606 rb_entry_view_resort_model (view
);
610 impl_get_browser_key (RBSource
*source
)
612 return CONF_STATE_SHOW_BROWSER
;
616 impl_get_paned_key (RBLibrarySource
*status
)
618 return CONF_STATE_PANED_POSITION
;
622 impl_search (RBSource
*asource
, const char *search_text
)
624 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
626 if (search_text
== NULL
&& source
->priv
->search_text
== NULL
)
628 if (search_text
!= NULL
&&
629 source
->priv
->search_text
!= NULL
630 && !strcmp (search_text
, source
->priv
->search_text
))
633 if (search_text
[0] == '\0')
636 rb_debug ("doing search for \"%s\"", search_text
? search_text
: "(NULL)");
638 g_free (source
->priv
->search_text
);
639 source
->priv
->search_text
= g_strdup (search_text
);
640 rb_library_source_do_query (source
);
642 rb_source_notify_filter_changed (RB_SOURCE (source
));
647 impl_get_entry_view (RBSource
*asource
)
649 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
651 return source
->priv
->songs
;
655 impl_get_property_views (RBSource
*asource
)
658 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
660 ret
= rb_library_browser_get_property_views (source
->priv
->browser
);
665 impl_reset_filters (RBSource
*asource
)
667 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
668 gboolean changed
= FALSE
;
671 if (rb_library_browser_reset (source
->priv
->browser
))
674 if (source
->priv
->search_text
!= NULL
)
676 g_free (source
->priv
->search_text
);
677 source
->priv
->search_text
= NULL
;
680 rb_library_source_do_query (source
);
681 rb_source_notify_filter_changed (RB_SOURCE (source
));
686 impl_get_config_widget (RBSource
*asource
, RBShellPreferences
*prefs
)
688 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
692 if (source
->priv
->config_widget
)
693 return source
->priv
->config_widget
;
695 g_object_ref (G_OBJECT (prefs
));
696 source
->priv
->shell_prefs
= prefs
;
698 xml
= rb_glade_xml_new ("library-prefs.glade", "library_vbox", source
);
699 source
->priv
->config_widget
=
700 glade_xml_get_widget (xml
, "library_vbox");
701 tmp
= glade_xml_get_widget (xml
, "library_browser_views_radio");
702 source
->priv
->browser_views_group
=
703 g_slist_reverse (g_slist_copy (gtk_radio_button_get_group
704 (GTK_RADIO_BUTTON (tmp
))));
706 source
->priv
->library_location_widget
=
707 (GtkFileChooser
*) glade_xml_get_widget (xml
, "library_location_chooser");
708 source
->priv
->watch_library_check
= glade_xml_get_widget (xml
, "watch_library_check");
710 rb_glade_boldify_label (xml
, "browser_views_label");
711 rb_glade_boldify_label (xml
, "library_location_label");
712 g_object_unref (G_OBJECT (xml
));
714 rb_library_source_preferences_sync (source
);
715 g_signal_connect (G_OBJECT (source
->priv
->library_location_widget
),
717 G_CALLBACK (rb_library_source_library_location_cb
),
719 g_signal_connect (G_OBJECT (source
->priv
->shell_prefs
),
721 G_CALLBACK (rb_library_source_prefs_update
),
723 g_signal_connect (G_OBJECT (source
->priv
->watch_library_check
),
725 G_CALLBACK (rb_library_source_watch_toggled_cb
),
728 return source
->priv
->config_widget
;
732 rb_library_source_preferences_sync (RBLibrarySource
*source
)
736 rb_debug ("syncing pref dialog state");
738 list
= g_slist_nth (source
->priv
->browser_views_group
,
739 eel_gconf_get_integer (CONF_UI_BROWSER_VIEWS
));
740 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (list
->data
), TRUE
);
742 list
= eel_gconf_get_string_list (CONF_LIBRARY_LOCATION
);
744 /* don't trigger the change notification */
745 g_signal_handlers_block_by_func (G_OBJECT (source
->priv
->library_location_widget
),
746 G_CALLBACK (rb_library_source_library_location_cb
),
749 if (g_slist_length (list
) == 1) {
750 /* the uri might be missing the trailing slash */
751 gchar
*s
= g_strconcat (list
->data
, G_DIR_SEPARATOR_S
, NULL
);
752 gtk_file_chooser_set_uri (source
->priv
->library_location_widget
, s
);
753 rb_debug ("syncing library location %s", s
);
756 /* either no or multiple folders are chosen. make the widget blank*/
757 /*gtk_file_chooser_set_uri (source->priv->library_location_widget,
761 g_signal_handlers_unblock_by_func (G_OBJECT (source
->priv
->library_location_widget
),
762 G_CALLBACK (rb_library_source_library_location_cb
),
765 g_slist_foreach (list
, (GFunc
) g_free
, NULL
);
768 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (source
->priv
->watch_library_check
),
769 eel_gconf_get_boolean (CONF_MONITOR_LIBRARY
));
773 rb_library_source_browser_views_activated_cb (GtkWidget
*widget
,
774 RBLibrarySource
*source
)
778 if (source
->priv
->loading_prefs
== TRUE
)
781 index
= g_slist_index (source
->priv
->browser_views_group
, widget
);
783 eel_gconf_set_integer (CONF_UI_BROWSER_VIEWS
, index
);
787 impl_delete (RBSource
*asource
)
789 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
792 sel
= rb_entry_view_get_selected_entries (source
->priv
->songs
);
793 for (tem
= sel
; tem
!= NULL
; tem
= tem
->next
) {
794 rhythmdb_entry_delete (source
->priv
->db
, tem
->data
);
795 rhythmdb_commit (source
->priv
->db
);
801 impl_move_to_trash (RBSource
*asource
)
803 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
806 sel
= rb_entry_view_get_selected_entries (source
->priv
->songs
);
807 for (tem
= sel
; tem
!= NULL
; tem
= tem
->next
) {
808 rhythmdb_entry_move_to_trash (source
->priv
->db
,
809 (RhythmDBEntry
*) tem
->data
);
810 rhythmdb_commit (source
->priv
->db
);
816 impl_song_properties (RBSource
*asource
)
818 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
819 GtkWidget
*song_info
= NULL
;
821 g_return_if_fail (source
->priv
->songs
!= NULL
);
823 song_info
= rb_song_info_new (asource
, NULL
);
825 g_return_if_fail (song_info
!= NULL
);
828 gtk_widget_show_all (song_info
);
830 rb_debug ("failed to create dialog, or no selection!");
834 paned_size_allocate_cb (GtkWidget
*widget
,
835 GtkAllocation
*allocation
,
836 RBLibrarySource
*source
)
838 const char *key
= rb_library_source_get_paned_key (source
);;
841 rb_debug ("paned size allocate");
843 eel_gconf_set_integer (key
, gtk_paned_get_position (GTK_PANED (source
->priv
->paned
)));
847 rb_library_source_state_prefs_sync (RBLibrarySource
*source
)
851 rb_debug ("syncing state");
852 key
= rb_library_source_get_paned_key (source
);
854 gtk_paned_set_position (GTK_PANED (source
->priv
->paned
),
855 eel_gconf_get_integer (key
));
857 key
= rb_source_get_browser_key (RB_SOURCE (source
));
858 if (key
&& eel_gconf_get_boolean (key
))
859 gtk_widget_show (GTK_WIDGET (source
->priv
->browser
));
861 gtk_widget_hide (GTK_WIDGET (source
->priv
->browser
));
865 rb_library_source_state_pref_changed (GConfClient
*client
,
868 RBLibrarySource
*source
)
870 rb_debug ("state prefs changed");
871 rb_library_source_state_prefs_sync (source
);
875 rb_library_source_add_location_entry_changed_cb (GtkEntry
*entry
,
878 gtk_widget_set_sensitive (target
, g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (entry
)), -1) > 0);
882 rb_library_source_add_location (RBLibrarySource
*source
, GtkWindow
*win
)
884 GladeXML
*xml
= rb_glade_xml_new ("uri.glade",
885 "open_uri_dialog_content",
887 GtkWidget
*content
, *uri_widget
, *open_button
;
888 GtkWidget
*dialog
= gtk_dialog_new_with_buttons (_("Add Location"),
890 GTK_DIALOG_MODAL
| GTK_DIALOG_NO_SEPARATOR
,
895 g_return_if_fail (dialog
!= NULL
);
897 open_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
900 gtk_widget_set_sensitive (open_button
, FALSE
);
902 gtk_dialog_set_default_response (GTK_DIALOG (dialog
),
904 gtk_container_set_border_width (GTK_CONTAINER (dialog
), 5);
905 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), 2);
907 gtk_window_set_resizable (GTK_WINDOW (dialog
), FALSE
);
909 content
= glade_xml_get_widget (xml
, "open_uri_dialog_content");
911 g_return_if_fail (content
!= NULL
);
913 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
),
914 content
, FALSE
, FALSE
, 0);
915 gtk_container_set_border_width (GTK_CONTAINER (content
), 5);
917 uri_widget
= glade_xml_get_widget (xml
, "uri");
919 g_return_if_fail (uri_widget
!= NULL
);
921 g_signal_connect_object (G_OBJECT (uri_widget
),
923 G_CALLBACK (rb_library_source_add_location_entry_changed_cb
),
926 switch (gtk_dialog_run (GTK_DIALOG (dialog
))) {
927 case GTK_RESPONSE_OK
:
929 char *uri
= gtk_editable_get_chars (GTK_EDITABLE (uri_widget
), 0, -1);
931 GnomeVFSURI
*vfsuri
= gnome_vfs_uri_new (uri
);
932 if (vfsuri
!= NULL
) {
933 rhythmdb_add_uri (source
->priv
->db
, uri
);
934 gnome_vfs_uri_unref (vfsuri
);
936 rb_debug ("invalid uri: \"%s\"", uri
);
942 rb_debug ("Location add cancelled");
944 gtk_widget_destroy (GTK_WIDGET (dialog
));
949 impl_receive_drag (RBSource
*asource
, GtkSelectionData
*data
)
951 RBLibrarySource
*source
= RB_LIBRARY_SOURCE (asource
);
952 GList
*list
, *uri_list
, *i
;
954 rb_debug ("parsing uri list");
955 list
= gnome_vfs_uri_list_parse ((char *) data
->data
);
962 for (i
= list
; i
!= NULL
; i
= g_list_next (i
))
963 uri_list
= g_list_prepend (uri_list
, gnome_vfs_uri_to_string ((const GnomeVFSURI
*) i
->data
, 0));
965 gnome_vfs_uri_list_free (list
);
967 if (uri_list
== NULL
)
970 rb_debug ("adding uris");
972 for (i
= uri_list
; i
!= NULL
; i
= i
->next
) {
976 rhythmdb_add_uri (source
->priv
->db
, uri
);
981 g_list_free (uri_list
);
986 impl_show_popup (RBSource
*source
)
988 _rb_source_show_popup (RB_SOURCE (source
), "/LibrarySourcePopup");
993 rb_library_source_get_paned_key (RBLibrarySource
*source
)
995 RBLibrarySourceClass
*klass
= RB_LIBRARY_SOURCE_GET_CLASS (source
);
997 if (klass
->impl_get_paned_key
)
998 return klass
->impl_get_paned_key (source
);
1004 rb_library_source_has_first_added_column (RBLibrarySource
*source
)
1006 RBLibrarySourceClass
*klass
= RB_LIBRARY_SOURCE_GET_CLASS (source
);
1008 return klass
->impl_has_first_added_column (source
);
1012 rb_library_source_has_drop_support (RBLibrarySource
*source
)
1014 RBLibrarySourceClass
*klass
= RB_LIBRARY_SOURCE_GET_CLASS (source
);
1016 return klass
->impl_has_drop_support (source
);
1020 construct_query_from_selection (RBLibrarySource
*source
)
1023 RhythmDBEntryType entry_type
;
1024 GPtrArray
*browser_query
;
1026 g_object_get (G_OBJECT (source
), "entry-type", &entry_type
, NULL
);
1027 query
= rhythmdb_query_parse (source
->priv
->db
,
1028 RHYTHMDB_QUERY_PROP_EQUALS
,
1031 RHYTHMDB_QUERY_END
);
1033 /* select where type="song"
1036 if (source
->priv
->search_text
) {
1037 GPtrArray
*subquery
= rhythmdb_query_parse (source
->priv
->db
,
1038 RHYTHMDB_QUERY_PROP_LIKE
,
1039 RHYTHMDB_PROP_SEARCH_MATCH
,
1040 source
->priv
->search_text
,
1041 RHYTHMDB_QUERY_END
);
1042 rhythmdb_query_append (source
->priv
->db
,
1044 RHYTHMDB_QUERY_SUBQUERY
,
1046 RHYTHMDB_QUERY_END
);
1047 /* select where type="song" and
1048 * (genre like "foo" or artist like "foo" or album like "foo")
1053 browser_query
= rb_library_browser_construct_query (source
->priv
->browser
);
1054 rhythmdb_query_concatenate (query
, browser_query
);
1055 rhythmdb_query_free (browser_query
);
1061 rb_library_source_do_query (RBLibrarySource
*source
)
1063 RhythmDBQueryModel
*query_model
;
1064 gboolean is_all_query
;
1066 is_all_query
= (!rb_library_browser_has_selection (source
->priv
->browser
) &&
1067 (source
->priv
->search_text
== NULL
));
1070 g_object_ref (source
->priv
->cached_all_query
);
1071 query_model
= source
->priv
->cached_all_query
;
1075 query_model
= rhythmdb_query_model_new_empty (source
->priv
->db
);
1077 query
= construct_query_from_selection (source
);
1078 rhythmdb_do_full_query_async_parsed (source
->priv
->db
,
1079 RHYTHMDB_QUERY_RESULTS (query_model
),
1081 rhythmdb_query_free (query
);
1084 rb_debug ("setting query model");
1085 rb_entry_view_set_model (source
->priv
->songs
, query_model
);
1086 g_object_set (RB_SOURCE (source
), "query-model", query_model
, NULL
);
1088 g_object_unref (G_OBJECT (query_model
));
1092 songs_view_drag_data_received_cb (GtkWidget
*widget
,
1095 GtkSelectionData
*selection_data
,
1096 guint info
, guint time
,
1097 RBLibrarySource
*source
)
1099 rb_debug ("data dropped on the library source song view");
1100 rb_source_receive_drag (RB_SOURCE (source
), selection_data
);
1104 rb_library_source_process_library_location_change (RBLibrarySource
*source
)
1108 if (!source
->priv
->library_location_change_pending
)
1111 /* process the change */
1112 list
= gtk_file_chooser_get_uris (source
->priv
->library_location_widget
);
1113 eel_gconf_set_string_list (CONF_LIBRARY_LOCATION
, list
);
1115 g_slist_foreach (list
, (GFunc
) g_free
, NULL
);
1116 g_slist_free (list
);
1118 source
->priv
->library_location_change_pending
= FALSE
;
1124 rb_library_source_prefs_update (RBShellPreferences
*prefs
,
1125 RBLibrarySource
*source
)
1127 rb_library_source_process_library_location_change (source
);
1131 rb_library_source_process_library_handle_selection (RBLibrarySource
*source
)
1135 if (!source
->priv
->library_location_handle_pending
)
1138 /* this can't be processed immediately, because we sometimes get intemediate signals */
1139 for (list
= gtk_file_chooser_get_uris (source
->priv
->library_location_widget
);
1140 list
!= NULL
; list
= g_slist_next (list
)) {
1141 if ((strcmp (list
->data
, "file:///") == 0) ||
1142 (strcmp (list
->data
, "file://") == 0)) {
1143 rb_error_dialog (GTK_WINDOW (source
->priv
->shell_prefs
), _("Cannot select library location"),
1144 _("The root filesystem cannot be chosen as your library location. Please choose a different location"));
1146 source
->priv
->library_location_change_pending
= TRUE
;
1150 source
->priv
->library_location_handle_pending
= FALSE
;
1155 rb_library_source_library_location_cb (GtkFileChooser
*chooser
,
1156 RBLibrarySource
*source
)
1158 source
->priv
->library_location_handle_pending
= TRUE
;
1160 g_idle_add ((GSourceFunc
)rb_library_source_process_library_handle_selection
, source
);
1162 /* don't do the first-run druid if the user sets the library location */
1163 eel_gconf_set_boolean (CONF_FIRST_TIME
, TRUE
);
1167 rb_library_source_watch_toggled_cb (GtkToggleButton
*button
, RBLibrarySource
*source
)
1171 active
= gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (source
->priv
->watch_library_check
));
1172 eel_gconf_set_boolean (CONF_MONITOR_LIBRARY
, active
);
1176 rb_library_source_browser_changed_cb (RBLibraryBrowser
*entry
, RBLibrarySource
*source
)
1178 rb_library_source_do_query (source
);
1179 rb_source_notify_filter_changed (RB_SOURCE (source
));