355 lines
9.0 KiB
C
355 lines
9.0 KiB
C
/*
|
|
* Copyright 2016 (c) Anna Schumaker.
|
|
*/
|
|
#include <core/audio.h>
|
|
#include <core/playlist.h>
|
|
#include <core/string.h>
|
|
#include <gui/builder.h>
|
|
#include <gui/model.h>
|
|
|
|
static gboolean __gui_model_iter_nth_child(GtkTreeModel *, GtkTreeIter *,
|
|
GtkTreeIter *, gint);
|
|
|
|
static struct playlist *cur_playlist = NULL;
|
|
static GObjectClass *parent_class = NULL;
|
|
static GuiModel *queue_model = NULL;
|
|
static GType gui_model_type = 0;
|
|
|
|
static GType gui_model_columns[GUI_MODEL_N_COLUMNS] = {
|
|
[GUI_MODEL_TRACK_NR] = G_TYPE_UINT,
|
|
[GUI_MODEL_TITLE] = G_TYPE_STRING,
|
|
[GUI_MODEL_LENGTH] = G_TYPE_STRING,
|
|
[GUI_MODEL_ARTIST] = G_TYPE_STRING,
|
|
[GUI_MODEL_ALBUM] = G_TYPE_STRING,
|
|
[GUI_MODEL_YEAR] = G_TYPE_UINT,
|
|
[GUI_MODEL_GENRE] = G_TYPE_STRING,
|
|
[GUI_MODEL_COUNT] = G_TYPE_UINT,
|
|
[GUI_MODEL_LAST_PLAY] = G_TYPE_STRING,
|
|
[GUI_MODEL_FILE_PATH] = G_TYPE_STRING,
|
|
[GUI_MODEL_FONT] = G_TYPE_STRING,
|
|
};
|
|
|
|
static GtkTreeModelFlags __gui_model_get_flags(GtkTreeModel *model)
|
|
{
|
|
return GTK_TREE_MODEL_LIST_ONLY;
|
|
}
|
|
|
|
static gint __gui_model_get_n_columns(GtkTreeModel *model)
|
|
{
|
|
return GUI_MODEL_N_COLUMNS;
|
|
}
|
|
|
|
static GType __gui_model_get_column_type(GtkTreeModel *model, gint index)
|
|
{
|
|
g_return_val_if_fail(index >= 0, G_TYPE_INVALID);
|
|
g_return_val_if_fail(index < GUI_MODEL_N_COLUMNS, G_TYPE_INVALID);
|
|
return gui_model_columns[index];
|
|
}
|
|
|
|
static gboolean __gui_model_get_iter(GtkTreeModel *model, GtkTreeIter *iter,
|
|
GtkTreePath *path)
|
|
{
|
|
gint *indices, depth;
|
|
|
|
g_assert(path != NULL);
|
|
indices = gtk_tree_path_get_indices_with_depth(path, &depth);
|
|
g_assert(depth == 1);
|
|
|
|
return __gui_model_iter_nth_child(model, iter, NULL, indices[0]);
|
|
}
|
|
|
|
static GtkTreePath *__gui_model_get_path(GtkTreeModel *model, GtkTreeIter *iter)
|
|
{
|
|
GtkTreePath *path;
|
|
|
|
g_return_val_if_fail(iter != NULL, FALSE);
|
|
g_return_val_if_fail(iter->user_data2, FALSE);
|
|
|
|
path = gtk_tree_path_new();
|
|
gtk_tree_path_append_index(path, GPOINTER_TO_UINT(iter->user_data));
|
|
return path;
|
|
}
|
|
|
|
static void __gui_model_get_value(GtkTreeModel *model, GtkTreeIter *iter,
|
|
gint column, GValue *value)
|
|
{
|
|
struct track *track = iter->user_data2;
|
|
gchar *str;
|
|
|
|
g_return_if_fail(iter != NULL);
|
|
g_return_if_fail(iter->user_data2 != NULL);
|
|
g_return_if_fail(column < GUI_MODEL_N_COLUMNS);
|
|
|
|
g_value_init(value, gui_model_columns[column]);
|
|
|
|
switch (column) {
|
|
case GUI_MODEL_TRACK_NR:
|
|
g_value_set_uint(value, track->tr_track);
|
|
break;
|
|
case GUI_MODEL_TITLE:
|
|
g_value_set_static_string(value, track->tr_title);
|
|
break;
|
|
case GUI_MODEL_LENGTH:
|
|
g_value_take_string(value, string_sec2str(track->tr_length));
|
|
break;
|
|
case GUI_MODEL_ARTIST:
|
|
g_value_set_static_string(value, track->tr_album->al_artist->ar_name);
|
|
break;
|
|
case GUI_MODEL_ALBUM:
|
|
g_value_set_static_string(value, track->tr_album->al_name);
|
|
break;
|
|
case GUI_MODEL_YEAR:
|
|
g_value_set_uint(value, track->tr_album->al_year);
|
|
break;
|
|
case GUI_MODEL_GENRE:
|
|
g_value_set_static_string(value, track->tr_album->al_genre->ge_name);
|
|
break;
|
|
case GUI_MODEL_COUNT:
|
|
g_value_set_uint(value, track->tr_count);
|
|
break;
|
|
case GUI_MODEL_LAST_PLAY:
|
|
g_value_take_string(value, track_last_play(track));
|
|
break;
|
|
case GUI_MODEL_FILE_PATH:
|
|
str = track_path(track);
|
|
g_value_take_string(value, g_markup_escape_text(str, -1));
|
|
g_free(str);
|
|
break;
|
|
case GUI_MODEL_FONT:
|
|
str = (track == audio_cur_track()) ? "bold" : "";
|
|
g_value_take_string(value, g_strdup(str));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gboolean __gui_model_iter_next(GtkTreeModel *model, GtkTreeIter *iter)
|
|
{
|
|
unsigned int pos;
|
|
|
|
g_return_val_if_fail(iter != NULL, FALSE);
|
|
g_return_val_if_fail(iter->user_data2, FALSE);
|
|
|
|
pos = GPOINTER_TO_UINT(iter->user_data) + 1;
|
|
return __gui_model_iter_nth_child(model, iter, NULL, pos);
|
|
}
|
|
|
|
static gboolean __gui_model_iter_children(GtkTreeModel *model, GtkTreeIter *iter,
|
|
GtkTreeIter *parent)
|
|
{
|
|
return __gui_model_iter_nth_child(model, iter, parent, 0);
|
|
}
|
|
|
|
static gboolean __gui_model_iter_has_child(GtkTreeModel *model, GtkTreeIter *iter)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static gint __gui_model_iter_n_children(GtkTreeModel *model, GtkTreeIter *iter)
|
|
{
|
|
if (iter != NULL || !cur_playlist)
|
|
return 0;
|
|
return queue_size(&cur_playlist->pl_queue);
|
|
}
|
|
|
|
static gboolean __gui_model_iter_nth_child(GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
GtkTreeIter *parent,
|
|
gint n)
|
|
{
|
|
struct queue *queue = &cur_playlist->pl_queue;
|
|
|
|
if (parent || !cur_playlist || n >= queue_size(queue))
|
|
return FALSE;
|
|
|
|
iter->stamp = queue_model->gm_stamp;
|
|
iter->user_data = GUINT_TO_POINTER(n);
|
|
iter->user_data2 = queue_at(queue, n);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean __gui_model_iter_parent(GtkTreeModel *model, GtkTreeIter *iter,
|
|
GtkTreeIter *child)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static void __gui_model_init(GuiModel *model)
|
|
{
|
|
model->gm_stamp = g_random_int();
|
|
}
|
|
|
|
static void __gui_model_finalize(GObject *object)
|
|
{
|
|
parent_class->finalize(object);
|
|
}
|
|
|
|
static void __gui_model_class_init(GuiModelClass *class)
|
|
{
|
|
GObjectClass *object_class = (GObjectClass *)class;
|
|
|
|
object_class->finalize = __gui_model_finalize;
|
|
parent_class = g_type_class_peek_parent(class);
|
|
}
|
|
|
|
static void __gui_tree_model_init(GtkTreeModelIface *iface)
|
|
{
|
|
iface->get_flags = __gui_model_get_flags;
|
|
iface->get_n_columns = __gui_model_get_n_columns;
|
|
iface->get_column_type = __gui_model_get_column_type;
|
|
iface->get_iter = __gui_model_get_iter;
|
|
iface->get_path = __gui_model_get_path;
|
|
iface->get_value = __gui_model_get_value;
|
|
iface->iter_next = __gui_model_iter_next;
|
|
iface->iter_children = __gui_model_iter_children;
|
|
iface->iter_has_child = __gui_model_iter_has_child;
|
|
iface->iter_n_children = __gui_model_iter_n_children;
|
|
iface->iter_nth_child = __gui_model_iter_nth_child;
|
|
iface->iter_parent = __gui_model_iter_parent;
|
|
}
|
|
|
|
static const GTypeInfo gui_model_type_info = {
|
|
.class_size = sizeof(GuiModelClass),
|
|
.base_init = NULL,
|
|
.base_finalize = NULL,
|
|
.class_init = (GClassInitFunc)__gui_model_class_init,
|
|
.class_finalize = NULL,
|
|
.class_data = NULL,
|
|
.instance_size = sizeof(GuiModel),
|
|
.n_preallocs = 0,
|
|
.instance_init = (GInstanceInitFunc)__gui_model_init,
|
|
};
|
|
|
|
static const GInterfaceInfo gui_tree_model = {
|
|
.interface_init = (GInterfaceInitFunc)__gui_tree_model_init,
|
|
.interface_finalize = NULL,
|
|
.interface_data = NULL,
|
|
};
|
|
|
|
|
|
void gui_model_init(void)
|
|
{
|
|
gui_model_type = g_type_register_static(G_TYPE_OBJECT, "GuiModel",
|
|
&gui_model_type_info,
|
|
(GTypeFlags)0);
|
|
g_type_add_interface_static(gui_model_type, GTK_TYPE_TREE_MODEL,
|
|
&gui_tree_model);
|
|
|
|
queue_model = g_object_new(gui_model_type, NULL);
|
|
g_assert(queue_model != NULL);
|
|
}
|
|
|
|
void gui_model_deinit(void)
|
|
{
|
|
g_object_unref(queue_model);
|
|
gui_model_type = 0;
|
|
queue_model = NULL;
|
|
cur_playlist = NULL;
|
|
}
|
|
|
|
static void __gui_model_set_runtime(void)
|
|
{
|
|
gchar *len = NULL;
|
|
|
|
if (cur_playlist)
|
|
len = string_sec2str_long(cur_playlist->pl_queue.q_length);
|
|
|
|
gtk_label_set_text(gui_model_runtime(), len);
|
|
g_free(len);
|
|
}
|
|
|
|
GuiModel *gui_model_get(void)
|
|
{
|
|
return queue_model;
|
|
}
|
|
|
|
GType gui_model_get_type()
|
|
{
|
|
return gui_model_type;
|
|
}
|
|
|
|
void gui_model_add(struct playlist *playlist, unsigned int row)
|
|
{
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
|
|
if (cur_playlist != playlist)
|
|
return;
|
|
|
|
path = gtk_tree_path_new_from_indices(row, -1);
|
|
__gui_model_get_iter(GTK_TREE_MODEL(queue_model), &iter, path);
|
|
gtk_tree_model_row_inserted(GTK_TREE_MODEL(queue_model), path, &iter);
|
|
__gui_model_set_runtime();
|
|
gtk_tree_path_free(path);
|
|
}
|
|
|
|
void gui_model_remove(struct playlist *playlist, unsigned int row)
|
|
{
|
|
GtkTreePath *path;
|
|
|
|
if (cur_playlist != playlist)
|
|
return;
|
|
|
|
path = gtk_tree_path_new_from_indices(row, -1);
|
|
gtk_tree_model_row_deleted(GTK_TREE_MODEL(queue_model), path);
|
|
__gui_model_set_runtime();
|
|
gtk_tree_path_free(path);
|
|
}
|
|
|
|
void gui_model_clear(struct playlist *playlist, unsigned int n)
|
|
{
|
|
GtkTreePath *path;
|
|
unsigned int i;
|
|
|
|
if (!queue_model || cur_playlist != playlist)
|
|
return;
|
|
|
|
path = gtk_tree_path_new_from_indices(n - 1, -1);
|
|
for (i = 0; i < n; i++) {
|
|
gtk_tree_model_row_deleted(GTK_TREE_MODEL(queue_model), path);
|
|
gtk_tree_path_prev(path);
|
|
}
|
|
|
|
__gui_model_set_runtime();
|
|
gtk_tree_path_free(path);
|
|
}
|
|
|
|
void gui_model_update(struct playlist *playlist, unsigned int row)
|
|
{
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
|
|
if (cur_playlist != playlist)
|
|
return;
|
|
|
|
path = gtk_tree_path_new_from_indices(row, -1);
|
|
__gui_model_get_iter(GTK_TREE_MODEL(queue_model), &iter, path);
|
|
gtk_tree_model_row_changed(GTK_TREE_MODEL(queue_model), path, &iter);
|
|
__gui_model_set_runtime();
|
|
gtk_tree_path_free(path);
|
|
}
|
|
|
|
void gui_model_set_playlist(struct playlist *playlist)
|
|
{
|
|
if (cur_playlist)
|
|
gui_model_clear(cur_playlist, queue_size(&cur_playlist->pl_queue));
|
|
|
|
cur_playlist = playlist;
|
|
__gui_model_set_runtime();
|
|
|
|
if (playlist && queue_size(&playlist->pl_queue) > 0)
|
|
gui_model_add(playlist, 0);
|
|
}
|
|
|
|
struct playlist *gui_model_get_playlist(void)
|
|
{
|
|
return cur_playlist;
|
|
}
|
|
|
|
struct track * gui_model_path_get_track(GtkTreePath *path)
|
|
{
|
|
GtkTreeIter iter;
|
|
|
|
__gui_model_get_iter(GTK_TREE_MODEL(queue_model), &iter, path);
|
|
return gui_model_iter_get_track(&iter);
|
|
}
|