udapted vi.po
[rhythmbox.git] / sources / rb-browser-source.c
blob124b9d0d168df00d93b1553d5a4361f7a71c328d
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of browser-using 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.
24 #include "config.h"
26 #include <string.h>
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 #include <libgnomevfs/gnome-vfs.h>
31 #include <glade/glade.h>
33 #include "rb-source.h"
34 #include "rb-library-source.h"
36 #include "rhythmdb-query-model.h"
37 #include "rb-property-view.h"
38 #include "rb-glade-helpers.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_browser_source_class_init (RBBrowserSourceClass *klass);
52 static void rb_browser_source_init (RBBrowserSource *source);
53 static GObject *rb_browser_source_constructor (GType type,
54 guint n_construct_properties,
55 GObjectConstructParam *construct_properties);
56 static void rb_browser_source_dispose (GObject *object);
57 static void rb_browser_source_finalize (GObject *object);
58 static void rb_browser_source_set_property (GObject *object,
59 guint prop_id,
60 const GValue *value,
61 GParamSpec *pspec);
62 static void rb_browser_source_get_property (GObject *object,
63 guint prop_id,
64 GValue *value,
65 GParamSpec *pspec);
66 static void rb_browser_source_cmd_choose_genre (GtkAction *action,
67 RBShell *shell);
68 static void rb_browser_source_cmd_choose_artist (GtkAction *action,
69 RBShell *shell);
70 static void rb_browser_source_cmd_choose_album (GtkAction *action,
71 RBShell *shell);
72 static void songs_view_sort_order_changed_cb (RBEntryView *view, RBBrowserSource *source);
73 static void rb_browser_source_browser_changed_cb (RBLibraryBrowser *entry,
74 GParamSpec *param,
75 RBBrowserSource *source);
77 static void rb_browser_source_state_prefs_sync (RBBrowserSource *source);
78 static void rb_browser_source_state_pref_changed (GConfClient *client,
79 guint cnxn_id,
80 GConfEntry *entry,
81 RBBrowserSource *source);
83 static void paned_size_allocate_cb (GtkWidget *widget,
84 GtkAllocation *allocation,
85 RBBrowserSource *source);
86 /* source methods */
87 static RBEntryView *impl_get_entry_view (RBSource *source);
88 static GList *impl_get_property_views (RBSource *source);
89 static void impl_delete (RBSource *source);
90 static void impl_search (RBSource *source, const char *text);
91 static void impl_reset_filters (RBSource *source);
92 static void impl_song_properties (RBSource *source);
93 static gboolean impl_show_popup (RBSource *source);
94 static GList *impl_get_search_actions (RBSource *source);
95 static void impl_browser_toggled (RBSource *asource, gboolean disclosed);
96 static void default_show_entry_popup (RBBrowserSource *source);
98 void rb_browser_source_browser_views_activated_cb (GtkWidget *widget,
99 RBBrowserSource *source);
100 static void songs_view_drag_data_received_cb (GtkWidget *widget,
101 GdkDragContext *dc,
102 gint x, gint y,
103 GtkSelectionData *data,
104 guint info, guint time,
105 RBBrowserSource *source);
106 static void rb_browser_source_do_query (RBBrowserSource *source,
107 gboolean subset);
109 struct RBBrowserSourcePrivate
111 RhythmDB *db;
113 RBLibraryBrowser *browser;
114 GtkWidget *vbox;
116 RBEntryView *songs;
117 GtkWidget *paned;
119 char *search_text;
120 RhythmDBQueryModel *cached_all_query;
121 RhythmDBPropType search_prop;
122 gboolean initialized;
123 gboolean query_active;
124 gboolean search_on_completion;
126 GtkActionGroup *action_group;
127 GtkActionGroup *search_action_group;
129 char *sorting_key;
130 guint state_paned_notify_id;
131 guint state_browser_notify_id;
132 guint state_sorting_notify_id;
134 gboolean dispose_has_run;
137 #define RB_BROWSER_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_BROWSER_SOURCE, RBBrowserSourcePrivate))
139 static GtkActionEntry rb_browser_source_actions [] =
141 { "BrowserSrcChooseGenre", NULL, N_("Browse This _Genre"), NULL,
142 N_("Set the browser to view only this genre"),
143 G_CALLBACK (rb_browser_source_cmd_choose_genre) },
144 { "BrowserSrcChooseArtist", NULL , N_("Browse This _Artist"), NULL,
145 N_("Set the browser to view only this artist"),
146 G_CALLBACK (rb_browser_source_cmd_choose_artist) },
147 { "BrowserSrcChooseAlbum", NULL, N_("Browse This A_lbum"), NULL,
148 N_("Set the browser to view only this album"),
149 G_CALLBACK (rb_browser_source_cmd_choose_album) }
152 static GtkRadioActionEntry rb_browser_source_radio_actions [] =
154 { "BrowserSourceSearchAll", NULL, N_("All"), NULL, N_("Search all fields"), 0 },
155 { "BrowserSourceSearchArtists", NULL, N_("Artists"), NULL, N_("Search artists"), 1 },
156 { "BrowserSourceSearchAlbums", NULL, N_("Albums"), NULL, N_("Search albums"), 2 },
157 { "BrowserSourceSearchTitles", NULL, N_("Titles"), NULL, N_("Search titles"), 3 }
160 static const GtkTargetEntry songs_view_drag_types[] = {
161 { "application/x-rhythmbox-entry", 0, 0 },
162 { "text/uri-list", 0, 1 }
165 enum
167 PROP_0,
168 PROP_SORTING_KEY
171 G_DEFINE_ABSTRACT_TYPE (RBBrowserSource, rb_browser_source, RB_TYPE_SOURCE)
173 static void
174 rb_browser_source_class_init (RBBrowserSourceClass *klass)
176 GObjectClass *object_class = G_OBJECT_CLASS (klass);
177 RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
179 object_class->dispose = rb_browser_source_dispose;
180 object_class->finalize = rb_browser_source_finalize;
181 object_class->constructor = rb_browser_source_constructor;
183 object_class->set_property = rb_browser_source_set_property;
184 object_class->get_property = rb_browser_source_get_property;
186 source_class->impl_can_browse = (RBSourceFeatureFunc) rb_true_function;
187 source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
188 source_class->impl_search = impl_search;
189 source_class->impl_get_entry_view = impl_get_entry_view;
190 source_class->impl_get_property_views = impl_get_property_views;
191 source_class->impl_reset_filters = impl_reset_filters;
192 source_class->impl_song_properties = impl_song_properties;
193 source_class->impl_can_pause = (RBSourceFeatureFunc) rb_true_function;
194 source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
195 source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
196 source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
197 source_class->impl_can_add_to_queue = (RBSourceFeatureFunc) rb_true_function;
198 source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_true_function;
199 source_class->impl_delete = impl_delete;
200 source_class->impl_show_popup = impl_show_popup;
201 source_class->impl_get_search_actions = impl_get_search_actions;
202 source_class->impl_browser_toggled = impl_browser_toggled;
204 klass->impl_get_paned_key = (RBBrowserSourceStringFunc)rb_null_function;
205 klass->impl_has_drop_support = (RBBrowserSourceFeatureFunc) rb_false_function;
206 klass->impl_show_entry_popup = default_show_entry_popup;
208 g_object_class_install_property (object_class,
209 PROP_SORTING_KEY,
210 g_param_spec_string ("sorting-key",
211 "Sorting key",
212 "GConf key for storing sort-order",
213 NULL,
214 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
216 g_type_class_add_private (klass, sizeof (RBBrowserSourcePrivate));
219 static void
220 rb_browser_source_init (RBBrowserSource *source)
222 source->priv = RB_BROWSER_SOURCE_GET_PRIVATE (source);
224 source->priv->search_prop = RHYTHMDB_PROP_SEARCH_MATCH;
226 source->priv->vbox = gtk_vbox_new (FALSE, 5);
228 gtk_container_add (GTK_CONTAINER (source), source->priv->vbox);
231 static void
232 rb_browser_source_dispose (GObject *object)
234 RBBrowserSource *source;
235 source = RB_BROWSER_SOURCE (object);
237 if (source->priv->dispose_has_run) {
238 /* If dispose did already run, return. */
239 return;
241 /* Make sure dispose does not run twice. */
242 source->priv->dispose_has_run = TRUE;
244 if (source->priv->db != NULL) {
245 g_object_unref (source->priv->db);
246 source->priv->db = NULL;
249 if (source->priv->search_text != NULL) {
250 g_free (source->priv->search_text);
251 source->priv->search_text = NULL;
254 if (source->priv->cached_all_query != NULL) {
255 g_object_unref (source->priv->cached_all_query);
256 source->priv->cached_all_query = NULL;
259 G_OBJECT_CLASS (rb_browser_source_parent_class)->dispose (object);
262 static void
263 rb_browser_source_finalize (GObject *object)
265 RBBrowserSource *source;
267 g_return_if_fail (object != NULL);
268 g_return_if_fail (RB_IS_BROWSER_SOURCE (object));
270 source = RB_BROWSER_SOURCE (object);
272 g_return_if_fail (source->priv != NULL);
274 rb_debug ("finalizing browser source");
275 g_free (source->priv->sorting_key);
276 eel_gconf_notification_remove (source->priv->state_browser_notify_id);
277 eel_gconf_notification_remove (source->priv->state_paned_notify_id);
278 eel_gconf_notification_remove (source->priv->state_sorting_notify_id);
280 if (source->priv->action_group != NULL) {
281 g_object_unref (source->priv->action_group);
284 G_OBJECT_CLASS (rb_browser_source_parent_class)->finalize (object);
287 static void
288 rb_browser_source_songs_show_popup_cb (RBEntryView *view,
289 gboolean over_entry,
290 RBBrowserSource *source)
292 if (over_entry) {
293 RBBrowserSourceClass *klass = RB_BROWSER_SOURCE_GET_CLASS (source);
295 klass->impl_show_entry_popup (source);
296 } else {
297 rb_source_show_popup (RB_SOURCE (source));
301 static void
302 default_show_entry_popup (RBBrowserSource *source)
304 _rb_source_show_popup (RB_SOURCE (source), "/BrowserSourceViewPopup");
307 static RhythmDBPropType
308 search_action_to_prop (GtkAction *action)
310 const char *name;
311 RhythmDBPropType prop;
313 name = gtk_action_get_name (action);
315 if (name == NULL) {
316 prop = RHYTHMDB_PROP_SEARCH_MATCH;
317 } else if (strcmp (name, "BrowserSourceSearchAll") == 0) {
318 prop = RHYTHMDB_PROP_SEARCH_MATCH;
319 } else if (strcmp (name, "BrowserSourceSearchArtists") == 0) {
320 prop = RHYTHMDB_PROP_ARTIST_FOLDED;
321 } else if (strcmp (name, "BrowserSourceSearchAlbums") == 0) {
322 prop = RHYTHMDB_PROP_ALBUM_FOLDED;
323 } else if (strcmp (name, "BrowserSourceSearchTitles") == 0) {
324 prop = RHYTHMDB_PROP_TITLE_FOLDED;
325 } else {
326 prop = RHYTHMDB_PROP_SEARCH_MATCH;
329 return prop;
332 static void
333 search_action_changed (GtkRadioAction *action,
334 GtkRadioAction *current,
335 RBShell *shell)
337 gboolean active;
338 RBBrowserSource *source;
340 g_object_get (shell, "selected-source", &source, NULL);
342 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
344 if (active) {
345 /* update query */
346 source->priv->search_prop = search_action_to_prop (GTK_ACTION (current));
347 rb_browser_source_do_query (source, FALSE);
348 rb_source_notify_filter_changed (RB_SOURCE (source));
351 if (source != NULL) {
352 g_object_unref (source);
356 static GObject *
357 rb_browser_source_constructor (GType type,
358 guint n_construct_properties,
359 GObjectConstructParam *construct_properties)
361 RBBrowserSource *source;
362 RBBrowserSourceClass *klass;
363 RBShell *shell;
364 GObject *shell_player;
365 char *browser_key;
366 RhythmDBEntryType entry_type;
368 klass = RB_BROWSER_SOURCE_CLASS (g_type_class_peek (RB_TYPE_BROWSER_SOURCE));
370 source = RB_BROWSER_SOURCE (G_OBJECT_CLASS (rb_browser_source_parent_class)
371 ->constructor (type, n_construct_properties, construct_properties));
373 g_object_get (source,
374 "shell", &shell,
375 "entry-type", &entry_type,
376 NULL);
377 g_object_get (shell, "db", &source->priv->db, NULL);
378 shell_player = rb_shell_get_player (shell);
380 source->priv->action_group = _rb_source_register_action_group (RB_SOURCE (source),
381 "BrowserSourceActions",
382 rb_browser_source_actions,
383 G_N_ELEMENTS (rb_browser_source_actions),
384 shell);
386 /* only add the actions if we haven't already */
387 if (gtk_action_group_get_action (source->priv->action_group,
388 rb_browser_source_radio_actions[0].name) == NULL) {
389 gtk_action_group_add_radio_actions (source->priv->action_group,
390 rb_browser_source_radio_actions,
391 G_N_ELEMENTS (rb_browser_source_radio_actions),
393 (GCallback)search_action_changed,
394 shell);
396 g_object_unref (shell);
398 source->priv->paned = gtk_vpaned_new ();
400 source->priv->browser = rb_library_browser_new (source->priv->db, entry_type);
401 gtk_paned_pack1 (GTK_PANED (source->priv->paned), GTK_WIDGET (source->priv->browser), TRUE, FALSE);
402 gtk_container_child_set (GTK_CONTAINER (source->priv->paned),
403 GTK_WIDGET (source->priv->browser),
404 "resize", FALSE,
405 NULL);
406 g_signal_connect_object (G_OBJECT (source->priv->browser), "notify::output-model",
407 G_CALLBACK (rb_browser_source_browser_changed_cb),
408 source, 0);
410 /* set up songs tree view */
411 source->priv->songs = rb_entry_view_new (source->priv->db, shell_player,
412 source->priv->sorting_key,
413 TRUE, FALSE);
415 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE);
416 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TITLE, TRUE);
417 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_GENRE, FALSE);
418 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
419 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
420 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_YEAR, FALSE);
421 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_DURATION, FALSE);
422 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_QUALITY, FALSE);
423 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);
425 g_signal_connect_object (G_OBJECT (source->priv->songs), "show_popup",
426 G_CALLBACK (rb_browser_source_songs_show_popup_cb), source, 0);
427 g_signal_connect_object (G_OBJECT (source->priv->songs),
428 "sort-order-changed",
429 G_CALLBACK (songs_view_sort_order_changed_cb),
430 source, 0);
432 if (source->priv->sorting_key) {
433 source->priv->state_sorting_notify_id =
434 eel_gconf_notification_add (source->priv->sorting_key,
435 (GConfClientNotifyFunc) rb_browser_source_state_pref_changed,
436 source);
438 if (rb_browser_source_get_paned_key (source)) {
439 source->priv->state_paned_notify_id =
440 eel_gconf_notification_add (rb_browser_source_get_paned_key (source),
441 (GConfClientNotifyFunc) rb_browser_source_state_pref_changed,
442 source);
444 browser_key = rb_source_get_browser_key (RB_SOURCE (source));
445 if (browser_key) {
446 source->priv->state_browser_notify_id =
447 eel_gconf_notification_add (browser_key,
448 (GConfClientNotifyFunc) rb_browser_source_state_pref_changed,
449 source);
450 g_free (browser_key);
453 rb_browser_source_state_prefs_sync (source);
455 if (rb_browser_source_has_drop_support (source)) {
456 gtk_drag_dest_set (GTK_WIDGET (source->priv->songs),
457 GTK_DEST_DEFAULT_ALL,
458 songs_view_drag_types, G_N_ELEMENTS (songs_view_drag_types),
459 GDK_ACTION_COPY | GDK_ACTION_MOVE); /* really accept move actions? */
461 /* set up drag and drop for the song tree view.
462 * we don't use RBEntryView's DnD support because it does too much.
463 * we just want to be able to drop songs in to add them to the
464 * library.
466 g_signal_connect_object (G_OBJECT (source->priv->songs),
467 "drag_data_received",
468 G_CALLBACK (songs_view_drag_data_received_cb),
469 source, 0);
472 /* this gets emitted when the paned thingie is moved */
473 g_signal_connect_object (G_OBJECT (source->priv->songs),
474 "size_allocate",
475 G_CALLBACK (paned_size_allocate_cb),
476 source, 0);
478 gtk_paned_pack2 (GTK_PANED (source->priv->paned), GTK_WIDGET (source->priv->songs), TRUE, FALSE);
480 gtk_box_pack_start_defaults (GTK_BOX (source->priv->vbox), source->priv->paned);
481 gtk_widget_show_all (GTK_WIDGET (source));
483 source->priv->cached_all_query = rhythmdb_query_model_new_empty (source->priv->db);
484 rb_library_browser_set_model (source->priv->browser, source->priv->cached_all_query, TRUE);
485 rhythmdb_do_full_query_async (source->priv->db,
486 RHYTHMDB_QUERY_RESULTS (source->priv->cached_all_query),
487 RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, entry_type,
488 RHYTHMDB_QUERY_END);
489 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
491 return G_OBJECT (source);
494 static void
495 rb_browser_source_set_property (GObject *object,
496 guint prop_id,
497 const GValue *value,
498 GParamSpec *pspec)
500 RBBrowserSource *source = RB_BROWSER_SOURCE (object);
502 switch (prop_id) {
503 case PROP_SORTING_KEY:
504 g_free (source->priv->sorting_key);
505 source->priv->sorting_key = g_strdup (g_value_get_string (value));
506 break;
507 default:
508 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
509 break;
513 static void
514 rb_browser_source_get_property (GObject *object,
515 guint prop_id,
516 GValue *value,
517 GParamSpec *pspec)
519 RBBrowserSource *source = RB_BROWSER_SOURCE (object);
521 switch (prop_id) {
522 case PROP_SORTING_KEY:
523 g_value_set_string (value, source->priv->sorting_key);
524 break;
525 default:
526 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
527 break;
531 static void
532 rb_browser_source_cmd_choose_genre (GtkAction *action,
533 RBShell *shell)
535 GList *props;
536 RBBrowserSource *source;
537 RBPropertyView *view;
539 rb_debug ("choosing genre");
541 g_object_get (G_OBJECT (shell), "selected-source", &source, NULL);
542 props = rb_source_gather_selected_properties (RB_SOURCE (source), RHYTHMDB_PROP_GENRE);
543 view = rb_library_browser_get_property_view (source->priv->browser, RHYTHMDB_PROP_GENRE);
544 if (view)
545 rb_property_view_set_selection (view, props);
547 rb_list_deep_free (props);
548 g_object_unref (source);
551 static void
552 rb_browser_source_cmd_choose_artist (GtkAction *action,
553 RBShell *shell)
555 GList *props;
556 RBBrowserSource *source;
557 RBPropertyView *view;
559 rb_debug ("choosing artist");
561 g_object_get (shell, "selected-source", &source, NULL);
562 props = rb_source_gather_selected_properties (RB_SOURCE (source), RHYTHMDB_PROP_ARTIST);
563 view = rb_library_browser_get_property_view (source->priv->browser, RHYTHMDB_PROP_ARTIST);
564 if (view)
565 rb_property_view_set_selection (view, props);
567 rb_list_deep_free (props);
568 g_object_unref (source);
571 static void
572 rb_browser_source_cmd_choose_album (GtkAction *action,
573 RBShell *shell)
575 GList *props;
576 RBBrowserSource *source;
577 RBPropertyView *view;
579 rb_debug ("choosing album");
581 g_object_get (G_OBJECT (shell), "selected-source", &source, NULL);
582 props = rb_source_gather_selected_properties (RB_SOURCE (source), RHYTHMDB_PROP_ALBUM);
583 view = rb_library_browser_get_property_view (source->priv->browser, RHYTHMDB_PROP_ALBUM);
584 if (view)
585 rb_property_view_set_selection (view, props);
587 rb_list_deep_free (props);
588 g_object_unref (source);
591 static void
592 songs_view_sort_order_changed_cb (RBEntryView *view, RBBrowserSource *source)
594 rb_debug ("sort order changed");
595 rb_entry_view_resort_model (view);
598 static void
599 impl_search (RBSource *asource, const char *search_text)
601 RBBrowserSource *source = RB_BROWSER_SOURCE (asource);
602 char *old_search_text = NULL;
603 gboolean subset = FALSE;
604 const char *debug_search_text;
606 if (search_text != NULL && search_text[0] == '\0')
607 search_text = NULL;
609 if (search_text == NULL && source->priv->search_text == NULL)
610 return;
611 if (search_text != NULL && source->priv->search_text != NULL
612 && !strcmp (search_text, source->priv->search_text))
613 return;
615 old_search_text = source->priv->search_text;
616 if (search_text == NULL) {
617 source->priv->search_text = NULL;
618 debug_search_text = "(NULL)";
619 } else {
620 source->priv->search_text = g_strdup (search_text);
621 debug_search_text = source->priv->search_text;
623 if (old_search_text != NULL)
624 subset = (g_str_has_prefix (source->priv->search_text, old_search_text));
626 g_free (old_search_text);
628 /* we can't do subset searches until the original query is complete, because they
629 * reuse the query model.
631 if (source->priv->query_active && subset) {
632 rb_debug ("deferring search for \"%s\" until query completion", debug_search_text);
633 source->priv->search_on_completion = TRUE;
634 } else {
635 rb_debug ("doing search for \"%s\"", debug_search_text);
636 rb_browser_source_do_query (source, subset);
640 static RBEntryView *
641 impl_get_entry_view (RBSource *asource)
643 RBBrowserSource *source = RB_BROWSER_SOURCE (asource);
645 return source->priv->songs;
648 static GList *
649 impl_get_property_views (RBSource *asource)
651 GList *ret;
652 RBBrowserSource *source = RB_BROWSER_SOURCE (asource);
654 ret = rb_library_browser_get_property_views (source->priv->browser);
655 return ret;
658 static void
659 impl_reset_filters (RBSource *asource)
661 RBBrowserSource *source = RB_BROWSER_SOURCE (asource);
662 gboolean changed = FALSE;
664 rb_debug ("Resetting search filters");
666 if (rb_library_browser_reset (source->priv->browser))
667 changed = TRUE;
669 if (source->priv->search_text != NULL)
670 changed = TRUE;
671 g_free (source->priv->search_text);
672 source->priv->search_text = NULL;
674 if (changed)
675 rb_browser_source_do_query (source, FALSE);
678 static void
679 impl_delete (RBSource *asource)
681 RBBrowserSource *source = RB_BROWSER_SOURCE (asource);
682 GList *sel, *tem;
684 sel = rb_entry_view_get_selected_entries (source->priv->songs);
685 for (tem = sel; tem != NULL; tem = tem->next) {
686 rhythmdb_entry_delete (source->priv->db, tem->data);
687 rhythmdb_commit (source->priv->db);
689 g_list_foreach (sel, (GFunc)rhythmdb_entry_unref, NULL);
690 g_list_free (sel);
693 static void
694 impl_song_properties (RBSource *asource)
696 RBBrowserSource *source = RB_BROWSER_SOURCE (asource);
697 GtkWidget *song_info = NULL;
699 g_return_if_fail (source->priv->songs != NULL);
701 song_info = rb_song_info_new (asource, NULL);
703 g_return_if_fail (song_info != NULL);
705 if (song_info)
706 gtk_widget_show_all (song_info);
707 else
708 rb_debug ("failed to create dialog, or no selection!");
711 static void
712 paned_size_allocate_cb (GtkWidget *widget,
713 GtkAllocation *allocation,
714 RBBrowserSource *source)
716 const char *key = rb_browser_source_get_paned_key (source);;
718 /* save state */
719 rb_debug ("paned size allocate");
720 if (key)
721 eel_gconf_set_integer (key, gtk_paned_get_position (GTK_PANED (source->priv->paned)));
724 static void
725 rb_browser_source_state_pref_changed (GConfClient *client,
726 guint cnxn_id,
727 GConfEntry *entry,
728 RBBrowserSource *source)
730 rb_debug ("state prefs changed");
731 rb_browser_source_state_prefs_sync (source);
734 static void
735 rb_browser_source_state_prefs_sync (RBBrowserSource *source)
737 const char *paned_key;
738 char *browser_key;
740 rb_debug ("syncing state");
741 paned_key = rb_browser_source_get_paned_key (source);
742 if (paned_key)
743 gtk_paned_set_position (GTK_PANED (source->priv->paned),
744 eel_gconf_get_integer (paned_key));
746 browser_key = rb_source_get_browser_key (RB_SOURCE (source));
747 if (browser_key && eel_gconf_get_boolean (browser_key)) {
748 gtk_widget_show (GTK_WIDGET (source->priv->browser));
749 } else {
750 gtk_widget_hide (GTK_WIDGET (source->priv->browser));
752 g_free (browser_key);
755 static GList *
756 impl_get_search_actions (RBSource *source)
758 GList *actions = NULL;
760 actions = g_list_prepend (actions, g_strdup ("BrowserSourceSearchTitles"));
761 actions = g_list_prepend (actions, g_strdup ("BrowserSourceSearchAlbums"));
762 actions = g_list_prepend (actions, g_strdup ("BrowserSourceSearchArtists"));
763 actions = g_list_prepend (actions, g_strdup ("BrowserSourceSearchAll"));
765 return actions;
768 static void
769 impl_browser_toggled (RBSource *asource, gboolean disclosed)
771 RBBrowserSource *source = RB_BROWSER_SOURCE (asource);
773 if (disclosed) {
774 gtk_widget_show (GTK_WIDGET (source->priv->browser));
775 } else {
776 gtk_widget_hide (GTK_WIDGET (source->priv->browser));
777 rb_library_browser_reset (source->priv->browser);
781 static gboolean
782 impl_show_popup (RBSource *source)
784 _rb_source_show_popup (RB_SOURCE (source), "/BrowserSourcePopup");
785 return TRUE;
788 const char *
789 rb_browser_source_get_paned_key (RBBrowserSource *source)
791 RBBrowserSourceClass *klass = RB_BROWSER_SOURCE_GET_CLASS (source);
793 if (klass->impl_get_paned_key)
794 return klass->impl_get_paned_key (source);
795 else
796 return NULL;
799 gboolean
800 rb_browser_source_has_drop_support (RBBrowserSource *source)
802 RBBrowserSourceClass *klass = RB_BROWSER_SOURCE_GET_CLASS (source);
804 return klass->impl_has_drop_support (source);
807 static void
808 songs_view_drag_data_received_cb (GtkWidget *widget,
809 GdkDragContext *dc,
810 gint x, gint y,
811 GtkSelectionData *selection_data,
812 guint info, guint time,
813 RBBrowserSource *source)
815 rb_debug ("data dropped on the library source song view");
816 rb_source_receive_drag (RB_SOURCE (source), selection_data);
819 static void
820 rb_browser_source_browser_changed_cb (RBLibraryBrowser *browser,
821 GParamSpec *pspec,
822 RBBrowserSource *source)
824 RhythmDBQueryModel *query_model;
826 g_object_get (browser, "output-model", &query_model, NULL);
827 rb_entry_view_set_model (source->priv->songs, query_model);
828 g_object_set (source, "query-model", query_model, NULL);
829 g_object_unref (query_model);
831 rb_source_notify_filter_changed (RB_SOURCE (source));
834 static void
835 rb_browser_source_query_complete_cb (RhythmDBQueryModel *query_model,
836 RBBrowserSource *source)
838 source->priv->query_active = FALSE;
839 if (source->priv->search_on_completion) {
840 rb_debug ("performing deferred search");
841 source->priv->search_on_completion = FALSE;
842 /* this is only done for subset queries */
843 rb_browser_source_do_query (source, TRUE);
847 static void
848 rb_browser_source_do_query (RBBrowserSource *source, gboolean subset)
850 RhythmDBQueryModel *query_model;
851 GPtrArray *query;
852 RhythmDBEntryType entry_type;
854 /* use the cached 'all' query to optimise the no-search case */
855 if (!source->priv->search_text) {
856 rb_library_browser_set_model (source->priv->browser,
857 source->priv->cached_all_query,
858 FALSE);
859 return;
862 g_object_get (source, "entry-type", &entry_type, NULL);
863 query = rhythmdb_query_parse (source->priv->db,
864 RHYTHMDB_QUERY_PROP_EQUALS,
865 RHYTHMDB_PROP_TYPE,
866 entry_type,
867 RHYTHMDB_QUERY_PROP_LIKE,
868 source->priv->search_prop,
869 source->priv->search_text,
870 RHYTHMDB_QUERY_END);
871 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
873 if (subset) {
874 /* if we're appending text to an existing search string, the results will be a subset
875 * of the existing results, so rather than doing a whole new query, we can copy the
876 * results to a new query model with a more restrictive query.
878 RhythmDBQueryModel *old;
879 g_object_get (source->priv->browser, "input-model", &old, NULL);
881 query_model = rhythmdb_query_model_new_empty (source->priv->db);
882 g_object_set (query_model, "query", query, NULL);
883 rhythmdb_query_model_copy_contents (query_model, old);
884 g_object_unref (old);
886 rb_library_browser_set_model (source->priv->browser, query_model, FALSE);
887 g_object_unref (query_model);
889 } else {
890 /* otherwise build a query based on the search text and feed it to the browser */
891 query_model = rhythmdb_query_model_new_empty (source->priv->db);
892 rb_library_browser_set_model (source->priv->browser, query_model, TRUE);
893 source->priv->query_active = TRUE;
894 source->priv->search_on_completion = FALSE;
895 g_signal_connect_object (query_model,
896 "complete", G_CALLBACK (rb_browser_source_query_complete_cb),
897 source, 0);
898 rhythmdb_do_full_query_async_parsed (source->priv->db,
899 RHYTHMDB_QUERY_RESULTS (query_model),
900 query);
901 g_object_unref (query_model);
904 rhythmdb_query_free (query);