347 lines
9.1 KiB
C
347 lines
9.1 KiB
C
/*
|
|
* Copyright 2016 (c) Anna Schumaker.
|
|
*/
|
|
#include <core/idle.h>
|
|
#include <core/settings.h>
|
|
#include <core/string.h>
|
|
#include <gui/builder.h>
|
|
#include <gui/filter.h>
|
|
#include <gui/playlist.h>
|
|
#include <gui/queue.h>
|
|
#include <gui/sidebar.h>
|
|
|
|
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 = "<b>%s\n%d track%s</b>";
|
|
|
|
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,
|
|
};
|