void sort_view(view_t *v) { dir_entry_t *unsorted_list; if(v->sort[0] > SK_LAST) { /* Completely skip sorting if primary key isn't set. */ return; } view = v; view_sort = v->sort; view_sort_groups = v->sort_groups; custom_view = flist_custom_active(v); if(!custom_view || !cv_tree(v->custom.type)) { /* Tree sorting works fine for flat list, but requires a bit more * resources, so skip it. */ sort_sequence(&v->dir_entry[0], v->list_rows); return; } /* When local filter isn't empty, parent directories disappear and sorting * stops being aware of tree structure to some degree. Perform one more round * of stable sorting of origins to group child nodes. */ if(!filter_is_empty(&v->local_filter.filter)) { flist_custom_uncompress_tree(v); } unsorted_list = v->dir_entry; v->dir_entry = dynarray_extend(NULL, v->list_rows*sizeof(*v->dir_entry)); if(v->dir_entry != NULL) { sort_tree_slice(&v->dir_entry[0], unsorted_list, v->list_rows, 1); } else { /* Just do nothing on memory error. */ v->dir_entry = unsorted_list; unsorted_list = NULL; } if(filter_is_empty(&v->local_filter.filter)) { dynarray_free(unsorted_list); } else { filters_drop_temporaries(v, unsorted_list); } }
/* Checks whether list of files is incomplete. Returns non-zero if so, * otherwise zero is returned. */ static int list_is_incomplete(view_t *view) { if(view->filtered > 0 && !filter_is_empty(&view->local_filter.filter)) { return 1; } if(flist_custom_active(view) && view->custom.type != CV_TREE) { return 0; } /* Check if there are any directories without leaf nodes. They aren't counted * as filtered out, so need to check separately (or start counting them in * some way). */ int i; for(i = 0; i < view->list_rows; ++i) { dir_entry_t *const entry = &view->dir_entry[i]; if(entry->type == FT_DIR && entry->child_count == 0 && !is_parent_dir(entry->name)) { return 1; } } return 0; }
static void test_set_to_empty_is_like_clear(void) { filter_t filter; assert_int_equal(0, filter_init(&filter, 1)); assert_int_equal(0, filter_set(&filter, "")); assert_true(filter_is_empty(&filter)); filter_dispose(&filter); }
void local_filter_update_view(view_t *view, int rel_pos) { int pos = extract_previously_selected_pos(view); if(pos < 0) { pos = find_nearest_neighour(view); } if(pos >= 0) { if(pos == 0 && is_parent_dir(view->dir_entry[0].name) && view->list_rows > 1 && !filter_is_empty(&view->local_filter.filter)) { ++pos; } view->list_pos = pos; view->top_line = pos - rel_pos; } }
/* Copies/moves elements of the unfiltered list into dir_entry list. add * parameter controls whether entries matching filter are copied into dir_entry * list. clear parameter controls whether entries not matching filter are * cleared in unfiltered list. Returns zero unless addition is performed in * which case can return non-zero when all files got filtered out. */ static int update_filtering_lists(view_t *view, int add, int clear) { /* filters_drop_temporaries() is a similar function. */ size_t i; size_t list_size = 0U; dir_entry_t *parent_entry = NULL; int parent_added = 0; for(i = 0; i < view->local_filter.unfiltered_count; ++i) { /* FIXME: some very long file names won't be matched against some * regexps. */ char name_with_slash[NAME_MAX + 1 + 1]; dir_entry_t *const entry = &view->local_filter.unfiltered[i]; const char *name = entry->name; if(is_parent_dir(name)) { if(entry->child_pos == 0) { parent_entry = entry; if(add && cfg_parent_dir_is_visible(is_root_dir(view->curr_dir))) { (void)add_dir_entry(&view->dir_entry, &list_size, entry); parent_added = 1; } continue; } else if(!filter_is_empty(&view->local_filter.filter)) { if(clear) { fentry_free(view, entry); } continue; } } if(fentry_is_dir(entry)) { append_slash(name, name_with_slash, sizeof(name_with_slash)); name = name_with_slash; } /* tag links to position of nodes passed through filter in list of visible * files. Nodes that didn't pass have -1. */ entry->tag = -1; if(filter_matches(&view->local_filter.filter, name) != 0) { if(add) { dir_entry_t *e = add_dir_entry(&view->dir_entry, &list_size, entry); if(e != NULL) { entry->tag = list_size - 1U; /* We basically grow the tree node by node while performing * reparenting. */ reparent_tree_node(entry, e); } } } else { if(clear) { fentry_free(view, entry); } } } if(clear) { /* XXX: the check of name pointer is horrible, but is needed to prevent * freeing of entry in use. */ if(!parent_added && parent_entry != NULL && list_size != 0U && view->dir_entry[0].name != parent_entry->name) { fentry_free(view, parent_entry); } } if(add) { view->list_rows = list_size; view->filtered = view->local_filter.prefiltered_count + view->local_filter.unfiltered_count - list_size; ensure_filtered_list_not_empty(view, parent_entry); return list_size == 0U || (list_size == 1U && parent_added && (filter_matches(&view->local_filter.filter, "../") == 0)); } return 0; }
int name_filters_empty(view_t *view) { return matcher_is_empty(view->manual_filter) && filter_is_empty(&view->auto_filter); }
void name_filters_add_selection(view_t *view) { dir_entry_t *entry; filter_t filter; int filtered; (void)filter_init(&filter, FILTER_DEF_CASE_SENSITIVITY); /* Traverse items and update/create filter values. */ entry = NULL; while(iter_selection_or_current(view, &entry)) { const char *name = entry->name; char name_with_slash[NAME_MAX + 1 + 1]; if(fentry_is_dir(entry)) { append_slash(entry->name, name_with_slash, sizeof(name_with_slash)); name = name_with_slash; } (void)filter_append(&view->auto_filter, name); (void)filter_append(&filter, name); } /* Even current file might be unavailable for filtering. In this case, just * do nothing. */ if(filter_is_empty(&filter)) { filter_dispose(&filter); return; } if(view->custom.type == CV_DIFF) { (void)filter_in_compare(view, &filter, &is_newly_filtered); ui_view_schedule_redraw(view); filter_dispose(&filter); return; } /* Update entry lists to remove entries that must be filtered out now. No * view reload is needed. */ filtered = zap_entries(view, view->dir_entry, &view->list_rows, &is_newly_filtered, &filter, 0, 1); if(flist_custom_active(view)) { (void)zap_entries(view, view->local_filter.entries, &view->local_filter.entry_count, &is_newly_filtered, &filter, 1, 1); } else { view->filtered += filtered; } filter_dispose(&filter); fpos_ensure_valid_pos(view); ui_view_schedule_redraw(view); }