/* * Copyright 2016 (c) Anna Schumaker. */ #include #include #include #include #include #include #include #include enum playlist_sidebar_columns { P_SB_IMAGE, P_SB_NAME, P_SB_TYPE, }; static gchar *p_name = NULL; static bool p_filter_enable = true; static bool p_init_done = false; static void __playlist_update_sizes(struct queue *); static inline void __playlist_filter_get_iter(GtkTreeIter *iter, GtkTreeIter *child) { gtk_tree_model_filter_convert_iter_to_child_iter(gui_sidebar_filter(), child, iter); } static void __playlist_set_size(GtkTreeIter *iter, const gchar *name) { enum playlist_type_t type = gui_sidebar_iter_type(iter); unsigned int size = playlist_size(type, name); const gchar *fmt = "%s\n%d track%s"; gchar *text; if (playlist_cur() == playlist_get(type, name)) fmt = "%s\n%d track%s"; text = g_markup_printf_escaped(fmt, name, size, (size == 1) ? "" : "s"); gtk_tree_store_set(gui_sidebar_store(), iter, P_SB_NAME, text, -1); g_free(text); } void __playlist_selection_changed(GtkTreeSelection *selection, gpointer data) { GtkTreeModel *model = gui_sidebar_model(); GtkTreeIter iter, child; struct queue *queue; if (gtk_tree_selection_get_selected(selection, &model, &iter)) { __playlist_filter_get_iter(&iter, &child); if (p_name) g_free(p_name); p_name = gui_sidebar_iter_name(&child); queue = playlist_get_queue(gui_sidebar_iter_type(&child), p_name); gui_queue_show(gui_queue(queue)); } } gboolean __playlist_on_select(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean selected, gpointer data) { GtkTreeIter iter, child; gtk_tree_model_get_iter(model, &iter, path); __playlist_filter_get_iter(&iter, &child); return gui_sidebar_iter_type(&child) != PL_MAX_TYPE; } bool __playlist_keypress(GtkTreeView *treeview, GdkEventKey *event, gpointer data) { GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); GtkTreeModel *model = gui_sidebar_model(); GtkTreeIter iter, child; gchar *name = NULL; GtkTreePath *path; GList *rows; if (event->keyval != GDK_KEY_Delete) return false; rows = gtk_tree_selection_get_selected_rows(selection, &model); path = rows->data; if (!gtk_tree_model_get_iter(model, &iter, path)) return false; __playlist_filter_get_iter(&iter, &child); name = gui_sidebar_iter_name(&child); if (!name) goto out; if (playlist_delete(gui_sidebar_iter_type(&child), name)) gtk_tree_store_remove(gui_sidebar_store(), &child); g_free(name); out: g_list_free_full(rows, (GDestroyNotify)gtk_tree_path_free); return true; } void __playlist_row_activated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) { enum playlist_type_t type; GtkTreeIter iter, child; GtkTreeModel *model; struct queue *queue; unsigned int id; gchar *name; type = settings_get("core.playlist.cur.type"); id = settings_get("core.playlist.cur.id"); model = GTK_TREE_MODEL(gui_sidebar_filter()); gtk_tree_model_get_iter(model, &iter, path); __playlist_filter_get_iter(&iter, &child); name = gui_sidebar_iter_name(&child); playlist_select(gui_sidebar_iter_type(&child), name); __playlist_set_size(&child, name); g_free(name); name = playlist_get_name(type, id); queue = playlist_get_queue(type, name); __playlist_update_sizes(queue); g_free(name); } static bool __playlist_queue_set_size(struct queue *queue, GtkTreeIter *iter) { struct gui_queue *gq = gui_queue(queue); bool match = (gui_sidebar_iter_type(iter) == gq->gq_playlist->pl_type); gchar *name = gui_sidebar_iter_name(iter); if (match) match = string_match(name, gq->gq_text); if (match) __playlist_set_size(iter, name); g_free(name); return match; } static void __playlist_update_sizes(struct queue *queue) { GtkTreeModelFilter *filter; GtkTreeIter parent, iter; if (!gui_sidebar_iter_first(&parent)) return; do { if (__playlist_queue_set_size(queue, &parent)) goto out; if (gtk_tree_model_iter_children(gui_sidebar_model(), &iter, &parent)) { do { if (__playlist_queue_set_size(queue, &iter)) goto out; } while (gui_sidebar_iter_next(&iter)); } } while (gui_sidebar_iter_next(&parent)); out: filter = gui_sidebar_filter(); gtk_tree_model_filter_refilter(filter); } static void *__playlist_init(struct queue *queue, void *data) { struct playlist *playlist = (struct playlist *)data; unsigned int flags = 0; if (!string_match(playlist->pl_name, "History")) flags = GQ_CAN_RANDOM; if (p_init_done && playlist->pl_type == PL_ARTIST) { p_filter_enable = false; gui_playlist_add_artist(playlist); p_filter_enable = true; } if (p_init_done && playlist->pl_type == PL_USER) gui_playlist_add_user(playlist); return gui_queue_alloc(playlist, queue, playlist->pl_name, flags); } static void __playlist_deinit(struct queue *queue) { gui_filter_clear_search(gui_queue(queue)->gq_playlist); gui_queue_free(queue); } static void __playlist_added(struct queue *queue, unsigned int row) { gui_model_add(gui_queue(queue)->gq_playlist, row); __playlist_update_sizes(queue); } static void __playlist_removed(struct queue *queue, unsigned int row) { gui_model_remove(gui_queue(queue)->gq_playlist, row); __playlist_update_sizes(queue); } static void __playlist_cleared(struct queue *queue, unsigned int n) { gui_model_clear(gui_queue(queue)->gq_playlist, n); __playlist_update_sizes(queue); } static void __playlist_updated(struct queue *queue, unsigned int n) { gui_model_update(gui_queue(queue)->gq_playlist, n); } static bool __playlist_erase(struct queue *queue, struct track *track) { enum playlist_type_t type = gui_queue(queue)->gq_playlist->pl_type; const gchar *name = gui_queue(queue)->gq_playlist->pl_name; switch (type) { case PL_SYSTEM: if (string_match(name, "Collection")) name = "Hidden"; else if (!string_match(name, "Favorites") && !string_match(name, "Hidden") && !string_match(name, "Queued Tracks")) break; case PL_USER: playlist_remove(type, name, track); default: break; }; return false; } bool __gui_playlist_init_idle() { struct db_entry *dbe, *next; GtkTreeSelection *selection; GtkTreeView *treeview; GtkTreeModel *filter; GtkTreeIter iter; filter = GTK_TREE_MODEL(gui_sidebar_filter()); treeview = gui_sidebar_treeview(); selection = gtk_tree_view_get_selection(treeview); gtk_tree_model_get_iter_first(filter, &iter); gtk_tree_selection_select_iter(selection, &iter); p_filter_enable = false; db_for_each(dbe, next, pl_user_db_get()) gui_playlist_add_user(&USER_PLAYLIST(dbe)->pl_playlist); gtk_tree_view_expand_all(treeview); db_for_each(dbe, next, artist_db_get()) gui_playlist_add_artist(ARTIST(dbe)->ar_playlist); p_filter_enable = true; p_init_done = true; return true; } void gui_playlist_init() { GtkTreeView *treeview; GtkTreeIter parent; treeview = gui_sidebar_treeview(); gui_sidebar_iter_first(&parent); gui_sidebar_iter_add(&parent, playlist_get(PL_SYSTEM, "Queued Tracks"), "audio-x-generic"); gui_sidebar_iter_add(&parent, playlist_get(PL_SYSTEM, "Collection"), "media-optical"); gui_sidebar_iter_add(&parent, playlist_get(PL_SYSTEM, "History"), "document-open-recent"); /* Add "Playlists" header and playlists. */ gui_sidebar_iter_find(&parent, "Playlists", PL_MAX_TYPE); gui_sidebar_iter_append_child(&parent, playlist_get(PL_SYSTEM, "Favorites"), "emblem-favorite"); gui_sidebar_iter_append_child(&parent, playlist_get(PL_SYSTEM, "Hidden"), "window-close"); /* Add "Dynamic" header. */ gui_sidebar_iter_find(&parent, "Dynamic", PL_MAX_TYPE); gui_sidebar_iter_append_child(&parent, playlist_get(PL_SYSTEM, "Most Played"), "go-up"); gui_sidebar_iter_append_child(&parent, playlist_get(PL_SYSTEM, "Least Played"), "go-down"); gui_sidebar_iter_append_child(&parent, playlist_get(PL_SYSTEM, "Unplayed"), "audio-x-generic"); gtk_tree_selection_set_select_function( gtk_tree_view_get_selection(treeview), __playlist_on_select, NULL, NULL); idle_schedule(IDLE_SYNC, __gui_playlist_init_idle, NULL); } gchar *gui_playlist_cur() { return p_name; } void gui_playlist_add_library(struct playlist *playlist) { GtkTreeIter parent; gui_sidebar_iter_first(&parent); gui_sidebar_iter_find(&parent, "Library", PL_MAX_TYPE); gui_sidebar_iter_sort_child(&parent, playlist, "folder"); } void gui_playlist_add_artist(struct playlist *playlist) { GtkTreeIter parent; gui_sidebar_iter_first(&parent); gui_sidebar_iter_find(&parent, "Collection", PL_SYSTEM); gui_sidebar_iter_sort_child(&parent, playlist, "system-users"); } void gui_playlist_add_user(struct playlist *playlist) { GtkTreeIter parent; gui_sidebar_iter_first(&parent); gui_sidebar_iter_find(&parent, "Playlists", PL_MAX_TYPE); gui_sidebar_iter_sort_child(&parent, playlist, "text-x-generic"); } struct queue_ops playlist_ops = { .qop_init = __playlist_init, .qop_deinit = __playlist_deinit, .qop_added = __playlist_added, .qop_erase = __playlist_erase, .qop_removed = __playlist_removed, .qop_cleared = __playlist_cleared, .qop_updated = __playlist_updated, };