/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * arch-tag: Implementation of playlist source object * * Copyright (C) 2002 Jorn Baayen * Copyright (C) 2003 Colin Walters * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include #include #include #include "rb-stock-icons.h" #include "rb-entry-view.h" #include "rb-search-entry.h" #include "rb-file-helpers.h" #include "rb-preferences.h" #include "rb-dialog.h" #include "rb-util.h" #include "rb-playlist-source.h" #if defined(WITH_CD_BURNER_SUPPORT) #include "rb-playlist-source-recorder.h" #endif #include "rb-debug.h" #include "eel-gconf-extensions.h" #include "rb-song-info.h" #define RB_PLAYLIST_XML_VERSION "1.0" #define RB_PLAYLIST_PLAYLIST (xmlChar *) "playlist" #define RB_PLAYLIST_TYPE (xmlChar *) "type" #define RB_PLAYLIST_AUTOMATIC (xmlChar *) "automatic" #define RB_PLAYLIST_STATIC (xmlChar *) "static" #define RB_PLAYLIST_NAME (xmlChar *) "name" #define RB_PLAYLIST_INT_NAME (xmlChar *) "internal-name" #define RB_PLAYLIST_LIMIT_COUNT (xmlChar *) "limit-count" #define RB_PLAYLIST_LIMIT_SIZE (xmlChar *) "limit-size" #define RB_PLAYLIST_SORT_KEY (xmlChar *) "sort-key" #define RB_PLAYLIST_SORT_DIRECTION (xmlChar *) "sort-direction" #define RB_PLAYLIST_LIMIT (xmlChar *) "limit" #define RB_PLAYLIST_LOCATION (xmlChar *) "location" static void rb_playlist_source_class_init (RBPlaylistSourceClass *klass); static void rb_playlist_source_init (RBPlaylistSource *source); static GObject *rb_playlist_source_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties); static void rb_playlist_source_dispose (GObject *object); static void rb_playlist_source_finalize (GObject *object); static void rb_playlist_source_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void rb_playlist_source_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); /* source methods */ static char *impl_get_status (RBSource *source); static const char *impl_get_browser_key (RBSource *source); static GdkPixbuf *impl_get_pixbuf (RBSource *source); static RBEntryView *impl_get_entry_view (RBSource *source); static gboolean impl_can_cut (RBSource *asource); static GList * impl_cut (RBSource *source); static void impl_paste (RBSource *asource, GList *entries); static void impl_delete (RBSource *source); static void impl_song_properties (RBSource *source); static gboolean impl_receive_drag (RBSource *source, GtkSelectionData *data); static gboolean impl_show_popup (RBSource *source); static void rb_playlist_source_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry, RBPlaylistSource *source); static void rb_playlist_source_songs_sort_order_changed_cb (RBEntryView *view, RBPlaylistSource *source); static void rb_playlist_source_songs_show_popup_cb (RBEntryView *view, RBPlaylistSource *playlist_view); static void rb_playlist_source_drop_cb (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time, gpointer user_data); static void rb_playlist_source_add_list_uri (RBPlaylistSource *source, GList *list); static void rb_playlist_source_do_query (RBPlaylistSource *source, GPtrArray *query, guint limit_count, guint limit_mb); #define PLAYLIST_SOURCE_SONGS_POPUP_PATH "/PlaylistViewPopup" #define PLAYLIST_SOURCE_POPUP_PATH "/PlaylistSourcePopup" #define PLAYLIST_SOURCE_AUTOMATIC_POPUP_PATH "/SmartPlaylistSourcePopup" struct RBPlaylistSourcePrivate { gboolean disposed; RhythmDB *db; gboolean automatic; GHashTable *entries; RhythmDBQueryModel *model; gboolean query_resetting; GtkWidget *vbox; GdkPixbuf *normal_pixbuf; GdkPixbuf *smartypants_pixbuf; RBEntryView *songs; gboolean dirty; char *title; }; enum { PROP_0, PROP_AUTOMATIC, PROP_DIRTY }; static GObjectClass *parent_class = NULL; static const GtkTargetEntry target_uri [] = { { "text/uri-list", 0, 0 } }; GType rb_playlist_source_get_type (void) { static GType rb_playlist_source_type = 0; if (rb_playlist_source_type == 0) { static const GTypeInfo our_info = { sizeof (RBPlaylistSourceClass), NULL, NULL, (GClassInitFunc) rb_playlist_source_class_init, NULL, NULL, sizeof (RBPlaylistSource), 0, (GInstanceInitFunc) rb_playlist_source_init }; rb_playlist_source_type = g_type_register_static (RB_TYPE_SOURCE, "RBPlaylistSource", &our_info, 0); } return rb_playlist_source_type; } static void rb_playlist_source_class_init (RBPlaylistSourceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); RBSourceClass *source_class = RB_SOURCE_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->dispose = rb_playlist_source_dispose; object_class->finalize = rb_playlist_source_finalize; object_class->constructor = rb_playlist_source_constructor; object_class->set_property = rb_playlist_source_set_property; object_class->get_property = rb_playlist_source_get_property; source_class->impl_get_status = impl_get_status; source_class->impl_get_browser_key = impl_get_browser_key; source_class->impl_get_pixbuf = impl_get_pixbuf; source_class->impl_get_entry_view = impl_get_entry_view; source_class->impl_can_rename = (RBSourceFeatureFunc) rb_true_function; source_class->impl_can_search = (RBSourceFeatureFunc) rb_false_function; source_class->impl_can_cut = impl_can_cut; source_class->impl_can_copy = (RBSourceFeatureFunc) rb_true_function; source_class->impl_can_delete = impl_can_cut; source_class->impl_cut = impl_cut; source_class->impl_paste = impl_paste; source_class->impl_delete = impl_delete; source_class->impl_song_properties = impl_song_properties; source_class->impl_can_pause = (RBSourceFeatureFunc) rb_true_function; source_class->impl_have_artist_album = (RBSourceFeatureFunc) rb_true_function; source_class->impl_have_url = (RBSourceFeatureFunc) rb_false_function; source_class->impl_receive_drag = impl_receive_drag; source_class->impl_show_popup = impl_show_popup; g_object_class_install_property (object_class, PROP_AUTOMATIC, g_param_spec_boolean ("automatic", "automatic", "whether this playlist is a smartypants", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_DIRTY, g_param_spec_boolean ("dirty", "dirty", "whether this playlist should be saved", FALSE, G_PARAM_READABLE)); } static void rb_playlist_source_track_cell_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *tree_model, GtkTreeIter *iter, RBPlaylistSource *source) { char *str; int val; gtk_tree_model_get (tree_model, iter, 1, &val, -1); if (val >= 0) str = g_strdup_printf ("%d", val); else str = g_strdup (""); g_object_set (G_OBJECT (renderer), "text", str, NULL); g_free (str); } static GObject * rb_playlist_source_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { RBPlaylistSource *source; RBPlaylistSourceClass *klass; GObjectClass *parent_class; GtkWidget *dummy = gtk_tree_view_new (); RBShell *shell; klass = RB_PLAYLIST_SOURCE_CLASS (g_type_class_peek (RB_TYPE_PLAYLIST_SOURCE)); parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); source = RB_PLAYLIST_SOURCE (parent_class->constructor (type, n_construct_properties, construct_properties)); g_object_get (G_OBJECT (source), "shell", &shell, NULL); g_object_get (RB_SHELL (shell), "db", &source->priv->db, NULL); g_object_unref (G_OBJECT (shell)); g_signal_connect_object (G_OBJECT (source->priv->db), "entry_added", G_CALLBACK (rb_playlist_source_entry_added_cb), source, 0); g_signal_connect_object (G_OBJECT (source->priv->db), "entry_restored", G_CALLBACK (rb_playlist_source_entry_added_cb), source, 0); source->priv->vbox = gtk_vbox_new (FALSE, 5); gtk_container_add (GTK_CONTAINER (source), source->priv->vbox); source->priv->model = rhythmdb_query_model_new_empty (source->priv->db); source->priv->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); source->priv->songs = rb_entry_view_new (source->priv->db, NULL, TRUE, TRUE); rb_entry_view_set_model (source->priv->songs, RHYTHMDB_QUERY_MODEL (source->priv->model)); { GtkTreeViewColumn *column = gtk_tree_view_column_new (); GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_clickable (column, TRUE); gtk_tree_view_column_set_cell_data_func (column, renderer, (GtkTreeCellDataFunc) rb_playlist_source_track_cell_data_func, source, NULL); rb_entry_view_append_column_custom (source->priv->songs, column, _("Tra_ck"), "PlaylistTrack", NULL, NULL); } rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TITLE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_GENRE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ARTIST); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ALBUM); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_DURATION); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_RATING); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED); g_signal_connect_object (G_OBJECT (source->priv->songs), "show_popup", G_CALLBACK (rb_playlist_source_songs_show_popup_cb), source, 0); g_signal_connect_object (G_OBJECT (source->priv->songs), "sort-order-changed", G_CALLBACK (rb_playlist_source_songs_sort_order_changed_cb), source, 0); g_signal_connect_object (G_OBJECT (source->priv->songs), "drag_data_received", G_CALLBACK (rb_playlist_source_drop_cb), source, 0); gtk_drag_dest_set (GTK_WIDGET (source->priv->songs), GTK_DEST_DEFAULT_ALL, target_uri, G_N_ELEMENTS (target_uri), GDK_ACTION_COPY); source->priv->normal_pixbuf = gtk_widget_render_icon (dummy, RB_STOCK_PLAYLIST, GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); source->priv->smartypants_pixbuf = gtk_widget_render_icon (dummy, RB_STOCK_AUTOMATIC_PLAYLIST, GTK_ICON_SIZE_LARGE_TOOLBAR, NULL); gtk_widget_destroy (dummy); rb_entry_view_set_columns_clickable (source->priv->songs, FALSE); source->priv->query_resetting = FALSE; gtk_box_pack_start_defaults (GTK_BOX (source->priv->vbox), GTK_WIDGET (source->priv->songs)); gtk_widget_show_all (GTK_WIDGET (source)); return G_OBJECT (source); } static void rb_playlist_source_songs_show_popup_cb (RBEntryView *view, RBPlaylistSource *playlist_view) { _rb_source_show_popup (RB_SOURCE (playlist_view), PLAYLIST_SOURCE_SONGS_POPUP_PATH); } static void rb_playlist_source_init (RBPlaylistSource *source) { source->priv = g_new0 (RBPlaylistSourcePrivate, 1); } static void rb_playlist_source_dispose (GObject *object) { RBPlaylistSource *source; source = RB_PLAYLIST_SOURCE (object); if (source->priv->disposed) return; source->priv->disposed = TRUE; g_object_unref (source->priv->db); } static void rb_playlist_source_finalize (GObject *object) { RBPlaylistSource *source; g_return_if_fail (object != NULL); g_return_if_fail (RB_IS_PLAYLIST_SOURCE (object)); source = RB_PLAYLIST_SOURCE (object); g_return_if_fail (source->priv != NULL); g_hash_table_destroy (source->priv->entries); g_free (source->priv->title); g_free (source->priv); G_OBJECT_CLASS (parent_class)->finalize (object); } static void rb_playlist_source_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (object); switch (prop_id) { case PROP_AUTOMATIC: source->priv->automatic = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void rb_playlist_source_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (object); switch (prop_id) { case PROP_AUTOMATIC: g_value_set_boolean (value, source->priv->automatic); break; case PROP_DIRTY: g_value_set_boolean (value, source->priv->dirty); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } RBSource * rb_playlist_source_new (RBShell *shell, gboolean automatic) { RBSource *source; source = RB_SOURCE (g_object_new (RB_TYPE_PLAYLIST_SOURCE, "name", _("Unknown"), "shell", shell, "automatic", automatic, NULL)); return source; } static void rb_playlist_source_entry_added_cb (RhythmDB *db, RhythmDBEntry *entry, RBPlaylistSource *source) { const char *location; if (source->priv->automatic) return; location = entry->location; if (g_hash_table_lookup (source->priv->entries, location)) { rhythmdb_query_model_add_entry (source->priv->model, entry); source->priv->dirty = TRUE; } } void rb_playlist_source_set_query (RBPlaylistSource *source, GPtrArray *query, guint limit_count, guint limit_mb, const char *sort_key, gint sort_direction) { g_assert (source->priv->automatic); source->priv->query_resetting = TRUE; /* playlists that aren't limited, with a particular sort order, are user-orderable */ rb_entry_view_set_columns_clickable (source->priv->songs, (limit_count == 0 && limit_mb == 0)); rb_entry_view_set_sorting_order (source->priv->songs, sort_key, sort_direction); rb_playlist_source_do_query (source, query, limit_count, limit_mb); rhythmdb_query_free (query); source->priv->query_resetting = FALSE; } void rb_playlist_source_get_query (RBPlaylistSource *source, GPtrArray **query, guint *limit_count, guint *limit_mb, const char **sort_key, gint *sort_direction) { g_assert (source->priv->automatic); g_object_get (G_OBJECT (source->priv->model), "query", query, "max-count", limit_count, "max-size", limit_mb, NULL); rb_entry_view_get_sorting_order (source->priv->songs, sort_key, sort_direction); } static char * impl_get_status (RBSource *asource) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); gchar *status; status = rhythmdb_compute_status_normal (rb_entry_view_get_num_entries (source->priv->songs), rb_entry_view_get_duration (source->priv->songs), rb_entry_view_get_total_size (source->priv->songs)); return status; } static const char * impl_get_browser_key (RBSource *source) { return NULL; } static GdkPixbuf * impl_get_pixbuf (RBSource *asource) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); return source->priv->automatic ? source->priv->smartypants_pixbuf : source->priv->normal_pixbuf; } static RBEntryView * impl_get_entry_view (RBSource *asource) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); return source->priv->songs; } static gboolean impl_can_cut (RBSource *asource) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); return !source->priv->automatic; } static GList * impl_cut (RBSource *asource) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); GList *sel = rb_entry_view_get_selected_entries (source->priv->songs); GList *tem; for (tem = sel; tem; tem = tem->next) rb_playlist_source_remove_entry (source, tem->data); return sel; } static void impl_paste (RBSource *asource, GList *entries) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); for (; entries; entries = g_list_next (entries)) rb_playlist_source_add_entry (source, entries->data); } static void impl_delete (RBSource *asource) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); GList *sel, *tem; sel = rb_entry_view_get_selected_entries (source->priv->songs); for (tem = sel; tem != NULL; tem = tem->next) rb_playlist_source_remove_entry (source, tem->data); g_list_free (sel); } static void impl_song_properties (RBSource *asource) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); GtkWidget *song_info = NULL; g_return_if_fail (source->priv->songs != NULL); song_info = rb_song_info_new (source->priv->songs); if (song_info) gtk_widget_show_all (song_info); else rb_debug ("failed to create dialog, or no selection!"); } static RhythmDBPropType rb_playlist_source_drag_atom_to_prop (GdkAtom smasher) { if (smasher == gdk_atom_intern ("text/x-rhythmbox-album", TRUE)) return RHYTHMDB_PROP_ALBUM; else if (smasher == gdk_atom_intern ("text/x-rhythmbox-artist", TRUE)) return RHYTHMDB_PROP_ARTIST; else if (smasher == gdk_atom_intern ("text/x-rhythmbox-genre", TRUE)) return RHYTHMDB_PROP_GENRE; else { g_assert_not_reached (); return 0; } } static gboolean impl_receive_drag (RBSource *asource, GtkSelectionData *data) { GList *list; RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); if (data->type == gdk_atom_intern ("text/uri-list", TRUE)) { list = gnome_vfs_uri_list_parse ((char *) data->data); if (list != NULL) rb_playlist_source_add_list_uri (source, list); else return FALSE; } else { GPtrArray *query; GPtrArray *subquery; subquery = rhythmdb_query_parse (source->priv->db, RHYTHMDB_QUERY_PROP_EQUALS, rb_playlist_source_drag_atom_to_prop (data->type), data->data, RHYTHMDB_QUERY_END); query = rhythmdb_query_parse (source->priv->db, RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, RHYTHMDB_ENTRY_TYPE_SONG, RHYTHMDB_QUERY_SUBQUERY, subquery, RHYTHMDB_QUERY_END); rb_playlist_source_set_query (source, query, 0, 0, NULL, 0); } return TRUE; } static gboolean impl_show_popup (RBSource *asource) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); if (source->priv->automatic) { _rb_source_show_popup (RB_SOURCE (asource), PLAYLIST_SOURCE_AUTOMATIC_POPUP_PATH); } else { _rb_source_show_popup (RB_SOURCE (asource), PLAYLIST_SOURCE_POPUP_PATH); } return TRUE; } static void rb_playlist_source_drop_cb (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time, gpointer user_data) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (user_data); GtkTargetList *tlist; GdkAtom target; tlist = gtk_target_list_new (target_uri, G_N_ELEMENTS (target_uri)); target = gtk_drag_dest_find_target (widget, context, tlist); gtk_target_list_unref (tlist); if (target == GDK_NONE) return; impl_receive_drag (RB_SOURCE (source), data); gtk_drag_finish (context, TRUE, FALSE, time); } void rb_playlist_source_add_location (RBPlaylistSource *source, const char *location) { RhythmDBEntry *entry; if (g_hash_table_lookup (source->priv->entries, location)) { return; } g_hash_table_insert (source->priv->entries, g_strdup (location), GINT_TO_POINTER (1)); entry = rhythmdb_entry_lookup_by_location (source->priv->db, location); if (entry) rhythmdb_entry_ref (source->priv->db, entry); if (entry != NULL) { rhythmdb_query_model_add_entry (source->priv->model, entry); rhythmdb_entry_unref (source->priv->db, entry); source->priv->dirty = TRUE; } } void rb_playlist_source_remove_location (RBPlaylistSource *source, const char *location) { RhythmDBEntry *entry; g_return_if_fail (g_hash_table_lookup (source->priv->entries, location) != NULL); g_hash_table_remove (source->priv->entries, location); entry = rhythmdb_entry_lookup_by_location (source->priv->db, location); if (entry != NULL) { rhythmdb_query_model_remove_entry (source->priv->model, entry); source->priv->dirty = TRUE; } } void rb_playlist_source_add_entry (RBPlaylistSource *source, RhythmDBEntry *entry) { rb_playlist_source_add_location (source, entry->location); } void rb_playlist_source_remove_entry (RBPlaylistSource *source, RhythmDBEntry *entry) { rb_playlist_source_remove_location (source, entry->location); } static void rb_playlist_source_add_list_uri (RBPlaylistSource *source, GList *list) { GList *i, *uri_list = NULL; g_return_if_fail (list != NULL); for (i = list; i != NULL; i = g_list_next (i)) uri_list = g_list_append (uri_list, gnome_vfs_uri_to_string ((const GnomeVFSURI *) i->data, 0)); gnome_vfs_uri_list_free (list); if (uri_list == NULL) return; for (i = uri_list; i != NULL; i = i->next) { char *uri = i->data; if (uri != NULL) { rhythmdb_add_uri (source->priv->db, uri); rb_playlist_source_add_location (source, uri); } g_free (uri); } g_list_free (uri_list); } static void playlist_iter_func (GtkTreeModel *model, GtkTreeIter *iter, char **uri, char **title, gpointer user_data) { RhythmDBEntry *entry; gtk_tree_model_get (model, iter, 0, &entry, -1); *uri = g_strdup (entry->location); *title = g_strdup (rb_refstring_get (entry->title)); } void rb_playlist_source_save_playlist (RBPlaylistSource *source, const char *uri) { TotemPlParser *playlist; GError *error = NULL; rb_debug ("saving playlist"); playlist = totem_pl_parser_new (); totem_pl_parser_write (playlist, GTK_TREE_MODEL (source->priv->model), playlist_iter_func, uri, TOTEM_PL_PARSER_PLS, NULL, &error); if (error != NULL) rb_error_dialog (NULL, _("Couldn't save playlist"), "%s", error->message); } #if defined(WITH_CD_BURNER_SUPPORT) static void burn_playlist_iter_func (GtkTreeModel *model, GtkTreeIter *iter, char **uri, char **artist, char **title, gulong *duration) { RhythmDBEntry *entry; gtk_tree_model_get (model, iter, 0, &entry, -1); *uri = g_strdup (entry->location); *artist = g_strdup (rb_refstring_get (entry->artist)); *title = g_strdup (rb_refstring_get (entry->title)); *duration = entry->duration; } #endif void rb_playlist_source_burn_playlist (RBPlaylistSource *source) { #if defined(WITH_CD_BURNER_SUPPORT) GtkWidget *recorder; char *name; RBShell *shell; if (g_hash_table_size (source->priv->entries) == 0) return; rb_debug ("burning playlist"); g_object_get (source, "name", &name, "shell", &shell, NULL); recorder = rb_playlist_source_recorder_new (gtk_widget_get_toplevel (GTK_WIDGET (source)), shell, name); g_object_unref (shell); g_free (name); rb_playlist_source_recorder_add_from_model (RB_PLAYLIST_SOURCE_RECORDER (recorder), GTK_TREE_MODEL (source->priv->model), burn_playlist_iter_func, NULL); g_signal_connect (recorder, "response", G_CALLBACK (gtk_widget_destroy), NULL); gtk_widget_show (recorder); #endif } RBSource * rb_playlist_source_new_from_xml (RBShell *shell, xmlNodePtr node) { RBPlaylistSource *source; xmlNodePtr child; xmlChar *tmp; source = RB_PLAYLIST_SOURCE (rb_playlist_source_new (shell, FALSE)); tmp = xmlGetProp (node, RB_PLAYLIST_TYPE); if (!xmlStrcmp (tmp, RB_PLAYLIST_AUTOMATIC)) source->priv->automatic = TRUE; g_free (tmp); tmp = xmlGetProp (node, RB_PLAYLIST_NAME); g_object_set (G_OBJECT (source), "name", tmp, NULL); g_free (tmp); tmp = xmlGetProp (node, RB_PLAYLIST_INT_NAME); if (!tmp) { GTimeVal serial; /* Hm. Upgrades. */ g_get_current_time (&serial); tmp = (xmlChar *) g_strdup_printf ("", serial.tv_sec, serial.tv_usec); } g_object_set (G_OBJECT (source), "internal-name", tmp, NULL); g_free (tmp); if (source->priv->automatic) { GPtrArray *query; gint limit_count = 0; gint limit_mb = 0; gchar *sort_key = NULL; gint sort_direction = 0; child = node->children; while (xmlNodeIsText (child)) child = child->next; query = rhythmdb_query_deserialize (source->priv->db, child); tmp = xmlGetProp (node, RB_PLAYLIST_LIMIT_COUNT); if (!tmp) /* Backwards compatibility */ tmp = xmlGetProp (node, RB_PLAYLIST_LIMIT); if (tmp) { limit_count = atoi ((char*) tmp); g_free (tmp); } tmp = xmlGetProp (node, RB_PLAYLIST_LIMIT_SIZE); if (tmp) { limit_mb = atoi ((char*) tmp); g_free (tmp); } sort_key = (gchar*) xmlGetProp (node, RB_PLAYLIST_SORT_KEY); if (sort_key && *sort_key) { tmp = xmlGetProp (node, RB_PLAYLIST_SORT_DIRECTION); if (tmp) { sort_direction = atoi ((char*) tmp); g_free (tmp); } } else { g_free (sort_key); sort_key = NULL; sort_direction = 0; } rb_playlist_source_set_query (source, query, limit_count, limit_mb, sort_key, sort_direction); g_free (sort_key); } else { for (child = node->children; child; child = child->next) { xmlChar *location; if (xmlNodeIsText (child)) continue; if (xmlStrcmp (child->name, RB_PLAYLIST_LOCATION)) continue; location = xmlNodeGetContent (child); rb_playlist_source_add_location (source, (char *) location); } } return RB_SOURCE (source); } void rb_playlist_source_save_to_xml (RBPlaylistSource *source, xmlNodePtr parent_node) { xmlNodePtr node; xmlChar *name; xmlChar *internal_name; GtkTreeIter iter; node = xmlNewChild (parent_node, NULL, RB_PLAYLIST_PLAYLIST, NULL); g_object_get (G_OBJECT (source), "name", &name, "internal-name", &internal_name, NULL); xmlSetProp (node, RB_PLAYLIST_NAME, name); xmlSetProp (node, RB_PLAYLIST_INT_NAME, internal_name); xmlSetProp (node, RB_PLAYLIST_TYPE, source->priv->automatic ? RB_PLAYLIST_AUTOMATIC : RB_PLAYLIST_STATIC); if (!source->priv->automatic) { if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (source->priv->model), &iter)) return; do { xmlNodePtr child_node = xmlNewChild (node, NULL, RB_PLAYLIST_LOCATION, NULL); RhythmDBEntry *entry; xmlChar *encoded; gtk_tree_model_get (GTK_TREE_MODEL (source->priv->model), &iter, 0, &entry, -1); encoded = xmlEncodeEntitiesReentrant (NULL, BAD_CAST entry->location); xmlNodeSetContent (child_node, encoded); g_free (encoded); } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (source->priv->model), &iter)); } else { GPtrArray *query; guint max_count; guint max_size_mb; const gchar *sort_key; gint sort_direction; gchar *temp_str; rb_playlist_source_get_query (source, &query, &max_count, &max_size_mb, &sort_key, &sort_direction); temp_str = g_strdup_printf ("%d", max_count); xmlSetProp (node, RB_PLAYLIST_LIMIT_COUNT, BAD_CAST temp_str); g_free (temp_str); temp_str = g_strdup_printf ("%d", max_size_mb); xmlSetProp (node, RB_PLAYLIST_LIMIT_SIZE, BAD_CAST temp_str); g_free (temp_str); if (sort_key && *sort_key) { xmlSetProp (node, RB_PLAYLIST_SORT_KEY, BAD_CAST sort_key); temp_str = g_strdup_printf ("%d", sort_direction); xmlSetProp (node, RB_PLAYLIST_SORT_DIRECTION, BAD_CAST temp_str); g_free (temp_str); } rhythmdb_query_serialize (source->priv->db, query, node); } source->priv->dirty = FALSE; } static void rb_playlist_source_songs_sort_order_changed_cb (RBEntryView *view, RBPlaylistSource *source) { GPtrArray *query; guint limit_count; guint limit_mb; const gchar *sort_key; gint sort_direction; g_assert (source->priv->automatic); /* don't process this if we are in the middle of setting a query */ if (source->priv->query_resetting) return; rb_debug ("sort order changed"); /* need to re-run query with the same settings*/ g_object_get (G_OBJECT (source->priv->model), "query", &query, "max-count", &limit_count, "max-size", &limit_mb, NULL); rb_playlist_source_do_query (source, query, limit_count, limit_mb); rhythmdb_query_free (query); } static void rb_playlist_source_do_query (RBPlaylistSource *source, GPtrArray *query, guint limit_count, guint limit_mb) { g_assert (source->priv->automatic); source->priv->model = g_object_new (RHYTHMDB_TYPE_QUERY_MODEL, "db", source->priv->db, "max-count", limit_count, "max-size", limit_mb, NULL); rb_entry_view_set_model (source->priv->songs, source->priv->model); rhythmdb_do_full_query_async_parsed (source->priv->db, GTK_TREE_MODEL (source->priv->model), query); }