ocarina/gui/view.c

375 lines
9.7 KiB
C

/*
* Copyright 2016 (c) Anna Schumaker.
*/
#include <core/audio.h>
#include <core/playlist.h>
#include <core/tempq.h>
#include <gui/builder.h>
#include <gui/model.h>
#include <gui/queue.h>
#include <gui/settings.h>
#include <gui/view.h>
#include <stdlib.h>
static const gchar *QUEUE_SETTINGS[Q_MODEL_N_COLUMNS] = {
[Q_MODEL_TRACK_NR] = "gui.queue.track",
[Q_MODEL_TITLE] = "gui.queue.title",
[Q_MODEL_LENGTH] = "gui.queue.length",
[Q_MODEL_ARTIST] = "gui.queue.artist",
[Q_MODEL_ALBUM] = "gui.queue.album",
[Q_MODEL_YEAR] = "gui.queue.year",
[Q_MODEL_GENRE] = "gui.queue.genre",
[Q_MODEL_COUNT] = "gui.queue.count",
[Q_MODEL_LAST_PLAY] = "gui.queue.played",
[Q_MODEL_FILE_PATH] = "gui.queue.filepath",
[Q_MODEL_FONT] = "gui.queue.font",
};
static const enum compare_t QUEUE_SORT[Q_MODEL_N_COLUMNS] = {
[Q_MODEL_TRACK_NR] = COMPARE_TRACK,
[Q_MODEL_TITLE] = COMPARE_TITLE,
[Q_MODEL_LENGTH] = COMPARE_LENGTH,
[Q_MODEL_ARTIST] = COMPARE_ARTIST,
[Q_MODEL_ALBUM] = COMPARE_ALBUM,
[Q_MODEL_YEAR] = COMPARE_YEAR,
[Q_MODEL_GENRE] = COMPARE_GENRE,
[Q_MODEL_COUNT] = COMPARE_COUNT,
[Q_MODEL_LAST_PLAY] = COMPARE_PLAYED,
};
static GtkTreeView *view_treeview = NULL;
static GtkTreeModelFilter *view_filter = NULL;
static unsigned int view_sort_count = 0;
static bool view_no_scroll = false;
static inline GuiQueueModel *__view_filter_get_model()
{
if (view_filter == NULL)
return NULL;
return GUI_QUEUE_MODEL(gtk_tree_model_filter_get_model(view_filter));
}
static inline struct queue *__view_filter_get_queue()
{
if (view_filter == NULL)
return NULL;
return __view_filter_get_model()->gqm_queue;
}
static inline GtkTreePath *__view_filter_convert_path(GtkTreePath *orig)
{
return gtk_tree_model_filter_convert_path_to_child_path(view_filter, orig);
}
static struct track *__view_filter_get_track(GtkTreePath *orig)
{
GuiQueueModel *model = __view_filter_get_model();
GtkTreePath *real = __view_filter_convert_path(orig);
struct track *track = gui_queue_model_path_get_track(model, real);
gtk_tree_path_free(real);
return track;
}
static unsigned int __view_filter_get_index(GtkTreePath *orig)
{
GtkTreePath *real = __view_filter_convert_path(orig);
unsigned int ret = gtk_tree_path_get_indices(real)[0];
gtk_tree_path_free(real);
return ret;
}
static unsigned int __view_get_column_index(GtkTreeViewColumn *col)
{
unsigned int i;
for (i = 0; i < Q_MODEL_N_COLUMNS; i++) {
if (col == gtk_tree_view_get_column(view_treeview, i))
return i;
}
return Q_MODEL_N_COLUMNS;
}
static inline void __view_display_sorting(gchar *text)
{
gtk_label_set_text(GTK_LABEL(gui_builder_widget("o_sorting")), text);
}
static int __view_dec_sort(gpointer data)
{
if (view_sort_count > 0)
view_sort_count--;
if (view_sort_count == 0)
__view_display_sorting("");
return FALSE;
}
static void __view_set_column_sort_indicator(GtkTreeViewColumn *col,
unsigned int index)
{
struct queue *queue = __view_filter_get_queue();
GSList *cur = queue ? queue->q_sort : NULL;
unsigned int order = GTK_SORT_ASCENDING;
bool show = false;
int field;
while (cur) {
order = GTK_SORT_ASCENDING;
field = GPOINTER_TO_INT(cur->data);
if (abs(field) == QUEUE_SORT[index]) {
show = true;
if (field < 0)
order = GTK_SORT_DESCENDING;
break;
}
cur = g_slist_next(cur);
}
gtk_tree_view_column_set_sort_indicator(col, show);
gtk_tree_view_column_set_sort_order(col, order);
}
static void __view_set_sort_indicators()
{
GtkTreeViewColumn *col;
for (unsigned int i = 0; i < Q_MODEL_N_COLUMNS; i++) {
col = gtk_tree_view_get_column(view_treeview, i);
if (col)
__view_set_column_sort_indicator(col, i);
}
}
void __view_row_activated(GtkTreeView *treeview, GtkTreePath *path,
GtkTreeViewColumn *col, gpointer data)
{
view_no_scroll = true;
audio_load(__view_filter_get_track(path));
queue_selected(__view_filter_get_model()->gqm_queue,
gtk_tree_path_get_indices(path)[0]);
view_no_scroll = false;
}
void __view_column_resized(GtkTreeViewColumn *col, GParamSpec *pspec,
gpointer data)
{
unsigned int index = __view_get_column_index(col);
gui_settings_set(QUEUE_SETTINGS[index],
gtk_tree_view_column_get_width(col));
}
void __view_column_clicked(GtkTreeViewColumn *col, gpointer data)
{
struct queue *queue = __view_filter_get_queue();
unsigned int index = __view_get_column_index(col);
bool reset = view_sort_count == 0;
gchar *text;
if (!queue || queue_has_flag(queue, Q_NO_SORT))
return;
if (playlist_get_queue(PL_SYSTEM, gui_queue(queue)->gq_text))
playlist_sort(PL_SYSTEM, gui_queue(queue)->gq_text, QUEUE_SORT[index], reset);
else
queue_sort(queue, QUEUE_SORT[index], reset);
if (view_sort_count == 0) {
text = g_strdup_printf("Sorting within %s",
gtk_tree_view_column_get_title(col));
__view_display_sorting(text);
g_free(text);
}
__view_set_sort_indicators();
view_sort_count++;
g_timeout_add_seconds(3, __view_dec_sort, NULL);
}
static void __view_add_to_queue(GtkTreeModel *model, GtkTreePath *path,
GtkTreeIter *iter, gpointer data)
{
queue_add(data, __view_filter_get_track(path));
}
static void __view_add_to_playlist(GtkTreeModel *model, GtkTreePath *path,
GtkTreeIter *iter, gpointer data)
{
playlist_add(PL_SYSTEM, (gchar *)data, __view_filter_get_track(path));
}
static void __view_delete_selection(GtkTreeSelection *selection)
{
struct queue *queue = __view_filter_get_queue();
GList *rows = gtk_tree_selection_get_selected_rows(selection, NULL);
GList *cur = g_list_reverse(rows);
while (cur) {
queue_erase(queue, __view_filter_get_index(cur->data));
cur = g_list_next(cur);
}
g_list_free_full(rows, (GDestroyNotify) gtk_tree_path_free);
}
static void __view_process_selection(GtkTreeView *treeview, unsigned int keyval)
{
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
unsigned int flags = 0;
struct queue *queue;
switch (keyval) {
case GDK_KEY_KP_0 ... GDK_KEY_KP_9:
keyval = (keyval - GDK_KEY_KP_0) + GDK_KEY_0;
case GDK_KEY_0 ... GDK_KEY_9:
queue = tempq_get(keyval - GDK_KEY_0);
if (queue)
gtk_tree_selection_selected_foreach(selection,
__view_add_to_queue, queue);
break;
case GDK_KEY_f:
gtk_tree_selection_selected_foreach(selection,
__view_add_to_playlist,
"Favorites");
break;
case GDK_KEY_r:
flags = Q_RANDOM;
case GDK_KEY_q:
queue = tempq_alloc(flags);
gtk_tree_selection_selected_foreach(selection,
__view_add_to_queue, queue);
break;
case GDK_KEY_Delete:
__view_delete_selection(selection);
break;
}
}
void __view_keypress(GtkTreeView *treeview, GdkEventKey *event, gpointer data)
{
__view_process_selection(treeview, event->keyval);
}
void __view_rc_new_queue(GtkMenuItem *item, gpointer data)
{
__view_process_selection(view_treeview, GDK_KEY_q);
}
void __view_rc_add_to_queue(GtkMenuItem *item, gpointer data)
{
unsigned int i;
gchar *name;
for (i = 0; i < 10; i++) {
name = g_strdup_printf("o_queue_%d", i);
if (GTK_WIDGET(item) == gui_builder_widget(name))
__view_process_selection(view_treeview, GDK_KEY_0 + i);
g_free(name);
}
}
void __view_rc_add_favorites(GtkMenuItem *item, gpointer data)
{
__view_process_selection(view_treeview, GDK_KEY_f);
}
void __view_rc_add_hidden(GtkMenuItem *item, gpointer data)
{
GtkTreeSelection *selection = gtk_tree_view_get_selection(view_treeview);
GList *rows = gtk_tree_selection_get_selected_rows(selection, NULL);
GList *cur = g_list_reverse(rows);
while (cur) {
playlist_add(PL_SYSTEM, "Hidden", __view_filter_get_track(cur->data));
cur = g_list_next(cur);
}
g_list_free_full(rows, (GDestroyNotify) gtk_tree_path_free);
}
bool __view_button_press(GtkTreeView *treeview, GdkEventButton *event,
gpointer data)
{
GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
GtkMenu *menu = GTK_MENU(gui_builder_widget("o_menu"));
unsigned int i, size = tempq_count();
GtkTreePath *path;
gchar *name;
if (event->button != 3)
return false;
/* Select path if it isn't already selected */
if (gtk_tree_view_get_path_at_pos(treeview, event->x, event->y,
&path, NULL, NULL, NULL))
{
gtk_tree_selection_select_path(selection, path);
gtk_tree_path_free(path);
}
/* Determine which menu items can be shown */
gtk_widget_set_visible(gui_builder_widget("o_new_queue"), size < 10);
gtk_widget_set_visible(gui_builder_widget("o_add_to_queue"), size > 0);
for (i = 0; i < 10; i++) {
name = g_strdup_printf("o_queue_%d", i);
gtk_widget_set_visible(gui_builder_widget(name), i < size);
g_free(name);
}
gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
return true;
}
void gui_view_init()
{
GtkTreeViewColumn *col;
int i, pos;
view_treeview = GTK_TREE_VIEW(gui_builder_widget("o_treeview"));
for (i = 0; i < Q_MODEL_N_COLUMNS; i++) {
col = gtk_tree_view_get_column(view_treeview, i);
pos = gui_settings_get(QUEUE_SETTINGS[i]);
if (col && pos > 0)
gtk_tree_view_column_set_fixed_width(col, pos);
}
}
void gui_view_set_model(GtkTreeModelFilter *filter)
{
view_filter = filter;
gtk_tree_view_set_model(view_treeview, GTK_TREE_MODEL(filter));
view_sort_count = 0;
__view_display_sorting("");
__view_set_sort_indicators();
gui_view_scroll();
}
void gui_view_scroll()
{
struct queue *queue = __view_filter_get_queue();
GtkTreePath *real, *path;
if (!queue || (int)queue->q_cur.it_pos < 0 || view_no_scroll)
return;
real = gtk_tree_path_new_from_indices(queue->q_cur.it_pos, -1);
path = gtk_tree_model_filter_convert_child_path_to_path(view_filter, real);
if (!path)
goto out;
gtk_tree_view_set_cursor(view_treeview, path, NULL, false);
gtk_tree_view_scroll_to_cell(view_treeview, path, NULL, TRUE, 0.5, 0.5);
gtk_tree_path_free(path);
out:
gtk_tree_path_free(real);
}