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.
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"
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_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
,
62 static void rb_browser_source_get_property (GObject
*object
,
66 static void rb_browser_source_cmd_choose_genre (GtkAction
*action
,
68 static void rb_browser_source_cmd_choose_artist (GtkAction
*action
,
70 static void rb_browser_source_cmd_choose_album (GtkAction
*action
,
72 static void songs_view_sort_order_changed_cb (RBEntryView
*view
, RBBrowserSource
*source
);
73 static void rb_browser_source_browser_changed_cb (RBLibraryBrowser
*entry
,
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
,
81 RBBrowserSource
*source
);
83 static void paned_size_allocate_cb (GtkWidget
*widget
,
84 GtkAllocation
*allocation
,
85 RBBrowserSource
*source
);
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
,
103 GtkSelectionData
*data
,
104 guint info
, guint time
,
105 RBBrowserSource
*source
);
106 static void rb_browser_source_do_query (RBBrowserSource
*source
,
109 struct RBBrowserSourcePrivate
113 RBLibraryBrowser
*browser
;
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
;
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 }
171 G_DEFINE_ABSTRACT_TYPE (RBBrowserSource
, rb_browser_source
, RB_TYPE_SOURCE
)
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
,
210 g_param_spec_string ("sorting-key",
212 "GConf key for storing sort-order",
214 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
216 g_type_class_add_private (klass
, sizeof (RBBrowserSourcePrivate
));
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
);
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. */
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
);
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
);
288 rb_browser_source_songs_show_popup_cb (RBEntryView
*view
,
290 RBBrowserSource
*source
)
293 RBBrowserSourceClass
*klass
= RB_BROWSER_SOURCE_GET_CLASS (source
);
295 klass
->impl_show_entry_popup (source
);
297 rb_source_show_popup (RB_SOURCE (source
));
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
)
311 RhythmDBPropType prop
;
313 name
= gtk_action_get_name (action
);
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
;
326 prop
= RHYTHMDB_PROP_SEARCH_MATCH
;
333 search_action_changed (GtkRadioAction
*action
,
334 GtkRadioAction
*current
,
338 RBBrowserSource
*source
;
340 g_object_get (shell
, "selected-source", &source
, NULL
);
342 active
= gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current
));
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
);
357 rb_browser_source_constructor (GType type
,
358 guint n_construct_properties
,
359 GObjectConstructParam
*construct_properties
)
361 RBBrowserSource
*source
;
362 RBBrowserSourceClass
*klass
;
364 GObject
*shell_player
;
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
,
375 "entry-type", &entry_type
,
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
),
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
,
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
),
406 g_signal_connect_object (G_OBJECT (source
->priv
->browser
), "notify::output-model",
407 G_CALLBACK (rb_browser_source_browser_changed_cb
),
410 /* set up songs tree view */
411 source
->priv
->songs
= rb_entry_view_new (source
->priv
->db
, shell_player
,
412 source
->priv
->sorting_key
,
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
),
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
,
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
,
444 browser_key
= rb_source_get_browser_key (RB_SOURCE (source
));
446 source
->priv
->state_browser_notify_id
=
447 eel_gconf_notification_add (browser_key
,
448 (GConfClientNotifyFunc
) rb_browser_source_state_pref_changed
,
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
466 g_signal_connect_object (G_OBJECT (source
->priv
->songs
),
467 "drag_data_received",
468 G_CALLBACK (songs_view_drag_data_received_cb
),
472 /* this gets emitted when the paned thingie is moved */
473 g_signal_connect_object (G_OBJECT (source
->priv
->songs
),
475 G_CALLBACK (paned_size_allocate_cb
),
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
,
489 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
491 return G_OBJECT (source
);
495 rb_browser_source_set_property (GObject
*object
,
500 RBBrowserSource
*source
= RB_BROWSER_SOURCE (object
);
503 case PROP_SORTING_KEY
:
504 g_free (source
->priv
->sorting_key
);
505 source
->priv
->sorting_key
= g_strdup (g_value_get_string (value
));
508 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
514 rb_browser_source_get_property (GObject
*object
,
519 RBBrowserSource
*source
= RB_BROWSER_SOURCE (object
);
522 case PROP_SORTING_KEY
:
523 g_value_set_string (value
, source
->priv
->sorting_key
);
526 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
532 rb_browser_source_cmd_choose_genre (GtkAction
*action
,
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
);
545 rb_property_view_set_selection (view
, props
);
547 rb_list_deep_free (props
);
548 g_object_unref (source
);
552 rb_browser_source_cmd_choose_artist (GtkAction
*action
,
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
);
565 rb_property_view_set_selection (view
, props
);
567 rb_list_deep_free (props
);
568 g_object_unref (source
);
572 rb_browser_source_cmd_choose_album (GtkAction
*action
,
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
);
585 rb_property_view_set_selection (view
, props
);
587 rb_list_deep_free (props
);
588 g_object_unref (source
);
592 songs_view_sort_order_changed_cb (RBEntryView
*view
, RBBrowserSource
*source
)
594 rb_debug ("sort order changed");
595 rb_entry_view_resort_model (view
);
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')
609 if (search_text
== NULL
&& source
->priv
->search_text
== NULL
)
611 if (search_text
!= NULL
&& source
->priv
->search_text
!= NULL
612 && !strcmp (search_text
, source
->priv
->search_text
))
615 old_search_text
= source
->priv
->search_text
;
616 if (search_text
== NULL
) {
617 source
->priv
->search_text
= NULL
;
618 debug_search_text
= "(NULL)";
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
;
635 rb_debug ("doing search for \"%s\"", debug_search_text
);
636 rb_browser_source_do_query (source
, subset
);
641 impl_get_entry_view (RBSource
*asource
)
643 RBBrowserSource
*source
= RB_BROWSER_SOURCE (asource
);
645 return source
->priv
->songs
;
649 impl_get_property_views (RBSource
*asource
)
652 RBBrowserSource
*source
= RB_BROWSER_SOURCE (asource
);
654 ret
= rb_library_browser_get_property_views (source
->priv
->browser
);
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
))
669 if (source
->priv
->search_text
!= NULL
)
671 g_free (source
->priv
->search_text
);
672 source
->priv
->search_text
= NULL
;
675 rb_browser_source_do_query (source
, FALSE
);
679 impl_delete (RBSource
*asource
)
681 RBBrowserSource
*source
= RB_BROWSER_SOURCE (asource
);
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
);
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
);
706 gtk_widget_show_all (song_info
);
708 rb_debug ("failed to create dialog, or no selection!");
712 paned_size_allocate_cb (GtkWidget
*widget
,
713 GtkAllocation
*allocation
,
714 RBBrowserSource
*source
)
716 const char *key
= rb_browser_source_get_paned_key (source
);;
719 rb_debug ("paned size allocate");
721 eel_gconf_set_integer (key
, gtk_paned_get_position (GTK_PANED (source
->priv
->paned
)));
725 rb_browser_source_state_pref_changed (GConfClient
*client
,
728 RBBrowserSource
*source
)
730 rb_debug ("state prefs changed");
731 rb_browser_source_state_prefs_sync (source
);
735 rb_browser_source_state_prefs_sync (RBBrowserSource
*source
)
737 const char *paned_key
;
740 rb_debug ("syncing state");
741 paned_key
= rb_browser_source_get_paned_key (source
);
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
));
750 gtk_widget_hide (GTK_WIDGET (source
->priv
->browser
));
752 g_free (browser_key
);
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"));
769 impl_browser_toggled (RBSource
*asource
, gboolean disclosed
)
771 RBBrowserSource
*source
= RB_BROWSER_SOURCE (asource
);
774 gtk_widget_show (GTK_WIDGET (source
->priv
->browser
));
776 gtk_widget_hide (GTK_WIDGET (source
->priv
->browser
));
777 rb_library_browser_reset (source
->priv
->browser
);
782 impl_show_popup (RBSource
*source
)
784 _rb_source_show_popup (RB_SOURCE (source
), "/BrowserSourcePopup");
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
);
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
);
808 songs_view_drag_data_received_cb (GtkWidget
*widget
,
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
);
820 rb_browser_source_browser_changed_cb (RBLibraryBrowser
*browser
,
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
));
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
);
848 rb_browser_source_do_query (RBBrowserSource
*source
, gboolean subset
)
850 RhythmDBQueryModel
*query_model
;
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
,
862 g_object_get (source
, "entry-type", &entry_type
, NULL
);
863 query
= rhythmdb_query_parse (source
->priv
->db
,
864 RHYTHMDB_QUERY_PROP_EQUALS
,
867 RHYTHMDB_QUERY_PROP_LIKE
,
868 source
->priv
->search_prop
,
869 source
->priv
->search_text
,
871 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
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
);
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
),
898 rhythmdb_do_full_query_async_parsed (source
->priv
->db
,
899 RHYTHMDB_QUERY_RESULTS (query_model
),
901 g_object_unref (query_model
);
904 rhythmdb_query_free (query
);