/* * Copyright 2016 (c) Anna Schumaker. */ #include #include #include #include #include #include #include #include #include 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 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) { GtkTreePath *real = __view_filter_convert_path(orig); struct track *track = gui_queue_model_path_get_track(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 = gui_queue_model_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(gui_queue_model_get_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); settings_set(QUEUE_SETTINGS[index], gtk_tree_view_column_get_width(col)); } void __view_column_clicked(GtkTreeViewColumn *col, gpointer data) { struct queue *queue = gui_queue_model_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); } struct view_add_data { enum playlist_type_t vad_type; gchar *vad_name; }; static void __view_add_to_playlist(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { struct view_add_data *vad = (struct view_add_data *)data; playlist_add(vad->vad_type, vad->vad_name, __view_filter_get_track(path)); } static void __view_delete_selection(GtkTreeSelection *selection) { struct queue *queue = gui_queue_model_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 gchar *__view_get_new_playlist_name(void) { gchar *text = NULL; GtkWidget *entry = gtk_entry_new(); GtkWidget *dialog = gtk_dialog_new_with_buttons("New Playlist Name?", GTK_WINDOW(gui_builder_widget("o_window")), GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_OK"), GTK_RESPONSE_ACCEPT, NULL); GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); gtk_entry_set_activates_default(GTK_ENTRY(entry), true); gtk_container_add(GTK_CONTAINER(content), entry); gtk_widget_show_all(dialog); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) text = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); gtk_widget_destroy(dialog); return text; } static void __view_process_selection(GtkTreeView *treeview, unsigned int keyval) { GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); struct view_add_data vad_data; gchar *text = NULL; switch (keyval) { case GDK_KEY_f: vad_data.vad_type = PL_SYSTEM; vad_data.vad_name = "Favorites"; gtk_tree_selection_selected_foreach(selection, __view_add_to_playlist, &vad_data); break; case GDK_KEY_p: text = __view_get_new_playlist_name(); if (!text || !playlist_new(PL_USER, text)) break; vad_data.vad_type = PL_USER; vad_data.vad_name = text; gtk_tree_selection_selected_foreach(selection, __view_add_to_playlist, &vad_data); g_free(text); break; case GDK_KEY_q: vad_data.vad_type = PL_SYSTEM; vad_data.vad_name = "Queued Tracks"; gtk_tree_selection_selected_foreach(selection, __view_add_to_playlist, &vad_data); 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); } void __view_rc_add_new(GtkMenuItem *item, gpointer data) { __view_process_selection(view_treeview, GDK_KEY_p); } 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")); GtkTreePath *path; 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_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")); view_filter = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new( GTK_TREE_MODEL(gui_queue_model_get()), NULL)); for (i = 0; i < Q_MODEL_N_COLUMNS; i++) { col = gtk_tree_view_get_column(view_treeview, i); pos = settings_get(QUEUE_SETTINGS[i]); if (col && pos > 0) gtk_tree_view_column_set_fixed_width(col, pos); } gtk_tree_view_set_model(view_treeview, GTK_TREE_MODEL(view_filter)); } GtkTreeModelFilter *gui_view_get_filter(void) { return view_filter; } void gui_view_set_queue(struct queue *queue) { gui_queue_model_set_queue(queue); view_sort_count = 0; __view_display_sorting(""); __view_set_sort_indicators(); gui_view_scroll(); } void gui_view_scroll() { struct queue *queue = gui_queue_model_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); }