2006-12-13 Jonathan Matthew <jonathan@kaolin.wh9.net>
[rhythmbox.git] / sources / rb-static-playlist-source.c
blob2f3591b1b6b6fd74addd6ccc276de03b98b1e527
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
4 * Copyright (C) 2003 Colin Walters <walters@gnome.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "config.h"
24 #include <string.h>
26 #include <libxml/tree.h>
27 #include <glib/gi18n.h>
28 #include <gtk/gtk.h>
29 #include <libgnomevfs/gnome-vfs-uri.h>
31 #include "rb-static-playlist-source.h"
32 #include "rb-library-browser.h"
33 #include "rb-util.h"
34 #include "rb-debug.h"
35 #include "rb-stock-icons.h"
36 #include "rb-file-helpers.h"
37 #include "rb-playlist-xml.h"
39 static GObject *rb_static_playlist_source_constructor (GType type, guint n_construct_properties,
40 GObjectConstructParam *construct_properties);
41 static void rb_static_playlist_source_dispose (GObject *object);
42 static void rb_static_playlist_source_finalize (GObject *object);
44 /* source methods */
45 static GList * impl_cut (RBSource *source);
46 static void impl_paste (RBSource *asource, GList *entries);
47 static void impl_delete (RBSource *source);
48 static void impl_search (RBSource *asource, const char *search_text);
49 static void impl_browser_toggled (RBSource *source, gboolean enabled);
50 static void impl_reset_filters (RBSource *asource);
51 static gboolean impl_receive_drag (RBSource *source, GtkSelectionData *data);
52 static GList *impl_get_search_actions (RBSource *source);
53 static guint impl_want_uri (RBSource *source, const char *uri);
55 static GPtrArray *construct_query_from_selection (RBStaticPlaylistSource *source);
57 /* playlist methods */
58 static void impl_save_contents_to_xml (RBPlaylistSource *source,
59 xmlNodePtr node);
61 /* browser stuff */
62 static GList *impl_get_property_views (RBSource *source);
63 void rb_static_playlist_source_browser_views_activated_cb (GtkWidget *widget,
64 RBStaticPlaylistSource *source);
65 static void rb_static_playlist_source_browser_changed_cb (RBLibraryBrowser *entry,
66 GParamSpec *pspec,
67 RBStaticPlaylistSource *source);
69 static void rb_static_playlist_source_do_query (RBStaticPlaylistSource *source);
71 static void rb_static_playlist_source_add_id_list (RBStaticPlaylistSource *source,
72 GList *list);
73 static void rb_static_playlist_source_add_uri_list (RBStaticPlaylistSource *source,
74 GList *list);
75 static void rb_static_playlist_source_row_inserted (GtkTreeModel *model,
76 GtkTreePath *path,
77 GtkTreeIter *iter,
78 RBStaticPlaylistSource *source);
79 static void rb_static_playlist_source_non_entry_dropped (GtkTreeModel *model,
80 const char *uri,
81 int position,
82 RBStaticPlaylistSource *source);
84 static void search_action_changed (GtkRadioAction *action,
85 GtkRadioAction *current,
86 RBShell *shell);
88 static GtkRadioActionEntry rb_static_playlist_source_radio_actions [] =
90 { "StaticPlaylistSearchAll", NULL, N_("All"), NULL, N_("Search all fields"), 0 },
91 { "StaticPlaylistSearchArtists", NULL, N_("Artists"), NULL, N_("Search artists"), 1 },
92 { "StaticPlaylistSearchAlbums", NULL, N_("Albums"), NULL, N_("Search albums"), 2 },
93 { "StaticPlaylistSearchTitles", NULL, N_("Titles"), NULL, N_("Search titles"), 3 }
96 G_DEFINE_TYPE (RBStaticPlaylistSource, rb_static_playlist_source, RB_TYPE_PLAYLIST_SOURCE)
97 #define RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), \
98 RB_TYPE_STATIC_PLAYLIST_SOURCE, \
99 RBStaticPlaylistSourcePrivate))
101 typedef struct
103 RhythmDBQueryModel *base_model;
104 RhythmDBQueryModel *filter_model;
106 GtkWidget *paned;
107 RBLibraryBrowser *browser;
108 gboolean browser_shown;
110 char *search_text;
112 GtkActionGroup *action_group;
113 RhythmDBPropType search_prop;
114 gboolean dispose_has_run;
115 } RBStaticPlaylistSourcePrivate;
117 static gpointer playlist_pixbuf = NULL;
119 static void
120 rb_static_playlist_source_class_init (RBStaticPlaylistSourceClass *klass)
122 GObjectClass *object_class = G_OBJECT_CLASS (klass);
123 RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
124 RBPlaylistSourceClass *playlist_class = RB_PLAYLIST_SOURCE_CLASS (klass);
126 object_class->constructor = rb_static_playlist_source_constructor;
127 object_class->dispose = rb_static_playlist_source_dispose;
128 object_class->finalize = rb_static_playlist_source_finalize;
130 source_class->impl_can_cut = (RBSourceFeatureFunc) rb_true_function;
131 source_class->impl_can_paste = (RBSourceFeatureFunc) rb_true_function;
132 source_class->impl_can_delete = (RBSourceFeatureFunc) rb_true_function;
133 source_class->impl_cut = impl_cut;
134 source_class->impl_paste = impl_paste;
135 source_class->impl_delete = impl_delete;
136 source_class->impl_receive_drag = impl_receive_drag;
137 source_class->impl_can_search = (RBSourceFeatureFunc) rb_true_function;
138 source_class->impl_search = impl_search;
139 source_class->impl_reset_filters = impl_reset_filters;
140 source_class->impl_can_browse = (RBSourceFeatureFunc) rb_true_function;
141 source_class->impl_browser_toggled = impl_browser_toggled;
142 source_class->impl_get_property_views = impl_get_property_views;
143 source_class->impl_get_search_actions = impl_get_search_actions;
144 source_class->impl_want_uri = impl_want_uri;
146 playlist_class->impl_save_contents_to_xml = impl_save_contents_to_xml;
148 g_type_class_add_private (klass, sizeof (RBStaticPlaylistSourcePrivate));
151 static void
152 rb_static_playlist_source_init (RBStaticPlaylistSource *source)
154 if (playlist_pixbuf == NULL) {
155 gint size;
156 gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &size, NULL);
157 playlist_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
158 GNOME_MEDIA_PLAYLIST,
159 size,
160 0, NULL);
161 if (playlist_pixbuf) {
162 g_object_add_weak_pointer (playlist_pixbuf,
163 (gpointer *) &playlist_pixbuf);
165 rb_source_set_pixbuf (RB_SOURCE (source), playlist_pixbuf);
167 /* drop the initial reference to the icon */
168 g_object_unref (playlist_pixbuf);
170 } else {
171 rb_source_set_pixbuf (RB_SOURCE (source), playlist_pixbuf);
175 static void
176 rb_static_playlist_source_dispose (GObject *object)
178 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (object);
180 if (priv->dispose_has_run) {
181 /* If dispose did already run, return. */
182 rb_debug ("Dispose has already run for static playlist source %p", object);
183 return;
185 /* Make sure dispose does not run twice. */
186 priv->dispose_has_run = TRUE;
188 rb_debug ("Disposing static playlist source %p", object);
190 if (priv->base_model != NULL) {
191 g_object_unref (priv->base_model);
192 priv->base_model = NULL;
195 if (priv->filter_model != NULL) {
196 g_object_unref (priv->filter_model);
199 G_OBJECT_CLASS (rb_static_playlist_source_parent_class)->dispose (object);
202 static void
203 rb_static_playlist_source_finalize (GObject *object)
205 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (object);
207 rb_debug ("Finalizing static playlist source %p", object);
209 g_free (priv->search_text);
211 if (priv->action_group != NULL) {
212 g_object_unref (priv->action_group);
215 G_OBJECT_CLASS (rb_static_playlist_source_parent_class)->finalize (object);
218 static GObject *
219 rb_static_playlist_source_constructor (GType type,
220 guint n_construct_properties,
221 GObjectConstructParam *construct_properties)
223 GObjectClass *parent_class = G_OBJECT_CLASS (rb_static_playlist_source_parent_class);
224 RBStaticPlaylistSource *source = RB_STATIC_PLAYLIST_SOURCE (
225 parent_class->constructor (type, n_construct_properties, construct_properties));
226 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
227 RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source);
228 RBEntryView *songs;
229 RBShell *shell;
230 RhythmDBEntryType entry_type;
232 priv->base_model = rb_playlist_source_get_query_model (RB_PLAYLIST_SOURCE (psource));
233 g_object_set (priv->base_model, "show-hidden", TRUE, NULL);
234 g_object_ref (priv->base_model);
236 priv->paned = gtk_vpaned_new ();
238 g_object_get (source, "shell", &shell, NULL);
239 priv->action_group = _rb_source_register_action_group (RB_SOURCE (source),
240 "StaticPlaylistActions",
241 NULL, 0,
242 shell);
243 gtk_action_group_add_radio_actions (priv->action_group,
244 rb_static_playlist_source_radio_actions,
245 G_N_ELEMENTS (rb_static_playlist_source_radio_actions),
247 (GCallback)search_action_changed,
248 shell);
249 priv->search_prop = RHYTHMDB_PROP_SEARCH_MATCH;
251 g_object_unref (shell);
253 g_object_get (source, "entry-type", &entry_type, NULL);
254 priv->browser = rb_library_browser_new (rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source)),
255 entry_type);
256 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
257 gtk_paned_pack1 (GTK_PANED (priv->paned), GTK_WIDGET (priv->browser), TRUE, FALSE);
258 g_signal_connect_object (priv->browser, "notify::output-model",
259 G_CALLBACK (rb_static_playlist_source_browser_changed_cb),
260 source, 0);
262 rb_library_browser_set_model (priv->browser, priv->base_model, FALSE);
263 rb_static_playlist_source_do_query (source);
265 /* reparent the entry view */
266 songs = rb_source_get_entry_view (RB_SOURCE (source));
267 g_object_ref (songs);
268 gtk_container_remove (GTK_CONTAINER (source), GTK_WIDGET (songs));
269 gtk_paned_pack2 (GTK_PANED (priv->paned), GTK_WIDGET (songs), TRUE, FALSE);
270 gtk_container_add (GTK_CONTAINER (source), priv->paned);
271 g_object_unref (songs);
273 /* watch these to find out when things are dropped into the entry view */
274 g_signal_connect_object (priv->base_model, "row-inserted",
275 G_CALLBACK (rb_static_playlist_source_row_inserted),
276 source, 0);
277 g_signal_connect_object (priv->base_model, "non-entry-dropped",
278 G_CALLBACK (rb_static_playlist_source_non_entry_dropped),
279 source, 0);
281 gtk_widget_show_all (GTK_WIDGET (source));
283 return G_OBJECT (source);
286 RBSource *
287 rb_static_playlist_source_new (RBShell *shell, const char *name, gboolean local, RhythmDBEntryType entry_type)
289 if (name == NULL)
290 name = "";
292 return RB_SOURCE (g_object_new (RB_TYPE_STATIC_PLAYLIST_SOURCE,
293 "name", name,
294 "shell", shell,
295 "is-local", local,
296 "entry-type", entry_type,
297 "sourcelist-group", RB_SOURCELIST_GROUP_PERSISTANT,
298 NULL));
301 void
302 rb_static_playlist_source_load_from_xml (RBStaticPlaylistSource *source, xmlNodePtr node)
304 xmlNodePtr child;
306 for (child = node->children; child; child = child->next) {
307 xmlChar *location;
309 if (xmlNodeIsText (child))
310 continue;
312 if (xmlStrcmp (child->name, RB_PLAYLIST_LOCATION))
313 continue;
315 location = xmlNodeGetContent (child);
316 rb_static_playlist_source_add_location (source,
317 (char *) location, -1);
318 xmlFree (location);
322 RBSource *
323 rb_static_playlist_source_new_from_xml (RBShell *shell, xmlNodePtr node)
325 RBSource *psource = rb_static_playlist_source_new (shell,
326 NULL,
327 TRUE,
328 RHYTHMDB_ENTRY_TYPE_SONG);
329 RBStaticPlaylistSource *source = RB_STATIC_PLAYLIST_SOURCE (psource);
331 rb_static_playlist_source_load_from_xml (source, node);
333 return RB_SOURCE (source);
336 static GList *
337 impl_cut (RBSource *asource)
339 RBStaticPlaylistSource *source = RB_STATIC_PLAYLIST_SOURCE (asource);
340 RBEntryView *songs = rb_source_get_entry_view (asource);
341 GList *sel = rb_entry_view_get_selected_entries (songs);
342 GList *tem;
344 for (tem = sel; tem; tem = tem->next)
345 rb_static_playlist_source_remove_entry (source, (RhythmDBEntry *) tem->data);
347 return sel;
350 static void
351 impl_paste (RBSource *asource, GList *entries)
353 RBStaticPlaylistSource *source = RB_STATIC_PLAYLIST_SOURCE (asource);
355 for (; entries; entries = g_list_next (entries))
356 rb_static_playlist_source_add_entry (source, entries->data, -1);
359 static void
360 impl_delete (RBSource *asource)
362 RBEntryView *songs = rb_source_get_entry_view (asource);
363 RBStaticPlaylistSource *source = RB_STATIC_PLAYLIST_SOURCE (asource);
364 GList *sel, *tem;
366 sel = rb_entry_view_get_selected_entries (songs);
367 for (tem = sel; tem != NULL; tem = tem->next) {
368 rb_static_playlist_source_remove_entry (source, (RhythmDBEntry *) tem->data);
370 g_list_foreach (sel, (GFunc)rhythmdb_entry_unref, NULL);
371 g_list_free (sel);
374 static void
375 impl_reset_filters (RBSource *source)
377 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
378 gboolean changed = FALSE;
380 if (rb_library_browser_reset (priv->browser))
381 changed = TRUE;
383 if (priv->search_text != NULL) {
384 changed = TRUE;
385 g_free (priv->search_text);
386 priv->search_text = NULL;
389 if (changed) {
390 rb_static_playlist_source_do_query (RB_STATIC_PLAYLIST_SOURCE (source));
391 rb_source_notify_filter_changed (source);
395 static void
396 impl_search (RBSource *source, const char *search_text)
398 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
399 char *old_search_text = NULL;
401 if (search_text != NULL && search_text[0] == '\0')
402 search_text = NULL;
404 if (search_text == NULL && priv->search_text == NULL)
405 return;
406 if (search_text != NULL && priv->search_text != NULL
407 && !strcmp (search_text, priv->search_text))
408 return;
410 old_search_text = priv->search_text;
411 if (search_text == NULL) {
412 priv->search_text = NULL;
413 } else {
414 priv->search_text = g_strdup (search_text);
416 g_free (old_search_text);
418 rb_debug ("doing search for \"%s\"", priv->search_text ? priv->search_text : "(NULL)");
420 rb_static_playlist_source_do_query (RB_STATIC_PLAYLIST_SOURCE (source));
423 static GList *
424 impl_get_property_views (RBSource *source)
426 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
427 GList *ret;
429 ret = rb_library_browser_get_property_views (priv->browser);
430 return ret;
433 static void
434 impl_browser_toggled (RBSource *source, gboolean enabled)
436 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
438 priv->browser_shown = enabled;
440 if (enabled)
441 gtk_widget_show (GTK_WIDGET (priv->browser));
442 else
443 gtk_widget_hide (GTK_WIDGET (priv->browser));
446 static GPtrArray *
447 construct_query_from_selection (RBStaticPlaylistSource *source)
449 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
450 RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source);
451 RhythmDB *db = rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (psource));
452 GPtrArray *query = NULL;
454 query = g_ptr_array_new();
456 if (priv->search_text) {
457 rhythmdb_query_append (db,
458 query,
459 RHYTHMDB_QUERY_PROP_LIKE, priv->search_prop, priv->search_text,
460 RHYTHMDB_QUERY_END);
463 return query;
466 static void
467 rb_static_playlist_source_do_query (RBStaticPlaylistSource *source)
469 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
470 RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source);
471 RhythmDB *db = rb_playlist_source_get_db (psource);
472 GPtrArray *query;
474 if (priv->filter_model != NULL) {
475 g_object_unref (priv->filter_model);
477 priv->filter_model = rhythmdb_query_model_new_empty (db);
478 g_object_set (priv->filter_model, "base-model", priv->base_model, NULL);
480 query = construct_query_from_selection (source);
481 g_object_set (priv->filter_model, "query", query, NULL);
482 rhythmdb_query_free (query);
484 rhythmdb_query_model_reapply_query (priv->filter_model, TRUE);
485 rb_library_browser_set_model (priv->browser, priv->filter_model, FALSE);
488 static void
489 rb_static_playlist_source_browser_changed_cb (RBLibraryBrowser *browser,
490 GParamSpec *pspec,
491 RBStaticPlaylistSource *source)
493 RBEntryView *songs = rb_source_get_entry_view (RB_SOURCE (source));
494 RhythmDBQueryModel *query_model;
496 g_object_get (browser, "output-model", &query_model, NULL);
497 rb_entry_view_set_model (songs, query_model);
498 rb_playlist_source_set_query_model (RB_PLAYLIST_SOURCE (source), query_model);
499 g_object_unref (query_model);
501 rb_source_notify_filter_changed (RB_SOURCE (source));
504 static gboolean
505 impl_receive_drag (RBSource *asource, GtkSelectionData *data)
507 GList *list;
508 RBStaticPlaylistSource *source = RB_STATIC_PLAYLIST_SOURCE (asource);
510 if (data->type == gdk_atom_intern ("text/uri-list", TRUE) ||
511 data->type == gdk_atom_intern ("application/x-rhythmbox-entry", TRUE)) {
512 list = rb_uri_list_parse ((char *)data->data);
513 if (list == NULL)
514 return FALSE;
516 if (data->type == gdk_atom_intern ("text/uri-list", TRUE))
517 rb_static_playlist_source_add_uri_list (source, list);
518 else
519 rb_static_playlist_source_add_id_list (source, list);
520 rb_list_deep_free (list);
523 return TRUE;
526 static void
527 impl_save_contents_to_xml (RBPlaylistSource *source,
528 xmlNodePtr node)
530 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
531 GtkTreeIter iter;
533 xmlSetProp (node, RB_PLAYLIST_TYPE, RB_PLAYLIST_STATIC);
535 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->base_model), &iter))
536 return;
538 do {
539 xmlNodePtr child_node = xmlNewChild (node, NULL, RB_PLAYLIST_LOCATION, NULL);
540 RhythmDBEntry *entry;
541 xmlChar *encoded;
542 const char *location;
544 gtk_tree_model_get (GTK_TREE_MODEL (priv->base_model), &iter, 0, &entry, -1);
546 location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
547 encoded = xmlEncodeEntitiesReentrant (NULL, BAD_CAST location);
549 xmlNodeSetContent (child_node, encoded);
551 g_free (encoded);
552 rhythmdb_entry_unref (entry);
553 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->base_model), &iter));
556 static void
557 rb_static_playlist_source_add_id_list (RBStaticPlaylistSource *source,
558 GList *list)
560 RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source);
561 GList *i;
562 gint id;
564 g_return_if_fail (list != NULL);
566 for (i = list; i != NULL; i = i->next) {
567 RhythmDBEntry *entry;
569 id = strtoul ((const char *)i->data, NULL, 0);
570 if (id == 0)
571 continue;
573 entry = rhythmdb_entry_lookup_by_id (rb_playlist_source_get_db (psource), id);
574 if (entry == NULL) {
575 rb_debug ("received id %d, but can't find the entry", id);
576 continue;
579 rb_static_playlist_source_add_entry (source, entry, -1);
583 static void
584 rb_static_playlist_source_add_uri_list (RBStaticPlaylistSource *source,
585 GList *list)
587 GList *i, *uri_list = NULL;
588 RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source);
589 RhythmDBEntry *entry;
591 g_return_if_fail (list != NULL);
593 for (i = list; i != NULL; i = g_list_next (i)) {
594 char *uri = (char *) i->data;
595 uri_list = g_list_prepend (uri_list, rb_canonicalise_uri (uri));
598 uri_list = g_list_reverse (uri_list);
599 if (uri_list == NULL)
600 return;
602 for (i = uri_list; i != NULL; i = i->next) {
603 char *uri = i->data;
604 if (uri != NULL) {
605 entry = rhythmdb_entry_lookup_by_location (rb_playlist_source_get_db (psource), uri);
606 if (entry == NULL)
607 rhythmdb_add_uri (rb_playlist_source_get_db (psource), uri);
609 rb_static_playlist_source_add_location (source, uri, -1);
612 g_free (uri);
614 g_list_free (uri_list);
617 static void
618 rb_static_playlist_source_add_location_internal (RBStaticPlaylistSource *source,
619 const char *location,
620 gint index)
622 RhythmDB *db;
623 RhythmDBEntry *entry;
624 RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source);
625 if (rb_playlist_source_location_in_map (psource, location))
626 return;
628 db = rb_playlist_source_get_db (psource);
629 entry = rhythmdb_entry_lookup_by_location (db, location);
630 if (entry) {
631 RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
632 RhythmDBEntryType entry_type;
634 g_object_get (source, "entry-type", &entry_type, NULL);
635 if (entry_type != RHYTHMDB_ENTRY_TYPE_INVALID &&
636 rhythmdb_entry_get_entry_type (entry) != entry_type) {
637 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
638 rb_debug ("attempting to add an entry of the wrong type to playlist");
639 return;
642 rhythmdb_entry_ref (entry);
643 rhythmdb_query_model_add_entry (priv->base_model, entry, index);
644 rhythmdb_entry_unref (entry);
646 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
649 rb_playlist_source_add_to_map (psource, location);
651 rb_playlist_source_mark_dirty (psource);
654 static void
655 rb_static_playlist_source_add_location_cb (const char *uri,
656 gboolean dir,
657 RBStaticPlaylistSource *source)
659 if (!dir)
660 rb_static_playlist_source_add_location_internal (source, uri, -1);
663 void
664 rb_static_playlist_source_add_location (RBStaticPlaylistSource *source,
665 const char *location,
666 gint index)
668 RhythmDB *db;
669 RhythmDBEntry *entry;
671 db = rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source));
672 entry = rhythmdb_entry_lookup_by_location (db, location);
674 /* if there is an entry, it won't be a directory */
675 if (entry == NULL && rb_uri_is_directory (location))
676 rb_uri_handle_recursively (location,
677 (RBUriRecurseFunc) rb_static_playlist_source_add_location_cb,
678 NULL,
679 source);
680 else
681 rb_static_playlist_source_add_location_internal (source, location, index);
685 void
686 rb_static_playlist_source_add_locations (RBStaticPlaylistSource *source,
687 GList *locations)
689 GList *l;
691 for (l = locations; l; l = l->next) {
692 const gchar *uri = (const gchar *)l->data;
693 rb_static_playlist_source_add_location (source, uri, -1);
697 void
698 rb_static_playlist_source_remove_location (RBStaticPlaylistSource *source,
699 const char *location)
701 RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source);
702 RhythmDB *db;
703 RhythmDBEntry *entry;
705 g_return_if_fail (rb_playlist_source_location_in_map (psource, location));
707 db = rb_playlist_source_get_db (psource);
708 entry = rhythmdb_entry_lookup_by_location (db, location);
710 if (entry != NULL) {
711 RhythmDBQueryModel *model = rb_playlist_source_get_query_model (psource);
713 /* if this fails, the model and the playlist are out of sync */
714 g_assert (rhythmdb_query_model_remove_entry (model, entry));
715 rb_playlist_source_mark_dirty (psource);
719 void
720 rb_static_playlist_source_add_entry (RBStaticPlaylistSource *source,
721 RhythmDBEntry *entry,
722 gint index)
724 const char *location;
726 location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
727 rb_static_playlist_source_add_location_internal (source, location, index);
730 void
731 rb_static_playlist_source_remove_entry (RBStaticPlaylistSource *source,
732 RhythmDBEntry *entry)
734 const char *location;
736 location = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
737 rb_static_playlist_source_remove_location (source, location);
740 void
741 rb_static_playlist_source_move_entry (RBStaticPlaylistSource *source,
742 RhythmDBEntry *entry,
743 gint index)
745 RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source);
746 RhythmDBQueryModel *model = rb_playlist_source_get_query_model (psource);
747 rhythmdb_query_model_move_entry (model, entry, index);
749 rb_playlist_source_mark_dirty (psource);
752 static void
753 rb_static_playlist_source_non_entry_dropped (GtkTreeModel *model,
754 const char *uri,
755 int position,
756 RBStaticPlaylistSource *source)
758 g_assert (g_utf8_strlen (uri, -1) > 0);
760 rhythmdb_add_uri (rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source)), uri);
761 rb_static_playlist_source_add_location (source, uri, position);
764 static void
765 rb_static_playlist_source_row_inserted (GtkTreeModel *model,
766 GtkTreePath *path,
767 GtkTreeIter *iter,
768 RBStaticPlaylistSource *source)
770 RhythmDBEntry *entry;
772 gtk_tree_model_get (model, iter, 0, &entry, -1);
774 rb_static_playlist_source_add_entry (source, entry, -1);
776 rhythmdb_entry_unref (entry);
779 static GList *
780 impl_get_search_actions (RBSource *source)
782 GList *actions = NULL;
784 actions = g_list_prepend (actions, g_strdup ("StaticPlaylistSearchTitles"));
785 actions = g_list_prepend (actions, g_strdup ("StaticPlaylistSearchAlbums"));
786 actions = g_list_prepend (actions, g_strdup ("StaticPlaylistSearchArtists"));
787 actions = g_list_prepend (actions, g_strdup ("StaticPlaylistSearchAll"));
789 return actions;
792 static RhythmDBPropType
793 search_action_to_prop (GtkAction *action)
795 const char *name;
796 RhythmDBPropType prop;
798 name = gtk_action_get_name (action);
800 if (name == NULL) {
801 prop = RHYTHMDB_PROP_SEARCH_MATCH;
802 } else if (strcmp (name, "StaticPlaylistSearchAll") == 0) {
803 prop = RHYTHMDB_PROP_SEARCH_MATCH;
804 } else if (strcmp (name, "StaticPlaylistSearchArtists") == 0) {
805 prop = RHYTHMDB_PROP_ARTIST_FOLDED;
806 } else if (strcmp (name, "StaticPlaylistSearchAlbums") == 0) {
807 prop = RHYTHMDB_PROP_ALBUM_FOLDED;
808 } else if (strcmp (name, "StaticPlaylistSearchTitles") == 0) {
809 prop = RHYTHMDB_PROP_TITLE_FOLDED;
810 } else {
811 prop = RHYTHMDB_PROP_SEARCH_MATCH;
814 return prop;
817 static void
818 search_action_changed (GtkRadioAction *action,
819 GtkRadioAction *current,
820 RBShell *shell)
822 RBStaticPlaylistSource *source;
823 RBStaticPlaylistSourcePrivate *priv;
824 gboolean active;
826 g_object_get (shell, "selected-source", &source, NULL);
827 priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source);
829 active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
831 if (active) {
832 /* update query */
833 priv->search_prop = search_action_to_prop (GTK_ACTION (current));
834 rb_static_playlist_source_do_query (source);
835 rb_source_notify_filter_changed (RB_SOURCE (source));
838 if (source != NULL) {
839 g_object_unref (source);
843 static guint
844 impl_want_uri (RBSource *source, const char *uri)
846 /* take anything local, on smb, or sftp */
847 if (rb_uri_is_local (uri) ||
848 g_str_has_prefix (uri, "smb://") ||
849 g_str_has_prefix (uri, "sftp://"))
850 return 25; /* less than what the library returns */
852 return 0;