/* * Copyright 2015 (c) Anna Schumaker. */ #include #include #include #include #include enum sidebar_columns { SB_IMAGE, SB_NAME, SB_TYPE, SB_EDITABLE, }; const gchar *SIDEBAR_SETTING = "gui.sidebar.pos"; static gchar *__gui_sidebar_size_str(struct playlist *playlist) { const gchar *fmt = "%s\n%d track%s"; unsigned int size; if (!playlist) return NULL; size = playlist_size(playlist); if (playlist_current() == playlist) fmt = "%s\n%d track%s"; return g_markup_printf_escaped(fmt, playlist->pl_name, size, (size != 1) ? "s" : ""); } static void __gui_sidebar_set(GtkTreeIter *iter, const gchar *name, const gchar *image, enum playlist_type_t type) { gtk_tree_store_set(gui_sidebar_store(), iter, SB_NAME, name, SB_IMAGE, image, SB_TYPE, type, SB_EDITABLE, false, -1); } static void __gui_sidebar_set_playlist(GtkTreeIter *iter, struct playlist *playlist, const gchar *image) { gchar *text = __gui_sidebar_size_str(playlist); __gui_sidebar_set(iter, text, image, playlist->pl_type); g_free(text); } static void __gui_sidebar_add_header(GtkTreeIter *iter, const gchar *name, const gchar *image) { gchar *formatted = g_strdup_printf("%s", name); gtk_tree_store_insert(gui_sidebar_store(), iter, NULL, -1); __gui_sidebar_set(iter, NULL, NULL, PL_MAX_TYPE); gtk_tree_store_insert(gui_sidebar_store(), iter, NULL, -1); __gui_sidebar_set(iter, formatted, image, PL_MAX_TYPE); g_free(formatted); } static int __gui_sidebar_compare(GtkTreeIter *iter, const gchar *name, enum playlist_type_t type) { gchar *cur; int ret; if (gui_sidebar_iter_type(iter) != type) return gui_sidebar_iter_type(iter) - type; cur = gui_sidebar_iter_name(iter); ret = g_utf8_collate(cur, name); g_free(cur); return ret; } static inline void __gui_sidebar_filter_iter_convert(GtkTreeIter *iter, GtkTreeIter *child) { gtk_tree_model_filter_convert_iter_to_child_iter(gui_sidebar_filter(), child, iter); } static gchar *__gui_sidebar_filter_iter_name(GtkTreeIter *iter) { GtkTreeIter child; __gui_sidebar_filter_iter_convert(iter, &child); return gui_sidebar_iter_name(&child); } static gboolean __gui_sidebar_visible_func(GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { enum playlist_type_t type = gui_sidebar_iter_type(iter); if (type == PL_SYSTEM || type == PL_ARTIST) return playlist_size(gui_sidebar_iter_playlist(iter)) > 0; return TRUE; } static gboolean __gui_sidebar_can_select(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean selected, gpointer data) { GtkTreeIter iter, child; gtk_tree_model_get_iter(model, &iter, path); __gui_sidebar_filter_iter_convert(&iter, &child); return gui_sidebar_iter_type(&child) != PL_MAX_TYPE; } void __gui_sidebar_selection_changed(GtkTreeSelection *selection, gpointer data) { bool active = false, sensitive = false; struct playlist *playlist = NULL; GtkTreeIter iter; if (gui_sidebar_iter_current(&iter)) { playlist = gui_sidebar_iter_playlist(&iter); active = playlist->pl_random; sensitive = (playlist->pl_ops->pl_set_random != NULL); } gui_treeview_set_playlist(playlist); gtk_toggle_button_set_active(gui_random_button(), active); gtk_widget_set_sensitive(GTK_WIDGET(gui_random_button()), sensitive); } static void __gui_sidebar_do_rename(GtkTreePath *path) { GtkTreeView *treeview = gui_sidebar_treeview(); GtkTreeModel *model = gtk_tree_view_get_model(treeview); GtkTreeViewColumn *column = gtk_tree_view_get_column(treeview, SB_NAME); GtkTreeIter iter, child; if (!gtk_tree_model_get_iter(model, &iter, path)) return; __gui_sidebar_filter_iter_convert(&iter, &child); gui_sidebar_iter_set_editable(&child, true); gtk_tree_view_set_cursor(treeview, path, column, true); } static GtkTreePath *__gui_sidebar_current_path(void) { GtkTreeView *treeview = gui_sidebar_treeview(); GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); GtkTreeModel *model = gtk_tree_view_get_model(treeview); GtkTreeIter iter; if (!gtk_tree_selection_get_selected(selection, &model, &iter)) return NULL; return gtk_tree_model_get_path(model, &iter); } bool __gui_sidebar_rename(GtkMenuItem *item, gpointer data) { GtkTreePath *path = __gui_sidebar_current_path(); if (path) { __gui_sidebar_do_rename(path); gtk_tree_path_free(path); } return path != NULL; } bool __gui_sidebar_select(GtkMenuItem *item, gpointer data) { GtkTreeView *treeview = gui_sidebar_treeview(); GtkTreePath *path = __gui_sidebar_current_path(); if (path) { gtk_tree_view_row_activated(treeview, path, NULL); gtk_tree_path_free(path); } return path != NULL; } bool __gui_sidebar_delete(GtkMenuItem *item, gpointer data) { GtkTreeIter iter; if (!gui_sidebar_iter_current(&iter)) return false; if (playlist_delete(gui_model_get_playlist())) gtk_tree_store_remove(gui_sidebar_store(), &iter); return true; } bool __gui_sidebar_keypress(GtkTreeView *treeview, GdkEventKey *event, gpointer data) { switch (event->keyval) { case GDK_KEY_BackSpace: return __gui_sidebar_rename(NULL, NULL); case GDK_KEY_Return: return __gui_sidebar_select(NULL, NULL); case GDK_KEY_Delete: return __gui_sidebar_delete(NULL, NULL); default: return false; } } bool __gui_sidebar_button_press(GtkTreeView *treeview, GdkEventButton *event, gpointer data) { enum playlist_type_t type = PL_MAX_TYPE; GtkTreePath *path; GtkTreeIter iter; bool ret = true; if (!gtk_tree_view_get_path_at_pos(treeview, event->x, event->y, &path, NULL, NULL, NULL)) return false; if (event->button == GDK_BUTTON_SECONDARY) { gtk_tree_view_set_cursor(treeview, path, NULL, false); if (gui_sidebar_iter_current(&iter)) type = gui_sidebar_iter_type(&iter); gtk_widget_set_visible(gui_builder_widget("rc_sidebar_rename"), type == PL_USER); gtk_menu_popup_at_pointer(gui_sidebar_menu(), (GdkEvent *)event); } else if (event->type == GDK_2BUTTON_PRESS && event->button == GDK_BUTTON_MIDDLE) { __gui_sidebar_do_rename(path); } else ret = false; gtk_tree_path_free(path); return ret; } void __gui_sidebar_resized(GtkPaned *pane, GParamSpec *pspec, gpointer data) { settings_set(SIDEBAR_SETTING, gtk_paned_get_position(pane)); } void __gui_sidebar_random_toggled(GtkToggleButton *button, gpointer data) { struct playlist *playlist = gui_model_get_playlist(); bool active = gtk_toggle_button_get_active(button); if (playlist) playlist_set_random(playlist, active); } void gui_sidebar_init() { int pos = settings_get(SIDEBAR_SETTING); GtkTreeSelection *selection; GtkTreeIter iter; gtk_tree_view_enable_model_drag_dest(gui_sidebar_treeview(), gui_model_drag_targets, gui_model_n_targets, GDK_ACTION_MOVE); if (!gui_sidebar_iter_first(&iter)) { __gui_sidebar_add_header(&iter, "Playlists", "emblem-documents"); __gui_sidebar_add_header(&iter, "Dynamic", "emblem-generic"); __gui_sidebar_add_header(&iter, "Library", "emblem-system"); selection = gtk_tree_view_get_selection(gui_sidebar_treeview()); gtk_tree_selection_set_select_function(selection, __gui_sidebar_can_select, NULL, NULL); gtk_tree_model_filter_set_visible_func(gui_sidebar_filter(), __gui_sidebar_visible_func, NULL, NULL); } if (pos > 0) gtk_paned_set_position(gui_sidebar(), pos); } gboolean gui_sidebar_iter_current(GtkTreeIter *iter) { GtkTreeView *treeview = gui_sidebar_treeview(); GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); GtkTreeModel *model = GTK_TREE_MODEL(gui_sidebar_filter()); GtkTreeIter it; if (!gtk_tree_selection_get_selected(selection, &model, &it)) return false; __gui_sidebar_filter_iter_convert(&it, iter); return true; } gboolean gui_sidebar_iter_first(GtkTreeIter *iter) { return gtk_tree_model_get_iter_first(gui_sidebar_model(), iter); } gboolean gui_sidebar_iter_next(GtkTreeIter *iter) { return gtk_tree_model_iter_next(gui_sidebar_model(), iter); } gboolean gui_sidebar_iter_down(GtkTreeIter *iter, GtkTreeIter *child) { return gtk_tree_model_iter_children(gui_sidebar_model(), child, iter); } gchar *gui_sidebar_iter_name(GtkTreeIter *iter) { gchar *text = NULL, *parsed = NULL, *name, **split; gtk_tree_model_get(gui_sidebar_model(), iter, SB_NAME, &text, -1); if (!text) return g_strdup(""); pango_parse_markup(text, -1, 0, NULL, &parsed, NULL, NULL); if (!parsed) return g_strdup(""); split = g_strsplit(parsed, "\n", 2); name = g_strdup(split[0]); g_strfreev(split); g_free(parsed); g_free(text); return name; } enum playlist_type_t gui_sidebar_iter_type(GtkTreeIter *iter) { enum playlist_type_t type; gtk_tree_model_get(gui_sidebar_model(), iter, SB_TYPE, &type, -1); return type; } bool gui_sidebar_iter_editable(GtkTreeIter *iter) { gboolean editable; gtk_tree_model_get(gui_sidebar_model(), iter, SB_EDITABLE, &editable, -1); return editable == TRUE; } struct playlist *gui_sidebar_iter_playlist(GtkTreeIter *iter) { enum playlist_type_t type = gui_sidebar_iter_type(iter); gchar *name = gui_sidebar_iter_name(iter); struct playlist *playlist = playlist_lookup(type, name); g_free(name); return playlist; } void gui_sidebar_iter_add(GtkTreeIter *iter, struct playlist *playlist, const gchar *image) { GtkTreeIter new; gtk_tree_store_insert_before(gui_sidebar_store(), &new, NULL, iter); __gui_sidebar_set_playlist(&new, playlist, image); } void gui_sidebar_iter_sort_child(GtkTreeIter *iter, struct playlist *playlist, const gchar *image) { GtkTreeIter child, new; if (!gui_sidebar_iter_down(iter, &child)) goto out_append; do { if (__gui_sidebar_compare(&child, playlist->pl_name, playlist->pl_type) >= 0) { gtk_tree_store_insert_before(gui_sidebar_store(), &new, iter, &child); __gui_sidebar_set_playlist(&new, playlist, image); return; } } while (gui_sidebar_iter_next(&child)); out_append: gui_sidebar_iter_append_child(iter, playlist, image); } void gui_sidebar_iter_append_child(GtkTreeIter *iter, struct playlist *playlist, const gchar *image) { GtkTreeIter new; gtk_tree_store_insert_before(gui_sidebar_store(), &new, iter, NULL); __gui_sidebar_set_playlist(&new, playlist, image); } void gui_sidebar_iter_update_playlist(GtkTreeIter *iter, struct playlist *playlist) { gchar *text; if (!playlist) return; text = __gui_sidebar_size_str(playlist); gtk_tree_store_set(gui_sidebar_store(), iter, SB_NAME, text, -1); g_free(text); } void gui_sidebar_iter_update(GtkTreeIter *iter) { gui_sidebar_iter_update_playlist(iter, gui_sidebar_iter_playlist(iter)); } void gui_sidebar_iter_select(GtkTreeIter *iter) { GtkTreeSelection *selection; GtkTreeIter filter; gtk_tree_model_filter_convert_child_iter_to_iter(gui_sidebar_filter(), &filter, iter); selection = gtk_tree_view_get_selection(gui_sidebar_treeview()); gtk_tree_selection_select_iter(selection, &filter); } bool gui_sidebar_iter_set_editable(GtkTreeIter *iter, bool editable) { enum playlist_type_t type = gui_sidebar_iter_type(iter); if (type != PL_USER) return false; gtk_tree_store_set(gui_sidebar_store(), iter, SB_EDITABLE, editable, -1); return true; } void gui_sidebar_filter_path_select(GtkTreePath *path) { GtkTreeModel *model = GTK_TREE_MODEL(gui_sidebar_filter()); GtkTreeIter iter, child; gtk_tree_model_get_iter(model, &iter, path); __gui_sidebar_filter_iter_convert(&iter, &child); if (playlist_select(gui_sidebar_iter_playlist(&child))) gui_sidebar_iter_update(&child); } void gui_sidebar_filter_set_expand(GtkTreeIter *iter) { gchar *name = __gui_sidebar_filter_iter_name(iter); gchar *setting = g_strdup_printf("gui.sidebar.expand.%s", name); GtkTreePath *path; if (settings_get(setting) == true) { path = gtk_tree_model_get_path( GTK_TREE_MODEL(gui_sidebar_filter()), iter); gtk_tree_view_expand_row(gui_sidebar_treeview(), path, false); gtk_tree_path_free(path); } g_free(setting); g_free(name); } void gui_sidebar_filter_row_expanded(GtkTreeIter *iter, bool expanded) { gchar *name = __gui_sidebar_filter_iter_name(iter); gchar *setting = g_strdup_printf("gui.sidebar.expand.%s", name); settings_set(setting, expanded); g_free(setting); g_free(name); } gboolean gui_sidebar_iter_find(GtkTreeIter *iter, const gchar *name, enum playlist_type_t type) { do { if (__gui_sidebar_compare(iter, name, type) == 0) return TRUE; } while (gui_sidebar_iter_next(iter)); return FALSE; } gboolean gui_sidebar_iter_from_string(const gchar *path, GtkTreeIter *child) { GtkTreeModel *model = GTK_TREE_MODEL(gui_sidebar_filter()); GtkTreeIter iter; if (!gtk_tree_model_get_iter_from_string(model, &iter, path)) return FALSE; __gui_sidebar_filter_iter_convert(&iter, child); return TRUE; } gboolean gui_sidebar_iter_from_xy(gint x, gint y, GtkTreeIter *child) { GtkTreeModel *model = GTK_TREE_MODEL(gui_sidebar_filter()); GtkTreePath *path; GtkTreeIter iter; if (!gtk_tree_view_get_path_at_pos(gui_sidebar_treeview(), x, y, &path, NULL, NULL, NULL)) return false; gtk_tree_model_get_iter(model, &iter, path); __gui_sidebar_filter_iter_convert(&iter, child); gtk_tree_path_free(path); return true; }