2006-10-27 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / sources / rb-playlist-source.c
blobd6256094e82d3418a88e93a30568fa8ecdcad51a
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * arch-tag: Implementation of playlist source object
5 * Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
6 * Copyright (C) 2003 Colin Walters <walters@gnome.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 <unistd.h>
27 #include <string.h>
29 #include <libxml/tree.h>
30 #include <glib/gi18n.h>
31 #include <gtk/gtk.h>
32 #include <libgnomevfs/gnome-vfs-uri.h>
33 #include <totem-pl-parser.h>
35 #include "rb-entry-view.h"
36 #include "rb-search-entry.h"
37 #include "rb-file-helpers.h"
38 #include "rb-preferences.h"
39 #include "rb-dialog.h"
40 #include "rb-util.h"
41 #include "rb-playlist-source.h"
42 #include "rb-debug.h"
43 #include "eel-gconf-extensions.h"
44 #include "rb-song-info.h"
46 #include "rb-playlist-xml.h"
47 #include "rb-static-playlist-source.h"
48 #include "rb-auto-playlist-source.h"
50 static void rb_playlist_source_class_init (RBPlaylistSourceClass *klass);
51 static void rb_playlist_source_init (RBPlaylistSource *source);
52 static GObject *rb_playlist_source_constructor (GType type,
53 guint n_construct_properties,
54 GObjectConstructParam *construct_properties);
55 static void rb_playlist_source_dispose (GObject *object);
56 static void rb_playlist_source_finalize (GObject *object);
57 static void rb_playlist_source_set_property (GObject *object,
58 guint prop_id,
59 const GValue *value,
60 GParamSpec *pspec);
61 static void rb_playlist_source_get_property (GObject *object,
62 guint prop_id,
63 GValue *value,
64 GParamSpec *pspec);
66 /* source methods */
67 static char *impl_get_browser_key (RBSource *source);
68 static RBEntryView *impl_get_entry_view (RBSource *source);
69 static void impl_song_properties (RBSource *source);
70 static gboolean impl_show_popup (RBSource *source);
72 static void rb_playlist_source_songs_show_popup_cb (RBEntryView *view,
73 gboolean over_entry,
74 RBPlaylistSource *playlist_view);
75 static void rb_playlist_source_drop_cb (GtkWidget *widget,
76 GdkDragContext *context,
77 gint x,
78 gint y,
79 GtkSelectionData *data,
80 guint info,
81 guint time,
82 gpointer user_data);
84 static void rb_playlist_source_row_deleted (GtkTreeModel *model,
85 GtkTreePath *path,
86 RBPlaylistSource *playlist);
87 static void default_show_entry_view_popup (RBPlaylistSource *source,
88 RBEntryView *view,
89 gboolean over_entry);
90 static void rb_playlist_source_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry,
91 RBPlaylistSource *source);
92 static void rb_playlist_source_track_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
93 GtkTreeModel *tree_model, GtkTreeIter *iter,
94 RBPlaylistSource *source);
96 #define PLAYLIST_SOURCE_SONGS_POPUP_PATH "/PlaylistViewPopup"
97 #define PLAYLIST_SOURCE_POPUP_PATH "/PlaylistSourcePopup"
99 struct RBPlaylistSourcePrivate
101 RhythmDB *db;
103 GHashTable *entries;
105 RhythmDBQueryModel *model;
107 RBEntryView *songs;
109 gboolean dirty;
110 gboolean is_local;
111 gboolean dispose_has_run;
113 char *title;
116 #define RB_PLAYLIST_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_PLAYLIST_SOURCE, RBPlaylistSourcePrivate))
118 enum
120 PROP_0,
121 PROP_DB,
122 PROP_DIRTY,
123 PROP_LOCAL,
126 static const GtkTargetEntry target_uri [] = { { "text/uri-list", 0, 0 } };
128 G_DEFINE_ABSTRACT_TYPE (RBPlaylistSource, rb_playlist_source, RB_TYPE_SOURCE);
130 static void
131 rb_playlist_source_class_init (RBPlaylistSourceClass *klass)
133 GObjectClass *object_class = G_OBJECT_CLASS (klass);
134 RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
136 object_class->dispose = rb_playlist_source_dispose;
137 object_class->finalize = rb_playlist_source_finalize;
138 object_class->constructor = rb_playlist_source_constructor;
140 object_class->set_property = rb_playlist_source_set_property;
141 object_class->get_property = rb_playlist_source_get_property;
143 source_class->impl_get_browser_key = impl_get_browser_key;
144 source_class->impl_get_entry_view = impl_get_entry_view;
145 source_class->impl_can_rename = (RBSourceFeatureFunc) rb_true_function;
146 source_class->impl_can_search = (RBSourceFeatureFunc) rb_false_function;
147 source_class->impl_can_cut = (RBSourceFeatureFunc) rb_false_function;
148 source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function;
149 source_class->impl_can_delete = (RBSourceFeatureFunc) rb_false_function;
150 source_class->impl_can_add_to_queue = (RBSourceFeatureFunc) rb_true_function;
151 source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_true_function;
152 source_class->impl_song_properties = impl_song_properties;
153 source_class->impl_can_pause = (RBSourceFeatureFunc) rb_true_function;
154 source_class->impl_show_popup = impl_show_popup;
156 klass->impl_show_entry_view_popup = default_show_entry_view_popup;
158 g_object_class_install_property (object_class,
159 PROP_DB,
160 g_param_spec_object ("db",
161 "db",
162 "rhythmdb instance",
163 RHYTHMDB_TYPE,
164 G_PARAM_READABLE));
165 g_object_class_install_property (object_class,
166 PROP_DIRTY,
167 g_param_spec_boolean ("dirty",
168 "dirty",
169 "whether this playlist should be saved",
170 FALSE,
171 G_PARAM_READABLE));
172 g_object_class_install_property (object_class,
173 PROP_LOCAL,
174 g_param_spec_boolean ("is-local",
175 "is-local",
176 "whether this playlist is attached to the local library",
177 TRUE,
178 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
180 g_type_class_add_private (klass, sizeof (RBPlaylistSourcePrivate));
183 static void
184 rb_playlist_source_init (RBPlaylistSource *source)
186 source->priv = RB_PLAYLIST_SOURCE_GET_PRIVATE (source);
189 static void
190 rb_playlist_source_set_db (RBPlaylistSource *source,
191 RhythmDB *db)
193 if (source->priv->db != NULL) {
194 g_signal_handlers_disconnect_by_func (source->priv->db,
195 rb_playlist_source_entry_added_cb,
196 source);
197 g_object_unref (source->priv->db);
201 source->priv->db = db;
203 if (source->priv->db != NULL) {
204 g_object_ref (source->priv->db);
205 g_signal_connect_object (G_OBJECT (source->priv->db), "entry_added",
206 G_CALLBACK (rb_playlist_source_entry_added_cb),
207 source, 0);
212 static GObject *
213 rb_playlist_source_constructor (GType type,
214 guint n_construct_properties,
215 GObjectConstructParam *construct_properties)
217 GObject *shell_player;
218 RBPlaylistSource *source;
219 RBPlaylistSourceClass *klass;
220 RBShell *shell;
221 RhythmDB *db;
222 RhythmDBQueryModel *query_model;
224 klass = RB_PLAYLIST_SOURCE_CLASS (g_type_class_peek (RB_TYPE_PLAYLIST_SOURCE));
226 source = RB_PLAYLIST_SOURCE (G_OBJECT_CLASS (rb_playlist_source_parent_class)->
227 constructor (type, n_construct_properties, construct_properties));
229 g_object_get (source, "shell", &shell, NULL);
230 g_object_get (shell, "db", &db, NULL);
231 shell_player = rb_shell_get_player (shell);
232 rb_playlist_source_set_db (source, db);
233 g_object_unref (db);
234 g_object_unref (shell);
236 source->priv->entries = g_hash_table_new_full (rb_refstring_hash, rb_refstring_equal,
237 (GDestroyNotify)rb_refstring_unref, NULL);
239 source->priv->songs = rb_entry_view_new (source->priv->db,
240 shell_player,
241 NULL, TRUE, TRUE);
243 query_model = rhythmdb_query_model_new_empty (source->priv->db);
244 rb_playlist_source_set_query_model (source, query_model);
245 g_object_unref (query_model);
248 const char *title = "";
249 const char *strings[3] = {0};
251 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
252 GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
254 g_object_set(renderer,
255 "style", PANGO_STYLE_OBLIQUE,
256 "weight", PANGO_WEIGHT_LIGHT,
257 "xalign", 1.0,
258 NULL);
260 gtk_tree_view_column_pack_start (column, renderer, TRUE);
262 gtk_tree_view_column_set_resizable (column, TRUE);
263 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
265 strings[0] = title;
266 strings[1] = "9999";
267 rb_entry_view_set_fixed_column_width (source->priv->songs, column, renderer,
268 strings);
269 gtk_tree_view_column_set_cell_data_func (column, renderer,
270 (GtkTreeCellDataFunc)
271 rb_playlist_source_track_cell_data_func,
272 source, NULL);
273 rb_entry_view_insert_column_custom (source->priv->songs, column, title,
274 "PlaylistTrack", NULL, 0, 0);
277 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE);
278 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TITLE, TRUE);
279 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_GENRE, FALSE);
280 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
281 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
282 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_YEAR, FALSE);
283 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_DURATION, FALSE);
284 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_QUALITY, FALSE);
285 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_RATING, FALSE);
286 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);
287 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
288 rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_FIRST_SEEN, FALSE);
289 rb_entry_view_set_columns_clickable (source->priv->songs, FALSE);
291 rb_playlist_source_setup_entry_view (source, source->priv->songs);
293 gtk_container_add (GTK_CONTAINER (source), GTK_WIDGET (source->priv->songs));
295 gtk_widget_show_all (GTK_WIDGET (source));
297 return G_OBJECT (source);
300 static void
301 rb_playlist_source_dispose (GObject *object)
303 RBPlaylistSource *source = RB_PLAYLIST_SOURCE (object);
305 if (source->priv->dispose_has_run) {
306 /* If dispose did already run, return. */
307 rb_debug ("Dispose has already run for playlist source %p", object);
308 return;
310 /* Make sure dispose does not run twice. */
311 source->priv->dispose_has_run = TRUE;
313 rb_debug ("Disposing playlist source %p", source);
315 if (source->priv->db != NULL) {
316 g_object_unref (source->priv->db);
317 source->priv->db = NULL;
320 if (source->priv->model != NULL) {
321 g_object_unref (source->priv->model);
324 G_OBJECT_CLASS (rb_playlist_source_parent_class)->dispose (object);
327 static void
328 rb_playlist_source_finalize (GObject *object)
330 RBPlaylistSource *source;
332 g_return_if_fail (object != NULL);
333 g_return_if_fail (RB_IS_PLAYLIST_SOURCE (object));
335 source = RB_PLAYLIST_SOURCE (object);
336 g_return_if_fail (source->priv != NULL);
338 rb_debug ("Finalizing playlist source %p", source);
340 g_hash_table_destroy (source->priv->entries);
342 g_free (source->priv->title);
343 source->priv = NULL;
345 G_OBJECT_CLASS (rb_playlist_source_parent_class)->finalize (object);
348 static void
349 rb_playlist_source_set_property (GObject *object,
350 guint prop_id,
351 const GValue *value,
352 GParamSpec *pspec)
354 RBPlaylistSource *source = RB_PLAYLIST_SOURCE (object);
356 switch (prop_id) {
357 case PROP_LOCAL:
358 source->priv->is_local = g_value_get_boolean (value);
359 break;
360 default:
361 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362 break;
366 static void
367 rb_playlist_source_get_property (GObject *object,
368 guint prop_id,
369 GValue *value,
370 GParamSpec *pspec)
372 RBPlaylistSource *source = RB_PLAYLIST_SOURCE (object);
374 switch (prop_id) {
375 case PROP_DB:
376 g_value_set_object (value, source->priv->db);
377 break;
378 case PROP_DIRTY:
379 g_value_set_boolean (value, source->priv->dirty);
380 break;
381 case PROP_LOCAL:
382 g_value_set_boolean (value, source->priv->is_local);
383 break;
384 default:
385 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
386 break;
390 static void
391 default_show_entry_view_popup (RBPlaylistSource *source,
392 RBEntryView *view,
393 gboolean over_entry)
395 if (over_entry)
396 _rb_source_show_popup (RB_SOURCE (source), PLAYLIST_SOURCE_SONGS_POPUP_PATH);
399 static void
400 rb_playlist_source_songs_show_popup_cb (RBEntryView *view,
401 gboolean over_entry,
402 RBPlaylistSource *source)
404 RBPlaylistSourceClass *klass = RB_PLAYLIST_SOURCE_GET_CLASS (source);
405 if (klass->impl_show_entry_view_popup)
406 klass->impl_show_entry_view_popup (source, view, over_entry);
409 static char *
410 impl_get_browser_key (RBSource *source)
412 return NULL;
415 static RBEntryView *
416 impl_get_entry_view (RBSource *asource)
418 RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource);
420 return source->priv->songs;
423 static void
424 impl_song_properties (RBSource *asource)
426 RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource);
427 GtkWidget *song_info = NULL;
429 g_return_if_fail (source->priv->songs != NULL);
431 song_info = rb_song_info_new (asource, NULL);
432 if (song_info)
433 gtk_widget_show_all (song_info);
434 else
435 rb_debug ("failed to create dialog, or no selection!");
438 static gboolean
439 impl_show_popup (RBSource *asource)
441 _rb_source_show_popup (asource, PLAYLIST_SOURCE_POPUP_PATH);
442 return TRUE;
445 static void
446 rb_playlist_source_drop_cb (GtkWidget *widget,
447 GdkDragContext *context,
448 gint x,
449 gint y,
450 GtkSelectionData *data,
451 guint info,
452 guint time,
453 gpointer user_data)
455 RBPlaylistSource *source = RB_PLAYLIST_SOURCE (user_data);
456 GtkTargetList *tlist;
457 GdkAtom target;
459 tlist = gtk_target_list_new (target_uri, G_N_ELEMENTS (target_uri));
460 target = gtk_drag_dest_find_target (widget, context, tlist);
461 gtk_target_list_unref (tlist);
463 if (target == GDK_NONE)
464 return;
466 rb_source_receive_drag (RB_SOURCE (source), data);
468 gtk_drag_finish (context, TRUE, FALSE, time);
471 #ifndef TOTEM_PL_PARSER_CHECK_VERSION
472 static void
473 playlist_iter_func (GtkTreeModel *model,
474 GtkTreeIter *iter,
475 char **uri,
476 char **title,
477 gpointer user_data)
479 RhythmDBEntry *entry;
481 gtk_tree_model_get (model, iter, 0, &entry, -1);
483 if (uri != NULL) {
484 *uri = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_LOCATION);
486 if (title != NULL) {
487 *title = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_TITLE);
490 if (entry != NULL) {
491 rhythmdb_entry_unref (entry);
494 #else
495 static void
496 playlist_iter_func (GtkTreeModel *model,
497 GtkTreeIter *iter,
498 char **uri,
499 char **title,
500 gboolean *custom_title,
501 gpointer user_data)
503 RhythmDBEntry *entry;
505 gtk_tree_model_get (model, iter, 0, &entry, -1);
507 if (uri != NULL) {
508 *uri = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_LOCATION);
510 if (title != NULL) {
511 *title = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_TITLE);
513 if (custom_title != NULL) {
514 *custom_title = FALSE;
517 if (entry != NULL) {
518 rhythmdb_entry_unref (entry);
521 #endif /* TOTEM_PL_PARSER_CHECK_VERSION */
523 void
524 rb_playlist_source_save_playlist (RBPlaylistSource *source,
525 const char *uri,
526 gboolean m3u_format)
528 TotemPlParser *playlist;
529 GError *error = NULL;
530 char *name;
532 g_return_if_fail (RB_IS_PLAYLIST_SOURCE (source));
534 rb_debug ("saving playlist");
535 playlist = totem_pl_parser_new ();
537 g_object_get (source, "name", &name, NULL);
539 totem_pl_parser_write_with_title (playlist, GTK_TREE_MODEL (source->priv->model),
540 playlist_iter_func, uri, name,
541 m3u_format ? TOTEM_PL_PARSER_M3U : TOTEM_PL_PARSER_PLS,
542 NULL, &error);
543 g_free (name);
544 if (error != NULL)
545 rb_error_dialog (NULL, _("Couldn't save playlist"),
546 "%s", error->message);
549 /* Adapted from yelp-toc-pager.c */
550 static xmlChar *
551 xml_get_and_trim_names (xmlNodePtr node)
553 xmlNodePtr cur, keep = NULL;
554 xmlChar *keep_lang = NULL;
555 xmlChar *value;
556 int j, keep_pri = INT_MAX;
558 const gchar * const * langs = g_get_language_names ();
560 value = NULL;
562 for (cur = node->children; cur; cur = cur->next) {
563 if (! xmlStrcmp (cur->name, RB_PLAYLIST_NAME)) {
564 xmlChar *cur_lang = NULL;
565 int cur_pri = INT_MAX;
567 cur_lang = xmlNodeGetLang (cur);
569 if (cur_lang) {
570 for (j = 0; langs[j]; j++) {
571 if (g_str_equal (cur_lang, langs[j])) {
572 cur_pri = j;
573 break;
576 } else {
577 cur_pri = INT_MAX - 1;
580 if (cur_pri <= keep_pri) {
581 if (keep_lang)
582 xmlFree (keep_lang);
583 if (value)
584 xmlFree (value);
586 value = xmlNodeGetContent (cur);
588 keep_lang = cur_lang;
589 keep_pri = cur_pri;
590 keep = cur;
591 } else {
592 if (cur_lang)
593 xmlFree (cur_lang);
598 /* Delete all RB_PLAYLIST_NAME nodes */
599 cur = node->children;
600 while (cur) {
601 xmlNodePtr this = cur;
602 cur = cur->next;
603 if (! xmlStrcmp (this->name, RB_PLAYLIST_NAME)) {
604 xmlUnlinkNode (this);
605 xmlFreeNode (this);
609 return value;
612 static xmlChar *
613 get_playlist_name_from_xml (xmlNodePtr node)
615 xmlChar *name;
617 /* try to get and trim elements */
618 name = xml_get_and_trim_names (node);
620 if (name != NULL) {
621 return name;
624 /* try the attribute */
625 name = xmlGetProp (node, RB_PLAYLIST_NAME);
627 return name;
630 RBSource *
631 rb_playlist_source_new_from_xml (RBShell *shell,
632 xmlNodePtr node)
634 RBSource *source = NULL;
635 xmlChar *tmp;
636 xmlChar *name;
638 g_return_val_if_fail (RB_IS_SHELL (shell), NULL);
640 /* Try to get name from XML and remove translated names */
641 name = get_playlist_name_from_xml (node);
643 tmp = xmlGetProp (node, RB_PLAYLIST_TYPE);
645 if (!xmlStrcmp (tmp, RB_PLAYLIST_AUTOMATIC))
646 source = rb_auto_playlist_source_new_from_xml (shell, node);
647 else if (!xmlStrcmp (tmp, RB_PLAYLIST_STATIC))
648 source = rb_static_playlist_source_new_from_xml (shell, node);
649 else if (!xmlStrcmp (tmp, RB_PLAYLIST_QUEUE)) {
650 RBStaticPlaylistSource *queue;
652 g_object_get (shell, "queue-source", &queue, NULL);
653 rb_static_playlist_source_load_from_xml (queue, node);
654 g_object_unref (queue);
655 } else {
656 g_warning ("attempting to load playlist '%s' of unknown type '%s'", name, tmp);
659 if (source != NULL) {
660 g_object_set (G_OBJECT (source), "name", name, NULL);
663 xmlFree (name);
664 xmlFree (tmp);
666 return source;
669 void
670 rb_playlist_source_save_to_xml (RBPlaylistSource *source,
671 xmlNodePtr parent_node)
673 xmlNodePtr node;
674 xmlChar *name;
675 RBPlaylistSourceClass *klass = RB_PLAYLIST_SOURCE_GET_CLASS (source);
677 g_return_if_fail (RB_IS_PLAYLIST_SOURCE (source));
679 node = xmlNewChild (parent_node, NULL, RB_PLAYLIST_PLAYLIST, NULL);
680 g_object_get (source, "name", &name, NULL);
681 xmlSetProp (node, RB_PLAYLIST_NAME, name);
682 g_free (name);
684 klass->impl_save_contents_to_xml (source, node);
686 source->priv->dirty = FALSE;
689 static void
690 rb_playlist_source_row_deleted (GtkTreeModel *model,
691 GtkTreePath *path,
692 RBPlaylistSource *source)
694 RhythmDBEntry *entry;
695 RBRefString *location;
697 entry = rhythmdb_query_model_tree_path_to_entry (RHYTHMDB_QUERY_MODEL (model),
698 path);
700 location = rhythmdb_entry_get_refstring (entry, RHYTHMDB_PROP_LOCATION);
701 if (g_hash_table_remove (source->priv->entries, location))
702 source->priv->dirty = TRUE;
704 rb_refstring_unref (location);
705 rhythmdb_entry_unref (entry);
708 static void
709 rb_playlist_source_entry_added_cb (RhythmDB *db,
710 RhythmDBEntry *entry,
711 RBPlaylistSource *source)
713 RBRefString *location;
715 location = rhythmdb_entry_get_refstring (entry, RHYTHMDB_PROP_LOCATION);
717 if (g_hash_table_lookup (source->priv->entries, location)) {
718 rhythmdb_query_model_add_entry (source->priv->model, entry, -1);
719 source->priv->dirty = TRUE;
722 rb_refstring_unref (location);
725 static void
726 rb_playlist_source_track_cell_data_func (GtkTreeViewColumn *column,
727 GtkCellRenderer *renderer,
728 GtkTreeModel *tree_model,
729 GtkTreeIter *iter,
730 RBPlaylistSource *source)
732 char *str;
733 int val;
735 gtk_tree_model_get (tree_model, iter, 1, &val, -1);
737 if (val >= 0)
738 str = g_strdup_printf ("%d", val);
739 else
740 str = g_strdup ("");
742 g_object_set (G_OBJECT (renderer), "text", str, NULL);
743 g_free (str);
746 void
747 rb_playlist_source_setup_entry_view (RBPlaylistSource *source,
748 RBEntryView *entry_view)
750 g_return_if_fail (RB_IS_PLAYLIST_SOURCE (source));
752 g_signal_connect_object (entry_view, "show_popup",
753 G_CALLBACK (rb_playlist_source_songs_show_popup_cb), source, 0);
754 g_signal_connect_object (entry_view, "drag_data_received",
755 G_CALLBACK (rb_playlist_source_drop_cb), source, 0);
756 gtk_drag_dest_set (GTK_WIDGET (entry_view),
757 GTK_DEST_DEFAULT_ALL,
758 target_uri,
759 G_N_ELEMENTS (target_uri),
760 GDK_ACTION_COPY);
763 void
764 rb_playlist_source_set_query_model (RBPlaylistSource *source,
765 RhythmDBQueryModel *model)
767 g_return_if_fail (RB_IS_PLAYLIST_SOURCE (source));
769 if (source->priv->model != NULL) {
770 /* if the query model is replaced, the set of entries in
771 * the playlist will change, so we should mark the playlist dirty.
773 source->priv->dirty = TRUE;
774 g_signal_handlers_disconnect_by_func (source->priv->model,
775 G_CALLBACK (rb_playlist_source_row_deleted),
776 source);
777 g_object_unref (source->priv->model);
780 source->priv->model = model;
782 if (source->priv->model != NULL) {
783 g_object_ref (source->priv->model);
784 g_signal_connect_object (source->priv->model, "row_deleted",
785 G_CALLBACK (rb_playlist_source_row_deleted), source, 0);
788 rb_entry_view_set_model (source->priv->songs, RHYTHMDB_QUERY_MODEL (source->priv->model));
790 g_object_set (source, "query-model", source->priv->model, NULL);
793 RhythmDB *
794 rb_playlist_source_get_db (RBPlaylistSource *source)
796 g_return_val_if_fail (RB_IS_PLAYLIST_SOURCE (source), NULL);
798 return source->priv->db;
801 RhythmDBQueryModel *
802 rb_playlist_source_get_query_model (RBPlaylistSource *source)
804 g_return_val_if_fail (RB_IS_PLAYLIST_SOURCE (source), NULL);
806 return source->priv->model;
809 void
810 rb_playlist_source_mark_dirty (RBPlaylistSource *source)
812 g_return_if_fail (RB_IS_PLAYLIST_SOURCE (source));
814 source->priv->dirty = TRUE;
817 gboolean
818 rb_playlist_source_location_in_map (RBPlaylistSource *source,
819 const char *location)
821 RBRefString *refstr;
822 gboolean found;
824 g_return_val_if_fail (RB_IS_PLAYLIST_SOURCE (source), FALSE);
826 refstr = rb_refstring_find (location);
827 if (refstr == NULL) {
828 return FALSE;
831 found = (g_hash_table_lookup (source->priv->entries, refstr) != NULL);
832 rb_refstring_unref (refstr);
834 return found;
837 gboolean
838 rb_playlist_source_add_to_map (RBPlaylistSource *source,
839 const char *location)
841 RBRefString *refstr;
843 g_return_val_if_fail (RB_IS_PLAYLIST_SOURCE (source), FALSE);
845 refstr = rb_refstring_new (location);
846 if (g_hash_table_lookup (source->priv->entries, refstr)) {
847 rb_refstring_unref (refstr);
848 return FALSE;
851 g_hash_table_insert (source->priv->entries,
852 refstr, GINT_TO_POINTER (1));
854 return TRUE;