/* * Copyright 2016 (c) Anna Schumaker. */ #include #include #include #include #include #include enum playlist_sidebar_columns { P_SB_IMAGE, P_SB_NAME, P_SB_TYPE, }; static GtkTreeStore *p_store; static gchar *p_name = NULL; static inline enum playlist_type_t __playlist_type(GtkTreeIter *iter) { enum playlist_type_t type; gtk_tree_model_get(GTK_TREE_MODEL(p_store), iter, P_SB_TYPE, &type, -1); return (type < PL_MAX_TYPE) ? type : PL_SYSTEM; } static void __playlist_set(GtkTreeIter *iter, const gchar *name, const gchar *image, enum playlist_type_t type) { gtk_tree_store_set(p_store, iter, P_SB_NAME, name, P_SB_IMAGE, image, P_SB_TYPE, type, -1); } static void __playlist_set_size(GtkTreeIter *iter, const gchar *name) { GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(p_store), iter); unsigned int size = playlist_size(__playlist_type(iter), name); const gchar *fmt = "%s\n%d track%s"; gchar *text; if (gtk_tree_path_get_depth(path) == 1) fmt = "%s\n%d track%s"; text = g_markup_printf_escaped(fmt, name, size, (size == 1) ? "" : "s"); gtk_tree_store_set(p_store, iter, P_SB_NAME, text, -1); gtk_tree_path_free(path); g_free(text); } static void __playlist_add(GtkTreeIter *parent, const gchar *name, const gchar *image, enum playlist_type_t type) { GtkTreeIter iter; gtk_tree_store_insert(p_store, &iter, parent, -1); __playlist_set(&iter, name, image, type); __playlist_set_size(&iter, name); } static gchar *__playlist_name(GtkTreeIter *iter) { GtkTreeModel *model = GTK_TREE_MODEL(p_store); gchar *text, *parsed, *name, **split; gtk_tree_model_get(model, iter, P_SB_NAME, &text, -1); if (!text) return NULL; pango_parse_markup(text, -1, 0, NULL, &parsed, NULL, NULL); split = g_strsplit(parsed, "\n", 2); name = g_strdup(split[0]); g_strfreev(split); g_free(text); g_free(parsed); return name; } void __playlist_selection_changed(GtkTreeSelection *selection, gpointer data) { GtkTreeModel *model = GTK_TREE_MODEL(p_store); struct queue *queue; GtkTreeIter iter; if (gtk_tree_selection_get_selected(selection, &model, &iter)) { if (p_name) g_free(p_name); p_name = __playlist_name(&iter); queue = playlist_get_queue(__playlist_type(&iter), p_name); gui_sidebar_selected(SB_PLAYLIST, gui_queue(queue)); } } gboolean __playlist_on_select(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean selected, gpointer data) { struct queue *queue = NULL; GtkTreeIter iter; gchar *name; gtk_tree_model_get_iter(model, &iter, path); name = __playlist_name(&iter); queue = playlist_get_queue(__playlist_type(&iter), name); g_free(name); return queue != NULL; } bool __playlist_keypress(GtkTreeView *treeview, GdkEventKey *event, gpointer data) { GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); GtkTreeModel *model = GTK_TREE_MODEL(p_store); gchar *name = NULL; GtkTreePath *path; GtkTreeIter iter; 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)) name = __playlist_name(&iter); if (!name) goto out; if (playlist_delete(__playlist_type(&iter), name)) gtk_tree_store_remove(GTK_TREE_STORE(model), &iter); g_free(name); out: g_list_free_full(rows, (GDestroyNotify)gtk_tree_path_free); return true; } static bool __playlist_queue_set_size(struct queue *queue, GtkTreeIter *iter) { struct gui_queue *gq = gui_queue(queue); bool match = (__playlist_type(iter) == gq->gq_playlist->pl_type); gchar *name = __playlist_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) { GtkTreeModel *model = GTK_TREE_MODEL(p_store); GtkTreeIter parent, iter; if (!gtk_tree_model_get_iter_first(model, &parent)) return; do { if (__playlist_queue_set_size(queue, &parent)) return; if (gtk_tree_model_iter_children(model, &iter, &parent)) { do { if (__playlist_queue_set_size(queue, &iter)) return; } while (gtk_tree_model_iter_next(model, &iter)); } } while (gtk_tree_model_iter_next(model, &parent)); } 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, "Collection")) flags = GQ_CAN_RANDOM; return gui_queue_alloc(playlist, queue, playlist->pl_name, flags); } static void __playlist_added(struct queue *queue, unsigned int row) { gui_queue_added(queue, row); __playlist_update_sizes(queue); } static void __playlist_removed(struct queue *queue, unsigned int row) { gui_queue_removed(queue, row); __playlist_update_sizes(queue); } static bool __playlist_erase(struct queue *queue, struct track *track) { if (string_match(gui_playlist_cur(), "Collection")) playlist_add(PL_SYSTEM, "Hidden", track); else if (string_match(gui_playlist_cur(), "Favorites") || string_match(gui_playlist_cur(), "Hidden")) playlist_remove(PL_SYSTEM, gui_playlist_cur(), track); return false; } bool __gui_playlist_init_idle() { struct db_entry *artist, *next; db_for_each(artist, next, artist_db_get()) gui_playlist_add_artist(ARTIST(artist)); return true; } void gui_playlist_init() { GtkTreeSelection *selection; GtkTreeView *treeview; GtkTreeIter parent; GtkTreePath *path; p_store = GTK_TREE_STORE(gui_builder_object("o_playlist_store")); treeview = GTK_TREE_VIEW(gui_builder_widget("o_playlist_view")); selection = gtk_tree_view_get_selection(treeview); gtk_tree_store_insert(p_store, &parent, NULL, -1); __playlist_set(&parent, "Collection", "media-optical", PL_SYSTEM); __playlist_set_size(&parent, "Collection"); path = gtk_tree_model_get_path(GTK_TREE_MODEL(p_store), &parent); gtk_tree_selection_select_path(selection, path); gtk_tree_path_free(path); gtk_tree_store_insert(p_store, &parent, NULL, -1); __playlist_set(&parent, "History", "document-open-recent", PL_SYSTEM); __playlist_set_size(&parent, "History"); /* Add "Playlist" header. */ gtk_tree_store_insert(p_store, &parent, NULL, -1); gtk_tree_store_insert(p_store, &parent, NULL, -1); __playlist_set(&parent, "Playlists", "emblem-documents", 0); /* Add playlists. */ __playlist_add(&parent, "Favorites", "emblem-favorite", PL_SYSTEM); __playlist_add(&parent, "Hidden", "window-close", PL_SYSTEM); __playlist_add(&parent, "Most Played", "go-up", PL_SYSTEM); __playlist_add(&parent, "Least Played", "go-down", PL_SYSTEM); __playlist_add(&parent, "Unplayed", "audio-x-generic", PL_SYSTEM); /* Add "Library" header. */ gtk_tree_store_insert(p_store, &parent, NULL, -1); gtk_tree_store_insert(p_store, &parent, NULL, -1); __playlist_set(&parent, "Library", "system-file-manager", 0); gtk_tree_view_expand_all(treeview); 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 library *library) { GtkTreeIter parent; gchar *name; gtk_tree_model_get_iter_first(GTK_TREE_MODEL(p_store), &parent); while (gtk_tree_model_iter_next(GTK_TREE_MODEL(p_store), &parent)) { name = __playlist_name(&parent); if (string_match(name, "Library")) __playlist_add(&parent, library->li_path, "folder", PL_LIBRARY); g_free(name); } gtk_tree_view_expand_all( GTK_TREE_VIEW(gui_builder_widget("o_playlist_view"))); } void gui_playlist_add_artist(struct artist *artist) { GtkTreeIter parent, sibling, iter; gchar *name; bool match; gtk_tree_model_get_iter_first(GTK_TREE_MODEL(p_store), &parent); do { name = __playlist_name(&parent); match = string_match(name, "Collection"); g_free(name); if (match) break; } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(p_store), &parent)); if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(p_store), &sibling, &parent)) { __playlist_add(&parent, artist->ar_name, "system-users", PL_ARTIST); return; } do { name = __playlist_name(&sibling); match = string_compare(name, artist->ar_name) >= 0; g_free(name); if (match) { gtk_tree_store_insert_before(p_store, &iter, &parent, &sibling); __playlist_set(&iter, artist->ar_name, "system-users", PL_ARTIST); __playlist_set_size(&iter, artist->ar_name); return; } } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(p_store), &sibling)); __playlist_add(&parent, artist->ar_name, "system-users", PL_ARTIST); } struct queue_ops playlist_ops = { .qop_init = __playlist_init, .qop_deinit = gui_queue_free, .qop_added = __playlist_added, .qop_erase = __playlist_erase, .qop_removed = __playlist_removed, .qop_cleared = gui_queue_cleared, .qop_updated = gui_queue_updated, };