/* * Copyright 2016 (c) Anna Schumaker. */ #include #include #include #include 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); }