Anna Schumaker
3806577154
We only need to do this after Ocarina init has completed, otherwise artist playlists will be added twice. Fixes #79: Artist playlists not updated when tracks are added Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
339 lines
9.2 KiB
C
339 lines
9.2 KiB
C
/*
|
|
* Copyright 2016 (c) Anna Schumaker.
|
|
*/
|
|
#include <core/idle.h>
|
|
#include <core/string.h>
|
|
#include <gui/builder.h>
|
|
#include <gui/playlist.h>
|
|
#include <gui/sidebar.h>
|
|
|
|
enum playlist_sidebar_columns {
|
|
P_SB_IMAGE,
|
|
P_SB_NAME,
|
|
P_SB_TYPE,
|
|
};
|
|
|
|
static GtkTreeStore *p_store;
|
|
static gchar *p_name = NULL;
|
|
static bool p_init_done = false;
|
|
|
|
|
|
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 = "<b>%s</b>\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;
|
|
if (p_init_done && playlist->pl_type == PL_ARTIST)
|
|
gui_playlist_add_artist(artist_find(playlist->pl_name));
|
|
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));
|
|
|
|
p_init_done = true;
|
|
gui_queue_show(gui_queue(playlist_get_queue(PL_SYSTEM, "Collection")));
|
|
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, "<big>Playlists</big>", "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, "<big>Library</big>", "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,
|
|
};
|