From ba734b423b0b0b1c6a4a0a58af607a7c76168234 Mon Sep 17 00:00:00 2001 From: Omari Stephens Date: Sun, 9 Jul 2017 01:40:49 +0000 Subject: [PATCH] Uses custom hash/equals functions so that we can actually fetch equivalent FileDatas from the hash table. --- src/filecluster.c | 61 ++++++++++++++++++++++++++++++---- src/filecluster.h | 5 ++- src/typedefs.h | 8 ++--- src/ui_tree_edit.c | 11 +++--- src/view_file/view_file_icon.c | 39 ++++++++++++++++++++-- src/view_file/view_file_list.c | 2 +- 6 files changed, 106 insertions(+), 20 deletions(-) diff --git a/src/filecluster.c b/src/filecluster.c index a8cb22dd..2b7ebccb 100644 --- a/src/filecluster.c +++ b/src/filecluster.c @@ -42,37 +42,60 @@ static gboolean check_list_contains_sublist(GList *haystack, GList *needle) return TRUE; } +static gboolean filecluster_fd_equal(gconstpointer ptr_a, gconstpointer ptr_b) +{ + // TODO(xsdg): Is there anything we can/should do to validate inputs? + FileData *fd_a = (FileData *)ptr_a; + FileData *fd_b = (FileData *)ptr_b; + return !filelist_sort_compare_filedata(fd_a, fd_b); +} + +// TODO(xsdg): Move this into filedata.h +static guint filecluster_fd_hash(gconstpointer ptr) +{ + if (!ptr) return 1; + FileData *fd = (FileData *)ptr; + return 7 * g_str_hash(fd->original_path); +} + FileClusterList *fileclusterlist_new() { FileClusterList *fcl = g_new0(FileClusterList, 1); - fcl->clusters = g_hash_table_new(&g_direct_hash, &g_direct_equal); + fcl->clusters = g_hash_table_new(&filecluster_fd_hash, &filecluster_fd_equal); } FileCluster *filecluster_new() { FileCluster *fc = g_new0(FileCluster, 1); + fc->show_children = FALSE; } void fileclusterlist_free(FileClusterList *fcl) { - if (fcl->fd_list) g_list_free_full(fcl->fd_list, (GDestroyNotify)&filecluster_free); + // TODO(xsdg): don't leak stuff + // if (fcl->fd_list) g_list_free_full(fcl->fd_list, (GDestroyNotify)&filecluster_free); g_hash_table_destroy(fcl->clusters); g_free(fcl); } void filecluster_free(FileCluster *fc) { + filelist_free(fc->items); g_free(fc); } +gboolean filecluster_toggle_show_children(FileCluster *fc) +{ + fc->show_children = !fc->show_children; + return fc->show_children; +} + FileCluster *fileclusterlist_create_cluster(FileClusterList *fcl, GList *fd_items) { GList *work; // Check preconditions. if (!fd_items) return NULL; - // TODO(xsdg): Is this actually necessary? - // if (!check_list_contains_sublist(fcl->fd_list, fd_items)) return NULL; for (work = fd_items; work; work = work->next) { FileData *fd = work->data; @@ -85,7 +108,7 @@ FileCluster *fileclusterlist_create_cluster(FileClusterList *fcl, GList *fd_item } FileCluster *new_fc = filecluster_new(); - new_fc->items = g_list_copy(fd_items); + new_fc->items = filelist_copy(fd_items); new_fc->head = new_fc->items; for (GList *item = new_fc->items; item; item = item->next) @@ -111,7 +134,31 @@ gboolean fileclusterlist_has_child(FileClusterList *fcl, FileData *fd) return fc->head->data != fd; } -GList *filecluster_remove_children_from_list(FileClusterList *fcl, GList *list) +static gboolean fileclusterlist_should_hide(FileClusterList *fcl, FileData *fd) +{ + FileCluster *fc = g_hash_table_lookup(fcl->clusters, fd); + if (!fc) return FALSE; + if (fc->show_children) return FALSE; // TODO(xsdg): new function "should_show" + return fc->head->data != fd; +} + +// TODO(xsdg): pick a better name for this function +GList *fileclusterlist_next_non_child(FileClusterList *fcl, GList *list) +{ + // Check for no-ops + if (!list || !g_hash_table_size(fcl->clusters)) return list; + + // Clusters are being used, so we have to actually check things. + for (; list; list = list->next) + { + FileData *fd = list->data; + if (!fileclusterlist_has_child(fcl, fd)) return list; + } + + return NULL; +} + +GList *fileclusterlist_remove_children_from_list(FileClusterList *fcl, GList *list) { GList *work = list; @@ -122,7 +169,7 @@ GList *filecluster_remove_children_from_list(FileClusterList *fcl, GList *list) // Advance early in case link needs to be removed/freed. work = work->next; - if (fileclusterlist_has_child(fcl, fd)) + if (fileclusterlist_should_hide(fcl, fd)) { list = g_list_remove_link(list, link); file_data_unref(fd); diff --git a/src/filecluster.h b/src/filecluster.h index 3b2956d5..660f5ebc 100644 --- a/src/filecluster.h +++ b/src/filecluster.h @@ -26,6 +26,8 @@ FileCluster *filecluster_new(); // internal? void fileclusterlist_free(FileClusterList *fcl); void filecluster_free(FileCluster *fc); +gboolean filecluster_toggle_show_children(FileCluster *fc); + // Creates a cluster with items that must already be in the cluster list. Will fail (and make no // changes) if any of the specified items isn't in the list, or if any of the items is already // part of a different cluster. @@ -34,6 +36,7 @@ FileCluster *fileclusterlist_create_cluster(FileClusterList *fcl, GList *fd_item gboolean fileclusterlist_has_head(FileClusterList *fcl, FileData *fd); gboolean fileclusterlist_has_child(FileClusterList *fcl, FileData *fd); -GList *filecluster_remove_children_from_list(FileClusterList *fcl, GList *list); +GList *fileclusterlist_next_non_child(FileClusterList *fcl, GList *list); +GList *fileclusterlist_remove_children_from_list(FileClusterList *fcl, GList *list); #endif // FILECLUSTER_H diff --git a/src/typedefs.h b/src/typedefs.h index cbaf56da..fcf2d69b 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -256,7 +256,9 @@ typedef enum { SELECTION_NONE = 0, SELECTION_SELECTED = 1 << 0, SELECTION_PRELIGHT = 1 << 1, - SELECTION_FOCUS = 1 << 2 + SELECTION_FOCUS = 1 << 2, + SELECTION_CLUSTER_HEAD = 1 << 3, + SELECTION_CLUSTER_CHILD = 1 << 4 } SelectionType; typedef struct _ImageLoader ImageLoader; @@ -537,13 +539,11 @@ struct _FileCluster { GList *head; GList *items; + gboolean show_children; }; struct _FileClusterList { - // All of the elements in the list, regardless of whether they're part of a cluster or not. - GList *fd_list; - // A map from any clustered FileData to the FileCluster object that describes the cluster. GHashTable *clusters; }; diff --git a/src/ui_tree_edit.c b/src/ui_tree_edit.c index 0fe4b238..464a0ad4 100644 --- a/src/ui_tree_edit.c +++ b/src/ui_tree_edit.c @@ -496,6 +496,7 @@ gint tree_path_to_row(GtkTreePath *tpath) void shift_color(GdkColor *src, gshort val, gint direction) { gshort cs; + static gshort COLOR_MAX = 0xffff; if (val == -1) { @@ -505,11 +506,11 @@ void shift_color(GdkColor *src, gshort val, gint direction) { val = CLAMP(val, 1, 100); } - cs = 0xffff / 100 * val; + cs = COLOR_MAX / 100 * val; /* up or down ? */ if (direction < 0 || - (direction == 0 &&((gint)src->red + (gint)src->green + (gint)src->blue) / 3 > 0xffff / 2)) + (direction == 0 &&((gint)src->red + (gint)src->green + (gint)src->blue) / 3 > COLOR_MAX / 2)) { src->red = MAX(0 , src->red - cs); src->green = MAX(0 , src->green - cs); @@ -517,9 +518,9 @@ void shift_color(GdkColor *src, gshort val, gint direction) } else { - src->red = MIN(0xffff, src->red + cs); - src->green = MIN(0xffff, src->green + cs); - src->blue = MIN(0xffff, src->blue + cs); + src->red = MIN(COLOR_MAX, src->red + cs); + src->green = MIN(COLOR_MAX, src->green + cs); + src->blue = MIN(COLOR_MAX, src->blue + cs); } } diff --git a/src/view_file/view_file_icon.c b/src/view_file/view_file_icon.c index e3ab038e..c2a423b9 100644 --- a/src/view_file/view_file_icon.c +++ b/src/view_file/view_file_icon.c @@ -1261,9 +1261,16 @@ gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer dat // Make a cluster out of the entire selection if (VFICON(vf)->selection && VFICON(vf)->selection->next) { + FileCluster *fc; // At least two items selected; go for it. g_warning("Had requisite number of selection items; going for it!"); - fileclusterlist_create_cluster(vf->cluster_list, VFICON(vf)->selection); + fc = fileclusterlist_create_cluster(vf->cluster_list, VFICON(vf)->selection); + if (fc) + { + // TODO(xsdg): set CLUSTER_CHILD + vficon_selection_add(vf, VFICON(vf)->selection->data, SELECTION_CLUSTER_HEAD, NULL); + vf_refresh(vf); + } } else { @@ -1278,6 +1285,19 @@ gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer dat } } break; + case GDK_KEY_F2: + g_warning("Flipping show_children!"); + fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL); + if (fd) + { + FileCluster *fc = g_hash_table_lookup(vf->cluster_list->clusters, fd); + if (fc) + { + filecluster_toggle_show_children(fc); + vf_refresh(vf); + } + } + break; default: stop_signal = FALSE; break; @@ -1824,7 +1844,7 @@ static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position) { ret = filelist_read(vf->dir_fd, &new_filelist, NULL); new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf)); - new_filelist = filecluster_remove_children_from_list(vf->cluster_list, new_filelist); + new_filelist = fileclusterlist_remove_children_from_list(vf->cluster_list, new_filelist); } /* the list might not be sorted if there were renames */ @@ -2002,6 +2022,21 @@ static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer memcpy(&color_fg, &style->text[state], sizeof(color_fg)); memcpy(&color_bg, &style->base[state], sizeof(color_bg)); + if (fd->selected & SELECTION_CLUSTER_HEAD) + { + // TODO(xsdg): Cluster coloration should be part of the style. + color_bg.blue = 0x4000; + color_bg.green = 0x4000; + color_bg.red = 0xFFFF; + } + else if (fd->selected & SELECTION_CLUSTER_CHILD) + { + // TODO(xsdg): Cluster coloration should be part of the style. + color_bg.blue = 0x8000; + color_bg.green = 0x8000; + color_bg.red = 0xFFFF; + } + if (fd->selected & SELECTION_PRELIGHT) { shift_color(&color_bg, -1, 0); diff --git a/src/view_file/view_file_list.c b/src/view_file/view_file_list.c index 15a71c55..86cb6123 100644 --- a/src/view_file/view_file_list.c +++ b/src/view_file/view_file_list.c @@ -1669,7 +1669,7 @@ gboolean vflist_refresh(ViewFile *vf) } vf->list = file_data_filter_marks_list(vf->list, vf_marks_get_filter(vf)); - vf->list = filecluster_remove_children_from_list(vf->cluster_list, vf->list); + vf->list = fileclusterlist_remove_children_from_list(vf->cluster_list, vf->list); file_data_register_notify_func(vf_notify_cb, vf, NOTIFY_PRIORITY_MEDIUM); DEBUG_1("%s vflist_refresh: sort", get_exec_time()); -- 2.20.1