static void rb_static_playlist_source_add_location_internal (RBStaticPlaylistSource *source, const char *location, gint index) { RhythmDB *db; RhythmDBEntry *entry; RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source); if (rb_playlist_source_location_in_map (psource, location)) return; db = rb_playlist_source_get_db (psource); entry = rhythmdb_entry_lookup_by_location (db, location); if (entry) { RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source); if (_rb_source_check_entry_type (RB_SOURCE (source), entry)) { rhythmdb_entry_ref (entry); rhythmdb_query_model_add_entry (priv->base_model, entry, index); rhythmdb_entry_unref (entry); } } rb_playlist_source_add_to_map (psource, location); rb_playlist_source_mark_dirty (psource); }
static void rb_static_playlist_source_add_id_list (RBStaticPlaylistSource *source, GList *list) { RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source); GList *i; gint id; g_return_if_fail (list != NULL); for (i = list; i != NULL; i = i->next) { RhythmDBEntry *entry; id = strtoul ((const char *)i->data, NULL, 0); if (id == 0) continue; entry = rhythmdb_entry_lookup_by_id (rb_playlist_source_get_db (psource), id); if (entry == NULL) { rb_debug ("received id %d, but can't find the entry", id); continue; } rb_static_playlist_source_add_entry (source, entry, -1); } }
static void rb_static_playlist_source_add_uri_list (RBStaticPlaylistSource *source, GList *list) { GList *i, *uri_list = NULL; RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source); RhythmDBEntry *entry; g_return_if_fail (list != NULL); for (i = list; i != NULL; i = g_list_next (i)) { char *uri = (char *) i->data; uri_list = g_list_prepend (uri_list, rb_canonicalise_uri (uri)); } uri_list = g_list_reverse (uri_list); if (uri_list == NULL) return; for (i = uri_list; i != NULL; i = i->next) { char *uri = i->data; if (uri != NULL) { entry = rhythmdb_entry_lookup_by_location (rb_playlist_source_get_db (psource), uri); if (entry == NULL) rhythmdb_add_uri (rb_playlist_source_get_db (psource), uri); rb_static_playlist_source_add_location (source, uri, -1); } g_free (uri); } g_list_free (uri_list); }
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; rb_display_page_receive_drag (RB_DISPLAY_PAGE (source), data); gtk_drag_finish (context, TRUE, FALSE, time); }
static void rb_playlist_source_dispose (GObject *object) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (object); if (source->priv->dispose_has_run) { /* If dispose did already run, return. */ rb_debug ("Dispose has already run for playlist source %p", object); return; } /* Make sure dispose does not run twice. */ source->priv->dispose_has_run = TRUE; rb_debug ("Disposing playlist source %p", source); if (source->priv->db != NULL) { g_object_unref (source->priv->db); source->priv->db = NULL; } if (source->priv->model != NULL) { g_object_unref (source->priv->model); source->priv->model = NULL; } G_OBJECT_CLASS (rb_playlist_source_parent_class)->dispose (object); }
static RBEntryView * impl_get_entry_view (RBSource *asource) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (asource); return source->priv->songs; }
static void rb_static_playlist_source_rows_reordered (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gint *order_map, RBStaticPlaylistSource *source) { rb_playlist_source_mark_dirty (RB_PLAYLIST_SOURCE (source)); }
static GPtrArray * construct_query_from_selection (RBStaticPlaylistSource *source) { RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source); RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source); RhythmDB *db = rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (psource)); GPtrArray *query = NULL; query = g_ptr_array_new(); if (priv->search_query != NULL) { rhythmdb_query_append (db, query, RHYTHMDB_QUERY_SUBQUERY, priv->search_query, RHYTHMDB_QUERY_END); } return query; }
static void rb_static_playlist_source_non_entry_dropped (GtkTreeModel *model, const char *uri, int position, RBStaticPlaylistSource *source) { g_assert (g_utf8_strlen (uri, -1) > 0); rhythmdb_add_uri (rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source)), uri); rb_static_playlist_source_add_location (source, uri, position); }
/** * rb_static_playlist_source_move_entry: * @source: an #RBStaticPlaylistSource * @entry: the entry to move * @index: new location for the entry * * Moves an entry within the playlist. */ void rb_static_playlist_source_move_entry (RBStaticPlaylistSource *source, RhythmDBEntry *entry, gint index) { RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source); RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source); rhythmdb_query_model_move_entry (priv->base_model, entry, index); rb_playlist_source_mark_dirty (psource); }
/** * rb_playlist_source_new_from_xml: * @shell: the #RBShell instance * @node: libxml node containing the playlist * * Constructs a playlist source instance from the XML serialized * format. This function knows about all the playlist types that * can be saved to disk, and it hands off the XML node to the * appropriate constructor based on the 'type' attribute of * the root node of the playlist. * * Return value: the playlist */ RBSource * rb_playlist_source_new_from_xml (RBShell *shell, xmlNodePtr node) { RBSource *source = NULL; xmlChar *tmp; xmlChar *name; g_return_val_if_fail (RB_IS_SHELL (shell), NULL); /* Try to get name from XML and remove translated names */ name = get_playlist_name_from_xml (node); tmp = xmlGetProp (node, RB_PLAYLIST_TYPE); if (!xmlStrcmp (tmp, RB_PLAYLIST_AUTOMATIC)) source = rb_auto_playlist_source_new_from_xml (shell, (const char *)name, node); else if (!xmlStrcmp (tmp, RB_PLAYLIST_STATIC)) source = rb_static_playlist_source_new_from_xml (shell, (const char *)name, node); else if (!xmlStrcmp (tmp, RB_PLAYLIST_QUEUE)) { RBStaticPlaylistSource *queue; g_object_get (shell, "queue-source", &queue, NULL); rb_static_playlist_source_load_from_xml (queue, node); apply_source_settings (RB_PLAYLIST_SOURCE (queue), node); g_object_unref (queue); } else { g_warning ("attempting to load playlist '%s' of unknown type '%s'", name, tmp); } xmlFree (name); xmlFree (tmp); if (source != NULL) { apply_source_settings (RB_PLAYLIST_SOURCE (source), node); } return source; }
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 (asource, NULL); if (song_info) gtk_widget_show_all (song_info); else rb_debug ("failed to create dialog, or no selection!"); }
static void rb_static_playlist_source_browser_changed_cb (RBLibraryBrowser *browser, GParamSpec *pspec, RBStaticPlaylistSource *source) { RBEntryView *songs = rb_source_get_entry_view (RB_SOURCE (source)); RhythmDBQueryModel *query_model; g_object_get (browser, "output-model", &query_model, NULL); rb_entry_view_set_model (songs, query_model); rb_playlist_source_set_query_model (RB_PLAYLIST_SOURCE (source), query_model); g_object_unref (query_model); rb_source_notify_filter_changed (RB_SOURCE (source)); }
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_LOCAL: source->priv->is_local = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
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); rb_debug ("Finalizing playlist source %p", source); g_hash_table_destroy (source->priv->entries); g_free (source->priv->title); source->priv = NULL; G_OBJECT_CLASS (rb_playlist_source_parent_class)->finalize (object); }
static void impl_search (RBSource *source, RBSourceSearch *search, const char *cur_text, const char *new_text) { RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source); RhythmDB *db; if (search == NULL) { search = priv->default_search; } /* replace our search query */ if (priv->search_query != NULL) { rhythmdb_query_free (priv->search_query); priv->search_query = NULL; } db = rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source)); priv->search_query = rb_source_search_create_query (search, db, new_text); rb_static_playlist_source_do_query (RB_STATIC_PLAYLIST_SOURCE (source)); }
/** * rb_static_playlist_source_remove_location: * @source: an #RBStaticPlaylistSource * @location: location to remove * * Removes the specified location from the playlist. This affects both * the location map and the query model, whether an entry exists for the * location or not. */ void rb_static_playlist_source_remove_location (RBStaticPlaylistSource *source, const char *location) { RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source); RhythmDB *db; RhythmDBEntry *entry; g_return_if_fail (rb_playlist_source_location_in_map (psource, location)); db = rb_playlist_source_get_db (psource); entry = rhythmdb_entry_lookup_by_location (db, location); if (entry != NULL) { RhythmDBQueryModel *model = rb_playlist_source_get_query_model (psource); /* if this fails, the model and the playlist are out of sync */ g_assert (rhythmdb_query_model_remove_entry (model, entry)); rb_playlist_source_mark_dirty (psource); } }
static void rb_static_playlist_source_do_query (RBStaticPlaylistSource *source) { RBStaticPlaylistSourcePrivate *priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source); RBPlaylistSource *psource = RB_PLAYLIST_SOURCE (source); RhythmDB *db = rb_playlist_source_get_db (psource); GPtrArray *query; if (priv->filter_model != NULL) { g_object_unref (priv->filter_model); } priv->filter_model = rhythmdb_query_model_new_empty (db); g_object_set (priv->filter_model, "base-model", priv->base_model, NULL); query = construct_query_from_selection (source); g_object_set (priv->filter_model, "query", query, NULL); rhythmdb_query_free (query); rhythmdb_query_model_reapply_query (priv->filter_model, TRUE); rb_library_browser_set_model (priv->browser, priv->filter_model, FALSE); }
/** * rb_static_playlist_source_add_location: * @source: an #RBStaticPlaylistSource * @location: location (URI) to add to the playlist * @index: position at which to add the location (-1 to add at the end) * * If the location matches an entry in the database, the entry is added * to the playlist. Otherwise, if it identifies a directory, the contents * of that directory are added. */ void rb_static_playlist_source_add_location (RBStaticPlaylistSource *source, const char *location, gint index) { RhythmDB *db; RhythmDBEntry *entry; db = rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source)); entry = rhythmdb_entry_lookup_by_location (db, location); /* if there is an entry, it won't be a directory */ if (entry == NULL && rb_uri_is_directory (location)) rb_uri_handle_recursively (location, NULL, (RBUriRecurseFunc) _add_location_cb, source); else rb_static_playlist_source_add_location_internal (source, location, index); }
static DMAPContainerRecord * rb_dmap_container_db_adapter_lookup_by_id (DMAPContainerDb *db, guint id) { gchar *name; GList *playlists; DMAPContainerRecord *fnval = NULL; playlists = rb_playlist_manager_get_playlists (RB_DMAP_CONTAINER_DB_ADAPTER (db)->priv->playlist_manager); if (playlists != NULL && playlists->data != NULL) { GList *result; result = g_list_find_custom (playlists, GINT_TO_POINTER (id), (GCompareFunc) find_by_id); if (result != NULL && result->data != NULL) { RBPlaylistSource *source; source = RB_PLAYLIST_SOURCE (result->data); g_object_get (source, "name", &name, NULL); fnval = DMAP_CONTAINER_RECORD (rb_daap_container_record_new (name, source)); } } g_list_free (playlists); return fnval; }
static gboolean impl_can_remove (RBDisplayPage *page) { RBPlaylistSource *source = RB_PLAYLIST_SOURCE (page); return (source->priv->is_local); }
static void rb_static_playlist_source_constructed (GObject *object) { RBStaticPlaylistSource *source; RBStaticPlaylistSourcePrivate *priv; RBPlaylistSource *psource; RBEntryView *songs; RBShell *shell; RhythmDBEntryType *entry_type; RB_CHAIN_GOBJECT_METHOD (rb_static_playlist_source_parent_class, constructed, object); source = RB_STATIC_PLAYLIST_SOURCE (object); priv = RB_STATIC_PLAYLIST_SOURCE_GET_PRIVATE (source); psource = RB_PLAYLIST_SOURCE (source); priv->base_model = rb_playlist_source_get_query_model (RB_PLAYLIST_SOURCE (psource)); g_object_set (priv->base_model, "show-hidden", TRUE, NULL); g_object_ref (priv->base_model); g_signal_connect_object (priv->base_model, "filter-entry-drop", G_CALLBACK (rb_static_playlist_source_filter_entry_drop), source, 0); priv->paned = gtk_vpaned_new (); g_object_get (source, "shell", &shell, NULL); priv->action_group = _rb_display_page_register_action_group (RB_DISPLAY_PAGE (source), "StaticPlaylistActions", NULL, 0, shell); if (gtk_action_group_get_action (priv->action_group, rb_static_playlist_source_radio_actions[0].name) == NULL) { gtk_action_group_add_radio_actions (priv->action_group, rb_static_playlist_source_radio_actions, G_N_ELEMENTS (rb_static_playlist_source_radio_actions), 0, NULL, NULL); rb_source_search_basic_create_for_actions (priv->action_group, rb_static_playlist_source_radio_actions, G_N_ELEMENTS (rb_static_playlist_source_radio_actions)); } priv->default_search = rb_source_search_basic_new (RHYTHMDB_PROP_SEARCH_MATCH); g_object_unref (shell); g_object_get (source, "entry-type", &entry_type, NULL); priv->browser = rb_library_browser_new (rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source)), entry_type); if (entry_type != NULL) { g_object_unref (entry_type); } gtk_paned_pack1 (GTK_PANED (priv->paned), GTK_WIDGET (priv->browser), TRUE, FALSE); g_signal_connect_object (priv->browser, "notify::output-model", G_CALLBACK (rb_static_playlist_source_browser_changed_cb), source, 0); rb_library_browser_set_model (priv->browser, priv->base_model, FALSE); rb_static_playlist_source_do_query (source); /* reparent the entry view */ songs = rb_source_get_entry_view (RB_SOURCE (source)); g_object_ref (songs); gtk_container_remove (GTK_CONTAINER (source), GTK_WIDGET (songs)); gtk_paned_pack2 (GTK_PANED (priv->paned), GTK_WIDGET (songs), TRUE, FALSE); gtk_container_add (GTK_CONTAINER (source), priv->paned); rb_source_bind_settings (RB_SOURCE (source), GTK_WIDGET (songs), priv->paned, GTK_WIDGET (priv->browser)); g_object_unref (songs); /* watch these to find out when things are dropped into the entry view */ g_signal_connect_object (priv->base_model, "row-inserted", G_CALLBACK (rb_static_playlist_source_row_inserted), source, 0); g_signal_connect_object (priv->base_model, "non-entry-dropped", G_CALLBACK (rb_static_playlist_source_non_entry_dropped), source, 0); g_signal_connect_object (priv->base_model, "rows-reordered", G_CALLBACK (rb_static_playlist_source_rows_reordered), source, 0); gtk_widget_show_all (GTK_WIDGET (source)); }
static void rb_playlist_source_constructed (GObject *object) { GObject *shell_player; RBPlaylistSource *source; RBShell *shell; RhythmDB *db; RhythmDBQueryModel *query_model; GtkBuilder *builder; GSettings *settings; RB_CHAIN_GOBJECT_METHOD (rb_playlist_source_parent_class, constructed, object); source = RB_PLAYLIST_SOURCE (object); g_object_get (source, "shell", &shell, NULL); g_object_get (shell, "db", &db, "shell-player", &shell_player, NULL); rb_playlist_source_set_db (source, db); g_object_unref (db); g_object_unref (shell); /* store playlist settings using the memory backend * this means the settings path doesn't have to be consistent, * it just has to be unique, so the address of the source object works. * for local playlists, we write the settings into the playlist file on disk * to make them persistent. */ g_object_get (source, "settings", &settings, NULL); if (settings == NULL) { char *path; path = g_strdup_printf ("/org/gnome/rhythmbox/playlist/%p/", source); settings = g_settings_new_with_backend_and_path ("org.gnome.rhythmbox.source", playlist_settings_backend, path); g_free (path); g_object_set (source, "settings", settings, NULL); } g_signal_connect (settings, "changed", G_CALLBACK (playlist_settings_changed_cb), source); g_object_unref (settings); builder = rb_builder_load ("playlist-popup.ui", NULL); source->priv->popup = G_MENU (gtk_builder_get_object (builder, "playlist-popup")); rb_application_link_shared_menus (RB_APPLICATION (g_application_get_default ()), source->priv->popup); g_object_ref (source->priv->popup); g_object_unref (builder); source->priv->entries = g_hash_table_new_full (rb_refstring_hash, rb_refstring_equal, (GDestroyNotify)rb_refstring_unref, NULL); source->priv->songs = rb_entry_view_new (source->priv->db, shell_player, TRUE, TRUE); g_object_unref (shell_player); g_signal_connect_object (source->priv->songs, "notify::sort-order", G_CALLBACK (rb_playlist_source_songs_sort_order_changed_cb), source, 0); query_model = rhythmdb_query_model_new_empty (source->priv->db); rb_playlist_source_set_query_model (source, query_model); g_object_unref (query_model); { const char *title = ""; const char *strings[3] = {0}; GtkTreeViewColumn *column = gtk_tree_view_column_new (); GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); g_object_set(renderer, "style", PANGO_STYLE_OBLIQUE, "weight", PANGO_WEIGHT_LIGHT, "xalign", 1.0, NULL); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); strings[0] = title; strings[1] = "9999"; rb_entry_view_set_fixed_column_width (source->priv->songs, column, renderer, strings); gtk_tree_view_column_set_cell_data_func (column, renderer, (GtkTreeCellDataFunc) rb_playlist_source_track_cell_data_func, source, NULL); rb_entry_view_insert_column_custom (source->priv->songs, column, title, "PlaylistTrack", NULL, 0, NULL, 0); } rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TITLE, TRUE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_GENRE, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ARTIST, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_COMPOSER, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ALBUM, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_YEAR, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_DURATION, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_QUALITY, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_RATING, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_COMMENT, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LOCATION, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_FIRST_SEEN, FALSE); rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_BPM, FALSE); rb_entry_view_set_columns_clickable (source->priv->songs, FALSE); rb_playlist_source_setup_entry_view (source, source->priv->songs); gtk_container_add (GTK_CONTAINER (source), GTK_WIDGET (source->priv->songs)); gtk_widget_show_all (GTK_WIDGET (source)); }