Updated Macedonian Translation <arangela@cvs.gnome.org>
[rhythmbox.git] / sources / rb-library-source.c
blob2ecdb36afcf84143938f3460d1577a13177f6fd7
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.
24 #include <config.h>
25 #include <string.h>
27 #include <gtk/gtk.h>
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"
41 #include "rb-util.h"
42 #include "rb-file-helpers.h"
43 #include "rb-dialog.h"
44 #include "rb-debug.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,
58 guint prop_id,
59 const GValue *value,
60 GParamSpec *pspec);
61 static void rb_library_source_get_property (GObject *object,
62 guint prop_id,
63 GValue *value,
64 GParamSpec *pspec);
65 static void rb_library_source_cmd_choose_genre (GtkAction *action,
66 RBShell *shell);
67 static void rb_library_source_cmd_choose_artist (GtkAction *action,
68 RBShell *shell);
69 static void rb_library_source_cmd_choose_album (GtkAction *action,
70 RBShell *shell);
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,
81 guint cnxn_id,
82 GConfEntry *entry,
83 RBLibrarySource *source);
84 static void rb_library_source_ui_pref_changed (GConfClient *client,
85 guint cnxn_id,
86 GConfEntry *entry,
87 RBLibrarySource *source);
88 static void rb_library_source_library_location_changed (GConfClient *client,
89 guint cnxn_id,
90 GConfEntry *entry,
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);
97 /* source methods */
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,
116 GdkDragContext *dc,
117 gint x, gint y,
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
133 RhythmDB *db;
135 RBLibraryBrowser *browser;
136 GtkWidget *vbox;
137 GSList *browser_views_group;
139 GdkPixbuf *pixbuf;
141 RBEntryView *songs;
143 GtkWidget *paned;
145 gboolean lock;
147 RhythmDBQueryModel *cached_all_query;
148 char *search_text;
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;
162 char *sorting_key;
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 }};
187 enum
189 PROP_0,
190 PROP_ENTRY_TYPE,
191 PROP_SORTING_KEY
195 G_DEFINE_TYPE (RBLibrarySource, rb_library_source, RB_TYPE_SOURCE)
197 static void
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,
236 PROP_ENTRY_TYPE,
237 g_param_spec_uint ("entry-type",
238 "Entry type",
239 "Type of the entries which should be displayed by this source",
241 G_MAXINT,
242 RHYTHMDB_ENTRY_TYPE_SONG,
243 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
245 g_object_class_install_property (object_class,
246 PROP_SORTING_KEY,
247 g_param_spec_string ("sorting-key",
248 "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));
256 static void
257 rb_library_source_library_location_changed (GConfClient *client,
258 guint cnxn_id,
259 GConfEntry *entry,
260 RBLibrarySource *source)
262 if (source->priv->config_widget)
263 rb_library_source_preferences_sync (source);
266 static void
267 rb_library_source_ui_prefs_sync (RBLibrarySource *source)
269 if (source->priv->config_widget)
270 rb_library_source_preferences_sync (source);
273 static void
274 rb_library_source_ui_pref_changed (GConfClient *client,
275 guint cnxn_id,
276 GConfEntry *entry,
277 RBLibrarySource *source)
279 rb_debug ("ui pref changed");
280 rb_library_source_ui_prefs_sync (source);
283 static void
284 rb_library_source_init (RBLibrarySource *source)
286 source->priv = RB_LIBRARY_SOURCE_GET_PRIVATE (source);
288 /* Drag'n'Drop */
290 source->priv->vbox = gtk_vbox_new (FALSE, 5);
292 gtk_container_add (GTK_CONTAINER (source), source->priv->vbox);
295 static void
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;
317 static void
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);
345 static void
346 rb_library_source_songs_show_popup_cb (RBEntryView *view,
347 gboolean over_entry,
348 RBLibrarySource *source)
350 if (over_entry)
351 _rb_source_show_popup (RB_SOURCE (source), "/LibraryViewPopup");
352 else
353 _rb_source_show_popup (RB_SOURCE (source), "/LibrarySourcePopup");
356 static GObject *
357 rb_library_source_constructor (GType type, guint n_construct_properties,
358 GObjectConstructParam *construct_properties)
360 RBLibrarySource *source;
361 RBLibrarySourceClass *klass;
362 RBShell *shell;
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),
375 "LibraryActions",
376 rb_library_source_actions,
377 G_N_ELEMENTS (rb_library_source_actions),
378 shell);
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),
388 source, 0);
390 /* set up songs tree view */
391 source->priv->songs = rb_entry_view_new (source->priv->db, shell_player,
392 source->priv->sorting_key,
393 TRUE, FALSE);
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),
413 source, 0);
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
418 * library.
420 g_signal_connect_object (G_OBJECT (source->priv->songs),
421 "drag_data_received",
422 G_CALLBACK (songs_view_drag_data_received_cb),
423 source, 0);
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),
436 "size_allocate",
437 G_CALLBACK (paned_size_allocate_cb),
438 source, 0);
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,
453 source);
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,
459 source);
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,
465 source);
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,
479 RHYTHMDB_QUERY_END);
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);
487 static void
488 rb_library_source_set_property (GObject *object,
489 guint prop_id,
490 const GValue *value,
491 GParamSpec *pspec)
493 RBLibrarySource *source = RB_LIBRARY_SOURCE (object);
495 switch (prop_id) {
496 case PROP_ENTRY_TYPE:
497 source->priv->entry_type = g_value_get_uint (value);
498 break;
499 case PROP_SORTING_KEY:
500 g_free (source->priv->sorting_key);
501 source->priv->sorting_key = g_strdup (g_value_get_string (value));
502 break;
503 default:
504 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
505 break;
509 static void
510 rb_library_source_get_property (GObject *object,
511 guint prop_id,
512 GValue *value,
513 GParamSpec *pspec)
515 RBLibrarySource *source = RB_LIBRARY_SOURCE (object);
517 switch (prop_id) {
518 case PROP_ENTRY_TYPE:
519 g_value_set_uint (value, source->priv->entry_type);
520 break;
521 case PROP_SORTING_KEY:
522 g_value_set_string (value, source->priv->sorting_key);
523 break;
524 default:
525 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
526 break;
530 RBSource *
531 rb_library_source_new (RBShell *shell)
533 RBSource *source;
534 GdkPixbuf *icon;
535 gint size;
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",
540 size,
541 0, NULL);
543 source = RB_SOURCE (g_object_new (RB_TYPE_LIBRARY_SOURCE,
544 "name", _("Library"),
545 "entry-type", RHYTHMDB_ENTRY_TYPE_SONG,
546 "shell", shell,
547 "icon", icon,
548 NULL));
549 rb_shell_register_entry_type_for_source (shell, source,
550 RHYTHMDB_ENTRY_TYPE_SONG);
551 return source;
554 static void
555 rb_library_source_cmd_choose_genre (GtkAction *action,
556 RBShell *shell)
558 GList *props;
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);
570 static void
571 rb_library_source_cmd_choose_artist (GtkAction *action,
572 RBShell *shell)
574 GList *props;
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);
586 static void
587 rb_library_source_cmd_choose_album (GtkAction *action,
588 RBShell *shell)
590 GList *props;
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);
602 static void
603 songs_view_sort_order_changed_cb (RBEntryView *view, RBLibrarySource *source)
605 rb_debug ("sort order changed");
606 rb_entry_view_resort_model (view);
609 static const char *
610 impl_get_browser_key (RBSource *source)
612 return CONF_STATE_SHOW_BROWSER;
615 static const char *
616 impl_get_paned_key (RBLibrarySource *status)
618 return CONF_STATE_PANED_POSITION;
621 static void
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)
627 return;
628 if (search_text != NULL &&
629 source->priv->search_text != NULL
630 && !strcmp (search_text, source->priv->search_text))
631 return;
633 if (search_text[0] == '\0')
634 search_text = NULL;
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));
646 static RBEntryView *
647 impl_get_entry_view (RBSource *asource)
649 RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
651 return source->priv->songs;
654 static GList *
655 impl_get_property_views (RBSource *asource)
657 GList *ret;
658 RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
660 ret = rb_library_browser_get_property_views (source->priv->browser);
661 return ret;
664 static void
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))
672 changed = TRUE;
674 if (source->priv->search_text != NULL)
675 changed = TRUE;
676 g_free (source->priv->search_text);
677 source->priv->search_text = NULL;
679 if (changed) {
680 rb_library_source_do_query (source);
681 rb_source_notify_filter_changed (RB_SOURCE (source));
685 static GtkWidget *
686 impl_get_config_widget (RBSource *asource, RBShellPreferences *prefs)
688 RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
689 GtkWidget *tmp;
690 GladeXML *xml;
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),
716 "selection-changed",
717 G_CALLBACK (rb_library_source_library_location_cb),
718 asource);
719 g_signal_connect (G_OBJECT (source->priv->shell_prefs),
720 "hide",
721 G_CALLBACK (rb_library_source_prefs_update),
722 asource);
723 g_signal_connect (G_OBJECT (source->priv->watch_library_check),
724 "toggled",
725 G_CALLBACK (rb_library_source_watch_toggled_cb),
726 asource);
728 return source->priv->config_widget;
731 static void
732 rb_library_source_preferences_sync (RBLibrarySource *source)
734 GSList *list;
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),
747 source);
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);
754 g_free (s);
755 } else {
756 /* either no or multiple folders are chosen. make the widget blank*/
757 /*gtk_file_chooser_set_uri (source->priv->library_location_widget,
758 "");*/
761 g_signal_handlers_unblock_by_func (G_OBJECT (source->priv->library_location_widget),
762 G_CALLBACK (rb_library_source_library_location_cb),
763 source);
765 g_slist_foreach (list, (GFunc) g_free, NULL);
766 g_slist_free (list);
768 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (source->priv->watch_library_check),
769 eel_gconf_get_boolean (CONF_MONITOR_LIBRARY));
772 void
773 rb_library_source_browser_views_activated_cb (GtkWidget *widget,
774 RBLibrarySource *source)
776 int index;
778 if (source->priv->loading_prefs == TRUE)
779 return;
781 index = g_slist_index (source->priv->browser_views_group, widget);
783 eel_gconf_set_integer (CONF_UI_BROWSER_VIEWS, index);
786 static void
787 impl_delete (RBSource *asource)
789 RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
790 GList *sel, *tem;
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);
797 g_list_free (sel);
800 static void
801 impl_move_to_trash (RBSource *asource)
803 RBLibrarySource *source = RB_LIBRARY_SOURCE (asource);
804 GList *sel, *tem;
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);
812 g_list_free (sel);
815 static void
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);
827 if (song_info)
828 gtk_widget_show_all (song_info);
829 else
830 rb_debug ("failed to create dialog, or no selection!");
833 static void
834 paned_size_allocate_cb (GtkWidget *widget,
835 GtkAllocation *allocation,
836 RBLibrarySource *source)
838 const char *key = rb_library_source_get_paned_key (source);;
840 /* save state */
841 rb_debug ("paned size allocate");
842 if (key)
843 eel_gconf_set_integer (key, gtk_paned_get_position (GTK_PANED (source->priv->paned)));
846 static void
847 rb_library_source_state_prefs_sync (RBLibrarySource *source)
849 const char *key;
851 rb_debug ("syncing state");
852 key = rb_library_source_get_paned_key (source);
853 if (key)
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));
860 else
861 gtk_widget_hide (GTK_WIDGET (source->priv->browser));
864 static void
865 rb_library_source_state_pref_changed (GConfClient *client,
866 guint cnxn_id,
867 GConfEntry *entry,
868 RBLibrarySource *source)
870 rb_debug ("state prefs changed");
871 rb_library_source_state_prefs_sync (source);
874 static void
875 rb_library_source_add_location_entry_changed_cb (GtkEntry *entry,
876 GtkWidget *target)
878 gtk_widget_set_sensitive (target, g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (entry)), -1) > 0);
881 void
882 rb_library_source_add_location (RBLibrarySource *source, GtkWindow *win)
884 GladeXML *xml = rb_glade_xml_new ("uri.glade",
885 "open_uri_dialog_content",
886 source);
887 GtkWidget *content, *uri_widget, *open_button;
888 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Add Location"),
889 win,
890 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR,
891 GTK_STOCK_CANCEL,
892 GTK_RESPONSE_CANCEL,
893 NULL);
895 g_return_if_fail (dialog != NULL);
897 open_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
898 GTK_STOCK_OPEN,
899 GTK_RESPONSE_OK);
900 gtk_widget_set_sensitive (open_button, FALSE);
902 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
903 GTK_RESPONSE_OK);
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),
922 "changed",
923 G_CALLBACK (rb_library_source_add_location_entry_changed_cb),
924 open_button, 0);
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);
930 if (uri != NULL) {
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);
935 } else
936 rb_debug ("invalid uri: \"%s\"", uri);
939 break;
941 default:
942 rb_debug ("Location add cancelled");
944 gtk_widget_destroy (GTK_WIDGET (dialog));
948 static gboolean
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);
957 if (list == NULL)
958 return FALSE;
960 uri_list = NULL;
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)
968 return FALSE;
970 rb_debug ("adding uris");
972 for (i = uri_list; i != NULL; i = i->next) {
973 char *uri = i->data;
975 if (uri != NULL)
976 rhythmdb_add_uri (source->priv->db, uri);
978 g_free (uri);
981 g_list_free (uri_list);
982 return TRUE;
985 static gboolean
986 impl_show_popup (RBSource *source)
988 _rb_source_show_popup (RB_SOURCE (source), "/LibrarySourcePopup");
989 return TRUE;
992 const char *
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);
999 else
1000 return NULL;
1003 gboolean
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);
1011 gboolean
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);
1019 static GPtrArray *
1020 construct_query_from_selection (RBLibrarySource *source)
1022 GPtrArray *query;
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,
1029 RHYTHMDB_PROP_TYPE,
1030 entry_type,
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,
1043 query,
1044 RHYTHMDB_QUERY_SUBQUERY,
1045 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);
1057 return query;
1060 static void
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));
1069 if (is_all_query) {
1070 g_object_ref (source->priv->cached_all_query);
1071 query_model = source->priv->cached_all_query;
1072 } else {
1073 GPtrArray *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),
1080 query);
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));
1091 static void
1092 songs_view_drag_data_received_cb (GtkWidget *widget,
1093 GdkDragContext *dc,
1094 gint x, gint y,
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);
1103 static gboolean
1104 rb_library_source_process_library_location_change (RBLibrarySource *source)
1106 GSList *list;
1108 if (!source->priv->library_location_change_pending)
1109 return FALSE;
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;
1120 return FALSE;
1123 static void
1124 rb_library_source_prefs_update (RBShellPreferences *prefs,
1125 RBLibrarySource *source)
1127 rb_library_source_process_library_location_change (source);
1130 static gboolean
1131 rb_library_source_process_library_handle_selection (RBLibrarySource *source)
1133 GSList *list;
1135 if (!source->priv->library_location_handle_pending)
1136 return FALSE;
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"));
1145 } else {
1146 source->priv->library_location_change_pending = TRUE;
1150 source->priv->library_location_handle_pending = FALSE;
1151 return FALSE;
1154 static void
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);
1166 static void
1167 rb_library_source_watch_toggled_cb (GtkToggleButton *button, RBLibrarySource *source)
1169 gboolean active;
1171 active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (source->priv->watch_library_check));
1172 eel_gconf_set_boolean (CONF_MONITOR_LIBRARY, active);
1175 static void
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));