ocarina/gui/model.c

384 lines
10 KiB
C

/*
* Copyright 2016 (c) Anna Schumaker.
*/
#include <core/audio.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 *gui_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,
};
const GtkTargetEntry gui_model_drag_targets[] = {
{ GUI_DRAG_DATA, GTK_TARGET_SAME_APP, 0 },
};
const unsigned int gui_model_n_targets = G_N_ELEMENTS(gui_model_drag_targets);
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;
unsigned int pos;
g_return_val_if_fail(iter != NULL, FALSE);
g_return_val_if_fail(iter->user_data, FALSE);
path = gtk_tree_path_new();
pos = playlist_iter_index(cur_playlist, iter->user_data);
gtk_tree_path_append_index(path, pos);
return path;
}
static void __gui_model_get_value(GtkTreeModel *model, GtkTreeIter *iter,
gint column, GValue *value)
{
struct track *track = playlist_iter_track(iter->user_data);
gchar *str;
g_return_if_fail(iter != NULL);
g_return_if_fail(iter->user_data != 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)
{
g_return_val_if_fail(iter != NULL, FALSE);
g_return_val_if_fail(iter->user_data, FALSE);
iter->user_data = playlist_iter_next(iter->user_data);
return iter->user_data != NULL;
}
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 playlist_size(cur_playlist);
}
static gboolean __gui_model_iter_nth_child(GtkTreeModel *model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n)
{
if (parent || !cur_playlist || n >= playlist_size(cur_playlist))
return FALSE;
iter->stamp = gui_model->gm_stamp;
iter->user_data = playlist_iter_get(cur_playlist, n);
return iter->user_data != NULL;
}
static gboolean __gui_model_iter_parent(GtkTreeModel *model, GtkTreeIter *iter,
GtkTreeIter *child)
{
return FALSE;
}
static gboolean __gui_model_drag_data_get(GtkTreeDragSource *drag_source,
GtkTreePath *path,
GtkSelectionData *selection_data)
{
struct gui_model_drag_data *data = g_malloc(sizeof(*data));
data->drag_row = gtk_tree_path_get_indices(path)[0];
data->drag_track = gui_model_path_get_track(path);
gtk_selection_data_set(selection_data, gdk_atom_intern(GUI_DRAG_DATA, false),
8 /* bytes */, (void *)data, sizeof(*data));
g_free(data);
return true;
}
static gboolean __gui_model_drag_data_delete(GtkTreeDragSource *drag_source,
GtkTreePath *path)
{
return true;
}
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 void __gui_drag_source_init(GtkTreeDragSourceIface *iface)
{
iface->drag_data_get = __gui_model_drag_data_get;
iface->drag_data_delete = __gui_model_drag_data_delete;
}
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,
};
static const GInterfaceInfo gui_drag_source = {
.interface_init = (GInterfaceInitFunc)__gui_drag_source_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);
g_type_add_interface_static(gui_model_type, GTK_TYPE_TREE_DRAG_SOURCE,
&gui_drag_source);
gui_model = g_object_new(gui_model_type, NULL);
g_assert(gui_model != NULL);
}
void gui_model_deinit(void)
{
g_object_unref(gui_model);
gui_model_type = 0;
gui_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_length);
gtk_label_set_text(gui_model_runtime(), len);
g_free(len);
}
static gboolean __gui_model_foreach_changed(GtkTreeModel *model, GtkTreePath *path,
GtkTreeIter *iter, gpointer data)
{
if (!data || data == gui_model_iter_get_track(iter))
gtk_tree_model_row_changed(model, path, iter);
return FALSE;
}
GuiModel *gui_model_get(void)
{
return gui_model;
}
GType gui_model_get_type()
{
return gui_model_type;
}
void gui_model_add(struct playlist *playlist, struct track *track)
{
GtkTreePath *path;
GtkTreeIter iter;
if (cur_playlist != playlist)
return;
path = gtk_tree_path_new_from_indices(0, -1);
__gui_model_get_iter(GTK_TREE_MODEL(gui_model), &iter, path);
gtk_tree_model_row_inserted(GTK_TREE_MODEL(gui_model), path, &iter);
gtk_tree_path_free(path);
__gui_model_set_runtime();
}
void gui_model_remove(struct playlist *playlist, struct track *track,
unsigned int n)
{
GtkTreePath *path;
unsigned int i;
if (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(gui_model), path);
gtk_tree_path_prev(path);
}
gtk_tree_path_free(path);
__gui_model_set_runtime();
}
void gui_model_update(struct playlist *playlist, struct track *track)
{
if (cur_playlist == playlist)
gtk_tree_model_foreach(GTK_TREE_MODEL(gui_model),
__gui_model_foreach_changed, track);
__gui_model_set_runtime();
}
void gui_model_set_playlist(struct playlist *playlist)
{
if (cur_playlist)
gui_model_remove(cur_playlist, NULL, playlist_size(cur_playlist));
cur_playlist = playlist;
__gui_model_set_runtime();
if (playlist && playlist_size(playlist) > 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(gui_model), &iter, path);
return gui_model_iter_get_track(&iter);
}