diff options
Diffstat (limited to 'source/blender/editors/space_file/file_ops.c')
-rw-r--r-- | source/blender/editors/space_file/file_ops.c | 949 |
1 files changed, 779 insertions, 170 deletions
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 27d6fabba4e..8347ad87258 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -31,9 +31,11 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_fileops_types.h" +#include "BLI_linklist.h" #include "BLO_readfile.h" +#include "BKE_appdir.h" #include "BKE_context.h" #include "BKE_screen.h" #include "BKE_global.h" @@ -97,9 +99,9 @@ static void file_deselect_all(SpaceFile *sfile, unsigned int flag) { FileSelection sel; sel.first = 0; - sel.last = filelist_numfiles(sfile->files) - 1; + sel.last = filelist_files_ensure(sfile->files) - 1; - filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, flag, CHECK_ALL); + filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_REMOVE, flag, CHECK_ALL); } typedef enum FileSelect { @@ -137,7 +139,7 @@ static FileSelection file_selection_get(bContext *C, const rcti *rect, bool fill { ARegion *ar = CTX_wm_region(C); SpaceFile *sfile = CTX_wm_space_file(C); - int numfiles = filelist_numfiles(sfile->files); + int numfiles = filelist_files_ensure(sfile->files); FileSelection sel; sel = find_file_mouse_rect(sfile, ar, rect); @@ -150,7 +152,7 @@ static FileSelection file_selection_get(bContext *C, const rcti *rect, bool fill if (fill && (sel.last >= 0) && (sel.last < numfiles) ) { int f = sel.last; while (f >= 0) { - if (filelist_is_selected(sfile->files, f, CHECK_ALL) ) + if (filelist_entry_select_index_get(sfile->files, f, CHECK_ALL) ) break; f--; } @@ -166,50 +168,118 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen) FileSelect retval = FILE_SELECT_NOTHING; SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_get_params(sfile); - int numfiles = filelist_numfiles(sfile->files); - struct direntry *file; + int numfiles = filelist_files_ensure(sfile->files); + const FileDirEntry *file; /* make the selected file active */ if ((selected_idx >= 0) && (selected_idx < numfiles) && (file = filelist_file(sfile->files, selected_idx))) { + params->highlight_file = selected_idx; params->active_file = selected_idx; - if (S_ISDIR(file->type)) { + if (file->typeflag & FILE_TYPE_DIR) { + const bool is_parent_dir = FILENAME_IS_PARENT(file->relpath); + if (do_diropen == false) { params->file[0] = '\0'; retval = FILE_SELECT_DIR; } /* the path is too long and we are not going up! */ - else if (strcmp(file->relname, "..") && strlen(params->dir) + strlen(file->relname) >= FILE_MAX) { + else if (!is_parent_dir && strlen(params->dir) + strlen(file->relpath) >= FILE_MAX) { // XXX error("Path too long, cannot enter this directory"); } else { - if (strcmp(file->relname, "..") == 0) { + if (is_parent_dir) { /* avoids /../../ */ BLI_parent_dir(params->dir); + + if (params->recursion_level > 1) { + /* Disable 'dirtree' recursion when going up in tree. */ + params->recursion_level = 0; + filelist_setrecursion(sfile->files, params->recursion_level); + } } else { BLI_cleanup_dir(G.main->name, params->dir); - strcat(params->dir, file->relname); + strcat(params->dir, file->relpath); BLI_add_slash(params->dir); } - file_change_dir(C, 0); + ED_file_change_dir(C, false); retval = FILE_SELECT_DIR; } } else { - if (file->relname) { - BLI_strncpy(params->file, file->relname, FILE_MAXFILE); - } retval = FILE_SELECT_FILE; } + fileselect_file_set(sfile, selected_idx); } return retval; } +/** + * \warning: loops over all files so better use cautiously + */ +static bool file_is_any_selected(struct FileList *files) +{ + const int numfiles = filelist_files_ensure(files); + int i; + + /* Is any file selected ? */ + for (i = 0; i < numfiles; ++i) { + if (filelist_entry_select_index_get(files, i, CHECK_ALL)) { + return true; + } + } + + return false; +} + +/** + * If \a file is outside viewbounds, this adjusts view to make sure it's inside + */ +static void file_ensure_inside_viewbounds(ARegion *ar, SpaceFile *sfile, const int file) +{ + FileLayout *layout = ED_fileselect_get_layout(sfile, ar); + rctf *cur = &ar->v2d.cur; + rcti rect; + bool changed = true; + + file_tile_boundbox(ar, layout, file, &rect); + + /* down - also use if tile is higher than viewbounds so view is aligned to file name */ + if (cur->ymin > rect.ymin || layout->tile_h > ar->winy) { + cur->ymin = rect.ymin - (2 * layout->tile_border_y); + cur->ymax = cur->ymin + ar->winy; + } + /* up */ + else if (cur->ymax < rect.ymax) { + cur->ymax = rect.ymax + layout->tile_border_y; + cur->ymin = cur->ymax - ar->winy; + } + /* left - also use if tile is wider than viewbounds so view is aligned to file name */ + else if (cur->xmin > rect.xmin || layout->tile_w > ar->winx) { + cur->xmin = rect.xmin - layout->tile_border_x; + cur->xmax = cur->xmin + ar->winx; + } + /* right */ + else if (cur->xmax < rect.xmax) { + cur->xmax = rect.xmax + (2 * layout->tile_border_x); + cur->xmin = cur->xmax - ar->winx; + } + else { + BLI_assert(cur->xmin <= rect.xmin && cur->xmax >= rect.xmax && + cur->ymin <= rect.ymin && cur->ymax >= rect.ymax); + changed = false; + } + + if (changed) { + UI_view2d_curRect_validate(&ar->v2d); + } +} + static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select, bool fill, bool do_diropen) { @@ -219,25 +289,76 @@ static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select, const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_ALL; /* flag the files as selected in the filelist */ - filelist_select(sfile->files, &sel, select, SELECTED_FILE, check_type); + filelist_entries_select_index_range_set(sfile->files, &sel, select, FILE_SEL_SELECTED, check_type); /* Don't act on multiple selected files */ if (sel.first != sel.last) select = 0; /* Do we have a valid selection and are we actually selecting */ - if ((sel.last >= 0) && ((select == FILE_SEL_ADD) || (select == FILE_SEL_TOGGLE))) { + if ((sel.last >= 0) && (select != FILE_SEL_REMOVE)) { /* Check last selection, if selected, act on the file or dir */ - if (filelist_is_selected(sfile->files, sel.last, check_type)) { + if (filelist_entry_select_index_get(sfile->files, sel.last, check_type)) { retval = file_select_do(C, sel.last, do_diropen); } } + if (select != FILE_SEL_ADD && !file_is_any_selected(sfile->files)) { + sfile->params->active_file = -1; + } + else { + ARegion *ar = CTX_wm_region(C); + const FileLayout *layout = ED_fileselect_get_layout(sfile, ar); + + /* Adjust view to display selection. Doing iterations for first and last + * selected item makes view showing as much of the selection possible. + * Not really useful if tiles are (almost) bigger than viewbounds though. */ + if (((layout->flag & FILE_LAYOUT_HOR) && ar->winx > (1.2f * layout->tile_w)) || + ((layout->flag & FILE_LAYOUT_VER) && ar->winy > (2.0f * layout->tile_h))) + { + file_ensure_inside_viewbounds(ar, sfile, sel.last); + file_ensure_inside_viewbounds(ar, sfile, sel.first); + } + } + /* update operator for name change event */ - file_draw_check_cb(C, NULL, NULL); + file_draw_check(C); return retval; } +static int file_border_select_find_last_selected( + SpaceFile *sfile, ARegion *ar, const FileSelection *sel, + const int mouse_xy[2]) +{ + FileLayout *layout = ED_fileselect_get_layout(sfile, ar); + rcti bounds_first, bounds_last; + int dist_first, dist_last; + float mouseco_view[2]; + + UI_view2d_region_to_view(&ar->v2d, UNPACK2(mouse_xy), &mouseco_view[0], &mouseco_view[1]); + + file_tile_boundbox(ar, layout, sel->first, &bounds_first); + file_tile_boundbox(ar, layout, sel->last, &bounds_last); + + /* are first and last in the same column (horizontal layout)/row (vertical layout)? */ + if ((layout->flag & FILE_LAYOUT_HOR && bounds_first.xmin == bounds_last.xmin) || + (layout->flag & FILE_LAYOUT_VER && bounds_first.ymin != bounds_last.ymin)) + { + /* use vertical distance */ + const int my_loc = (int)mouseco_view[1]; + dist_first = BLI_rcti_length_y(&bounds_first, my_loc); + dist_last = BLI_rcti_length_y(&bounds_last, my_loc); + } + else { + /* use horizontal distance */ + const int mx_loc = (int)mouseco_view[0]; + dist_first = BLI_rcti_length_x(&bounds_first, mx_loc); + dist_last = BLI_rcti_length_x(&bounds_last, mx_loc); + } + + return (dist_first < dist_last) ? sel->first : sel->last; +} + static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); @@ -251,24 +372,40 @@ static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent * result = WM_border_select_modal(C, op, event); if (result == OPERATOR_RUNNING_MODAL) { - WM_operator_properties_border_to_rcti(op, &rect); BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect); sel = file_selection_get(C, &rect, 0); - if ( (sel.first != params->sel_first) || (sel.last != params->sel_last) ) { - file_deselect_all(sfile, HILITED_FILE); - filelist_select(sfile->files, &sel, FILE_SEL_ADD, HILITED_FILE, CHECK_ALL); + if ((sel.first != params->sel_first) || (sel.last != params->sel_last)) { + int idx; + + file_deselect_all(sfile, FILE_SEL_HIGHLIGHTED); + filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_HIGHLIGHTED, CHECK_ALL); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + + for (idx = sel.last; idx >= 0; idx--) { + const FileDirEntry *file = filelist_file(sfile->files, idx); + + /* dont highlight readonly file (".." or ".") on border select */ + if (FILENAME_IS_CURRPAR(file->relpath)) { + filelist_entry_select_set(sfile->files, file, FILE_SEL_REMOVE, FILE_SEL_HIGHLIGHTED, CHECK_ALL); + } + + /* make sure highlight_file is no readonly file */ + if (sel.last == idx) { + params->highlight_file = idx; + } + } } params->sel_first = sel.first; params->sel_last = sel.last; - + params->active_file = file_border_select_find_last_selected(sfile, ar, &sel, event->mval); } else { - params->active_file = -1; + params->highlight_file = -1; params->sel_first = params->sel_last = -1; - file_deselect_all(sfile, HILITED_FILE); + fileselect_file_set(sfile, params->active_file); + file_deselect_all(sfile, FILE_SEL_HIGHLIGHTED); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); } @@ -278,6 +415,7 @@ static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent * static int file_border_select_exec(bContext *C, wmOperator *op) { ARegion *ar = CTX_wm_region(C); + SpaceFile *sfile = CTX_wm_space_file(C); rcti rect; FileSelect ret; const bool select = (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_SELECT); @@ -286,14 +424,16 @@ static int file_border_select_exec(bContext *C, wmOperator *op) WM_operator_properties_border_to_rcti(op, &rect); if (!extend) { - SpaceFile *sfile = CTX_wm_space_file(C); - - file_deselect_all(sfile, SELECTED_FILE); + file_deselect_all(sfile, FILE_SEL_SELECTED); } BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect); ret = file_select(C, &rect, select ? FILE_SEL_ADD : FILE_SEL_REMOVE, false, false); + + /* unselect '..' parent entry - it's not supposed to be selected if more than one file is selected */ + filelist_entry_select_index_set(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + if (FILE_SELECT_DIR == ret) { WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); } @@ -340,10 +480,25 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (!BLI_rcti_isect_pt(&ar->v2d.mask, rect.xmin, rect.ymin)) return OPERATOR_CANCELLED; - /* single select, deselect all selected first */ - if (!extend) file_deselect_all(sfile, SELECTED_FILE); + if (sfile && sfile->params) { + int idx = sfile->params->highlight_file; + int numfiles = filelist_files_ensure(sfile->files); + + if ((idx >= 0) && (idx < numfiles)) { + /* single select, deselect all selected first */ + if (!extend) { + file_deselect_all(sfile, FILE_SEL_SELECTED); + } + } + } ret = file_select(C, &rect, extend ? FILE_SEL_TOGGLE : FILE_SEL_ADD, fill, do_diropen); + + if (extend) { + /* unselect '..' parent entry - it's not supposed to be selected if more than one file is selected */ + filelist_entry_select_index_set(sfile->files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + } + if (FILE_SELECT_DIR == ret) WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); else if (FILE_SELECT_FILE == ret) @@ -377,34 +532,267 @@ void FILE_OT_select(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +/** + * \returns true if selection has changed + */ +static bool file_walk_select_selection_set( + bContext *C, SpaceFile *sfile, + const int direction, const int numfiles, + const int active_old, const int active_new, const int other_site, + const bool has_selection, const bool extend, const bool fill) +{ + FileSelectParams *params = sfile->params; + struct FileList *files = sfile->files; + const int last_sel = params->active_file; /* store old value */ + int active = active_old; /* could use active_old instead, just for readability */ + bool deselect = false; + + BLI_assert(params); + + if (has_selection) { + if (extend && + filelist_entry_select_index_get(files, active_old, CHECK_ALL) && + filelist_entry_select_index_get(files, active_new, CHECK_ALL)) + { + /* conditions for deselecting: initial file is selected, new file is + * selected and either other_side isn't selected/found or we use fill */ + deselect = (fill || other_site == -1 || + !filelist_entry_select_index_get(files, other_site, CHECK_ALL)); + + /* don't change highlight_file here since we either want to deselect active or we want to + * walk through a block of selected files without selecting/deselecting anything */ + params->active_file = active_new; + /* but we want to change active if we use fill (needed to get correct selection bounds) */ + if (deselect && fill) { + active = active_new; + } + } + else { + /* regular selection change */ + params->active_file = active = active_new; + } + } + else { + /* select last file */ + if (ELEM(direction, FILE_SELECT_WALK_UP, FILE_SELECT_WALK_LEFT)) { + params->active_file = active = numfiles - 1; + } + /* select first file */ + else if (ELEM(direction, FILE_SELECT_WALK_DOWN, FILE_SELECT_WALK_RIGHT)) { + params->active_file = active = extend ? 1 : 0; + } + else { + BLI_assert(0); + } + } + + if (active < 0) { + return false; + } + + if (extend) { + /* highlight the active walker file for extended selection for better visual feedback */ + params->highlight_file = params->active_file; + + /* unselect '..' parent entry - it's not supposed to be selected if more than one file is selected */ + filelist_entry_select_index_set(files, 0, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + } + else { + /* deselect all first */ + file_deselect_all(sfile, FILE_SEL_SELECTED); + + /* highlight file under mouse pos */ + params->highlight_file = -1; + WM_event_add_mousemove(C); + } + + /* do the actual selection */ + if (fill) { + FileSelection sel = { MIN2(active, last_sel), MAX2(active, last_sel) }; + + /* clamping selection to not include '..' parent entry */ + if (sel.first == 0) { + sel.first = 1; + } + + /* fill selection between last and first selected file */ + filelist_entries_select_index_range_set( + files, &sel, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + /* entire sel is cleared here, so select active again */ + if (deselect) { + filelist_entry_select_index_set(files, active, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + } + } + else { + filelist_entry_select_index_set( + files, active, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + } + + BLI_assert(IN_RANGE(active, -1, numfiles)); + fileselect_file_set(sfile, params->active_file); + + /* ensure newly selected file is inside viewbounds */ + file_ensure_inside_viewbounds(CTX_wm_region(C), sfile, params->active_file); + + /* selection changed */ + return true; +} + +/** + * \returns true if selection has changed + */ +static bool file_walk_select_do( + bContext *C, SpaceFile *sfile, + FileSelectParams *params, const int direction, + const bool extend, const bool fill) +{ + struct FileList *files = sfile->files; + const int numfiles = filelist_files_ensure(files); + const bool has_selection = file_is_any_selected(files); + const int active_old = params->active_file; + int active_new = -1; + int other_site = -1; /* file on the other site of active_old */ + + + /* *** get all needed files for handling selection *** */ + + if (has_selection) { + ARegion *ar = CTX_wm_region(C); + FileLayout *layout = ED_fileselect_get_layout(sfile, ar); + const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->columns; + + if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_UP) || + (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_LEFT)) + { + active_new = active_old - 1; + other_site = active_old + 1; + } + else if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_DOWN) || + (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_RIGHT)) + { + active_new = active_old + 1; + other_site = active_old - 1; + } + else if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_LEFT) || + (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_UP)) + { + active_new = active_old - idx_shift; + other_site = active_old + idx_shift; + } + else if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_RIGHT) || + (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_DOWN)) + { + + active_new = active_old + idx_shift; + other_site = active_old - idx_shift; + } + else { + BLI_assert(0); + } + + if (!IN_RANGE(active_new, 0, numfiles)) { + if (extend) { + /* extend to invalid file -> abort */ + return false; + } + /* if we don't extend, selecting '..' (index == 0) is allowed so + * using key selection to go to parent directory is possible */ + else if (active_new != 0) { + /* select initial file */ + active_new = active_old; + } + } + if (!IN_RANGE(other_site, 0, numfiles)) { + other_site = -1; + } + } + + return file_walk_select_selection_set( + C, sfile, direction, numfiles, active_old, active_new, other_site, has_selection, extend, fill); +} + +static int file_walk_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C); + FileSelectParams *params = sfile->params; + const int direction = RNA_enum_get(op->ptr, "direction"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool fill = RNA_boolean_get(op->ptr, "fill"); + + if (file_walk_select_do(C, sfile, params, direction, extend, fill)) { + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void FILE_OT_select_walk(wmOperatorType *ot) +{ + static EnumPropertyItem direction_items[] = { + {FILE_SELECT_WALK_UP, "UP", 0, "Prev", ""}, + {FILE_SELECT_WALK_DOWN, "DOWN", 0, "Next", ""}, + {FILE_SELECT_WALK_LEFT, "LEFT", 0, "Left", ""}, + {FILE_SELECT_WALK_RIGHT, "RIGHT", 0, "Right", ""}, + {0, NULL, 0, NULL, NULL} + }; + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Walk Select/Deselect File"; + ot->description = "Select/Deselect files by walking through them"; + ot->idname = "FILE_OT_select_walk"; + + /* api callbacks */ + ot->invoke = file_walk_select_invoke; + ot->poll = ED_operator_file_active; + + /* properties */ + prop = RNA_def_enum(ot->srna, "direction", direction_items, 0, "Walk Direction", + "Select/Deselect file in this direction"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", + "Extend selection instead of deselecting everything first"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "fill", false, "Fill", "Select everything beginning with the last selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op)) { ScrArea *sa = CTX_wm_area(C); SpaceFile *sfile = CTX_wm_space_file(C); FileSelection sel; - int numfiles = filelist_numfiles(sfile->files); - int i; - bool is_selected = false; + const int numfiles = filelist_files_ensure(sfile->files); + const bool has_selection = file_is_any_selected(sfile->files); sel.first = 0; sel.last = numfiles - 1; - /* Is any file selected ? */ - for (i = 0; i < numfiles; ++i) { - if (filelist_is_selected(sfile->files, i, CHECK_ALL)) { - is_selected = true; - break; - } - } /* select all only if previously no file was selected */ - if (is_selected) { - filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, SELECTED_FILE, CHECK_ALL); + if (has_selection) { + filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + sfile->params->active_file = -1; } else { const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_FILES; - filelist_select(sfile->files, &sel, FILE_SEL_ADD, SELECTED_FILE, check_type); + int i; + + filelist_entries_select_index_range_set(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_SELECTED, check_type); + + /* set active_file to first selected */ + for (i = 0; i < numfiles; i++) { + if (filelist_entry_select_index_get(sfile->files, i, check_type)) { + sfile->params->active_file = i; + break; + } + } } + + file_draw_check(C); + WM_event_add_mousemove(C); ED_area_tag_redraw(sa); + return OPERATOR_FINISHED; } @@ -424,18 +812,20 @@ void FILE_OT_select_all_toggle(wmOperatorType *ot) /* ---------- BOOKMARKS ----------- */ +/* Note we could get rid of this one, but it's used by some addon so... Does not hurt keeping it around for now. */ static int bookmark_select_exec(bContext *C, wmOperator *op) { SpaceFile *sfile = CTX_wm_space_file(C); + PropertyRNA *prop; - if (RNA_struct_find_property(op->ptr, "dir")) { + if ((prop = RNA_struct_find_property(op->ptr, "dir"))) { char entry[256]; FileSelectParams *params = sfile->params; - RNA_string_get(op->ptr, "dir", entry); + RNA_property_string_get(op->ptr, prop, entry); BLI_strncpy(params->dir, entry, sizeof(params->dir)); BLI_cleanup_dir(G.main->name, params->dir); - file_change_dir(C, 1); + ED_file_change_dir(C, true); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); } @@ -465,17 +855,18 @@ static int bookmark_add_exec(bContext *C, wmOperator *UNUSED(op)) { ScrArea *sa = CTX_wm_area(C); SpaceFile *sfile = CTX_wm_space_file(C); - struct FSMenu *fsmenu = fsmenu_get(); + struct FSMenu *fsmenu = ED_fsmenu_get(); struct FileSelectParams *params = ED_fileselect_get_params(sfile); if (params->dir[0] != '\0') { char name[FILE_MAX]; - fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir, FS_INSERT_SAVE); - BLI_make_file_string("/", name, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); + fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir, NULL, FS_INSERT_SAVE); + BLI_make_file_string("/", name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); fsmenu_write_file(fsmenu, name); } + ED_area_tag_refresh(sa); ED_area_tag_redraw(sa); return OPERATOR_FINISHED; } @@ -495,17 +886,27 @@ void FILE_OT_bookmark_add(wmOperatorType *ot) static int bookmark_delete_exec(bContext *C, wmOperator *op) { ScrArea *sa = CTX_wm_area(C); - struct FSMenu *fsmenu = fsmenu_get(); - int nentries = fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS); - - if (RNA_struct_find_property(op->ptr, "index")) { - int index = RNA_int_get(op->ptr, "index"); + SpaceFile *sfile = CTX_wm_space_file(C); + struct FSMenu *fsmenu = ED_fsmenu_get(); + int nentries = ED_fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS); + + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "index"); + + if (prop) { + int index; + if (RNA_property_is_set(op->ptr, prop)) { + index = RNA_property_int_get(op->ptr, prop); + } + else { /* if index unset, use active bookmark... */ + index = sfile->bookmarknr; + } if ((index > -1) && (index < nentries)) { char name[FILE_MAX]; fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index); - BLI_make_file_string("/", name, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); + BLI_make_file_string("/", name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); fsmenu_write_file(fsmenu, name); + ED_area_tag_refresh(sa); ED_area_tag_redraw(sa); } } @@ -531,19 +932,150 @@ void FILE_OT_bookmark_delete(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +static int bookmark_cleanup_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ScrArea *sa = CTX_wm_area(C); + struct FSMenu *fsmenu = ED_fsmenu_get(); + struct FSMenuEntry *fsme_next, *fsme = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); + int index; + bool changed = false; + + for (index = 0; fsme; fsme = fsme_next) { + fsme_next = fsme->next; + + if (!BLI_is_dir(fsme->path)) { + fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index); + changed = true; + } + else { + index++; + } + } + + if (changed) { + char name[FILE_MAX]; + + BLI_make_file_string("/", name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); + fsmenu_write_file(fsmenu, name); + fsmenu_refresh_bookmarks_status(fsmenu); + ED_area_tag_refresh(sa); + ED_area_tag_redraw(sa); + } + + return OPERATOR_FINISHED; +} + +void FILE_OT_bookmark_cleanup(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Cleanup Bookmarks"; + ot->description = "Delete all invalid bookmarks"; + ot->idname = "FILE_OT_bookmark_cleanup"; + + /* api callbacks */ + ot->exec = bookmark_cleanup_exec; + ot->poll = ED_operator_file_active; + + /* properties */ +} + +enum { + FILE_BOOKMARK_MOVE_TOP = -2, + FILE_BOOKMARK_MOVE_UP = -1, + FILE_BOOKMARK_MOVE_DOWN = 1, + FILE_BOOKMARK_MOVE_BOTTOM = 2, +}; + +static int bookmark_move_exec(bContext *C, wmOperator *op) +{ + ScrArea *sa = CTX_wm_area(C); + SpaceFile *sfile = CTX_wm_space_file(C); + struct FSMenu *fsmenu = ED_fsmenu_get(); + struct FSMenuEntry *fsmentry = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); + const struct FSMenuEntry *fsmentry_org = fsmentry; + + char fname[FILE_MAX]; + + const int direction = RNA_enum_get(op->ptr, "direction"); + const int totitems = ED_fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS); + const int act_index = sfile->bookmarknr; + int new_index; + + if (totitems < 2) { + return OPERATOR_CANCELLED; + } + + switch (direction) { + case FILE_BOOKMARK_MOVE_TOP: + new_index = 0; + break; + case FILE_BOOKMARK_MOVE_BOTTOM: + new_index = totitems - 1; + break; + case FILE_BOOKMARK_MOVE_UP: + case FILE_BOOKMARK_MOVE_DOWN: + default: + new_index = (totitems + act_index + direction) % totitems; + break; + } + + if (new_index == act_index) { + return OPERATOR_CANCELLED; + } + + BLI_linklist_move_item((LinkNode **)&fsmentry, act_index, new_index); + if (fsmentry != fsmentry_org) { + ED_fsmenu_set_category(fsmenu, FS_CATEGORY_BOOKMARKS, fsmentry); + } + + /* Need to update active bookmark number. */ + sfile->bookmarknr = new_index; + + BLI_make_file_string("/", fname, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); + fsmenu_write_file(fsmenu, fname); + + ED_area_tag_redraw(sa); + return OPERATOR_FINISHED; +} + +void FILE_OT_bookmark_move(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {FILE_BOOKMARK_MOVE_TOP, "TOP", 0, "Top", "Top of the list"}, + {FILE_BOOKMARK_MOVE_UP, "UP", 0, "Up", ""}, + {FILE_BOOKMARK_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {FILE_BOOKMARK_MOVE_BOTTOM, "BOTTOM", 0, "Bottom", "Bottom of the list"}, + { 0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Move Bookmark"; + ot->idname = "FILE_OT_bookmark_move"; + ot->description = "Move the active bookmark up/down in the list"; + + /* api callbacks */ + ot->poll = ED_operator_file_active; + ot->exec = bookmark_move_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER; /* No undo! */ + + RNA_def_enum(ot->srna, "direction", slot_move, 0, "Direction", "Direction to move, UP or DOWN"); +} + static int reset_recent_exec(bContext *C, wmOperator *UNUSED(op)) { ScrArea *sa = CTX_wm_area(C); char name[FILE_MAX]; - struct FSMenu *fsmenu = fsmenu_get(); + struct FSMenu *fsmenu = ED_fsmenu_get(); - while (fsmenu_get_entry(fsmenu, FS_CATEGORY_RECENT, 0) != NULL) { + while (ED_fsmenu_get_entry(fsmenu, FS_CATEGORY_RECENT, 0) != NULL) { fsmenu_remove_entry(fsmenu, FS_CATEGORY_RECENT, 0); } - BLI_make_file_string("/", name, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); + BLI_make_file_string("/", name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); fsmenu_write_file(fsmenu, name); ED_area_tag_redraw(sa); - + return OPERATOR_FINISHED; } @@ -568,31 +1100,31 @@ int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my) if (sfile == NULL || sfile->files == NULL) return 0; - numfiles = filelist_numfiles(sfile->files); + numfiles = filelist_files_ensure(sfile->files); params = ED_fileselect_get_params(sfile); - origfile = params->active_file; + origfile = params->highlight_file; mx -= ar->winrct.xmin; my -= ar->winrct.ymin; if (BLI_rcti_isect_pt(&ar->v2d.mask, mx, my)) { float fx, fy; - int active_file; + int highlight_file; UI_view2d_region_to_view(v2d, mx, my, &fx, &fy); - active_file = ED_fileselect_layout_offset(sfile->layout, (int)(v2d->tot.xmin + fx), (int)(v2d->tot.ymax - fy)); + highlight_file = ED_fileselect_layout_offset(sfile->layout, (int)(v2d->tot.xmin + fx), (int)(v2d->tot.ymax - fy)); - if ((active_file >= 0) && (active_file < numfiles)) - params->active_file = active_file; + if ((highlight_file >= 0) && (highlight_file < numfiles)) + params->highlight_file = highlight_file; else - params->active_file = -1; + params->highlight_file = -1; } else - params->active_file = -1; + params->highlight_file = -1; - return (params->active_file != origfile); + return (params->highlight_file != origfile); } static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) @@ -658,65 +1190,67 @@ void FILE_OT_cancel(struct wmOperatorType *ot) void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath) { + PropertyRNA *prop; + BLI_join_dirfile(filepath, FILE_MAX, sfile->params->dir, sfile->params->file); /* XXX, not real length */ - if (RNA_struct_find_property(op->ptr, "relative_path")) { - if (RNA_boolean_get(op->ptr, "relative_path")) { + + if ((prop = RNA_struct_find_property(op->ptr, "relative_path"))) { + if (RNA_property_boolean_get(op->ptr, prop)) { BLI_path_rel(filepath, G.main->name); } } - if (RNA_struct_find_property(op->ptr, "filename")) { - RNA_string_set(op->ptr, "filename", sfile->params->file); + if ((prop = RNA_struct_find_property(op->ptr, "filename"))) { + RNA_property_string_set(op->ptr, prop, sfile->params->file); } - if (RNA_struct_find_property(op->ptr, "directory")) { - RNA_string_set(op->ptr, "directory", sfile->params->dir); + if ((prop = RNA_struct_find_property(op->ptr, "directory"))) { + RNA_property_string_set(op->ptr, prop, sfile->params->dir); } - if (RNA_struct_find_property(op->ptr, "filepath")) { - RNA_string_set(op->ptr, "filepath", filepath); + if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) { + RNA_property_string_set(op->ptr, prop, filepath); } /* some ops have multiple files to select */ /* this is called on operators check() so clear collections first since * they may be already set. */ { - PointerRNA itemptr; - PropertyRNA *prop_files = RNA_struct_find_property(op->ptr, "files"); - PropertyRNA *prop_dirs = RNA_struct_find_property(op->ptr, "dirs"); - int i, numfiles = filelist_numfiles(sfile->files); + int i, numfiles = filelist_files_ensure(sfile->files); - if (prop_files) { + if ((prop = RNA_struct_find_property(op->ptr, "files"))) { + PointerRNA itemptr; int num_files = 0; - RNA_property_collection_clear(op->ptr, prop_files); + RNA_property_collection_clear(op->ptr, prop); for (i = 0; i < numfiles; i++) { - if (filelist_is_selected(sfile->files, i, CHECK_FILES)) { - struct direntry *file = filelist_file(sfile->files, i); - RNA_property_collection_add(op->ptr, prop_files, &itemptr); - RNA_string_set(&itemptr, "name", file->relname); + if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) { + FileDirEntry *file = filelist_file(sfile->files, i); + RNA_property_collection_add(op->ptr, prop, &itemptr); + RNA_string_set(&itemptr, "name", file->relpath); num_files++; } } /* make sure the file specified in the filename button is added even if no files selected */ if (0 == num_files) { - RNA_property_collection_add(op->ptr, prop_files, &itemptr); + RNA_property_collection_add(op->ptr, prop, &itemptr); RNA_string_set(&itemptr, "name", sfile->params->file); } } - if (prop_dirs) { + if ((prop = RNA_struct_find_property(op->ptr, "dirs"))) { + PointerRNA itemptr; int num_dirs = 0; - RNA_property_collection_clear(op->ptr, prop_dirs); + RNA_property_collection_clear(op->ptr, prop); for (i = 0; i < numfiles; i++) { - if (filelist_is_selected(sfile->files, i, CHECK_DIRS)) { - struct direntry *file = filelist_file(sfile->files, i); - RNA_property_collection_add(op->ptr, prop_dirs, &itemptr); - RNA_string_set(&itemptr, "name", file->relname); + if (filelist_entry_select_index_get(sfile->files, i, CHECK_DIRS)) { + FileDirEntry *file = filelist_file(sfile->files, i); + RNA_property_collection_add(op->ptr, prop, &itemptr); + RNA_string_set(&itemptr, "name", file->relpath); num_dirs++; } } /* make sure the directory specified in the button is added even if no directory selected */ if (0 == num_dirs) { - RNA_property_collection_add(op->ptr, prop_dirs, &itemptr); + RNA_property_collection_add(op->ptr, prop, &itemptr); RNA_string_set(&itemptr, "name", sfile->params->dir); } } @@ -751,7 +1285,7 @@ void file_operator_to_sfile(SpaceFile *sfile, wmOperator *op) /* XXX, files and dirs updates missing, not really so important though */ } -void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2)) +void file_draw_check(bContext *C) { SpaceFile *sfile = CTX_wm_space_file(C); wmOperator *op = sfile->op; @@ -771,11 +1305,18 @@ void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2)) } } +/* for use with; UI_block_func_set */ +void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2)) +{ + file_draw_check(C); +} + bool file_draw_check_exists(SpaceFile *sfile) { if (sfile->op) { /* fails on reload */ - if (RNA_struct_find_property(sfile->op->ptr, "check_existing")) { - if (RNA_boolean_get(sfile->op->ptr, "check_existing")) { + PropertyRNA *prop; + if ((prop = RNA_struct_find_property(sfile->op->ptr, "check_existing"))) { + if (RNA_property_boolean_get(sfile->op->ptr, prop)) { char filepath[FILE_MAX]; BLI_join_dirfile(filepath, sizeof(filepath), sfile->params->dir, sfile->params->file); if (BLI_is_file(filepath)) { @@ -788,23 +1329,42 @@ bool file_draw_check_exists(SpaceFile *sfile) return false; } -/* sends events now, so things get handled on windowqueue level */ int file_exec(bContext *C, wmOperator *exec_op) { wmWindowManager *wm = CTX_wm_manager(C); SpaceFile *sfile = CTX_wm_space_file(C); + const struct FileDirEntry *file = filelist_file(sfile->files, sfile->params->active_file); char filepath[FILE_MAX]; - - if (sfile->op) { + + /* directory change */ + if (file && (file->typeflag & FILE_TYPE_DIR)) { + if (!file->relpath) { + return OPERATOR_CANCELLED; + } + + if (FILENAME_IS_PARENT(file->relpath)) { + BLI_parent_dir(sfile->params->dir); + } + else { + BLI_cleanup_dir(G.main->name, sfile->params->dir); + strcat(sfile->params->dir, file->relpath); + BLI_add_slash(sfile->params->dir); + } + + ED_file_change_dir(C, false); + } + /* opening file - sends events now, so things get handled on windowqueue level */ + else if (sfile->op) { wmOperator *op = sfile->op; /* when used as a macro, for doubleclick, * to prevent closing when doubleclicking on .. item */ if (RNA_boolean_get(exec_op->ptr, "need_active")) { + const int numfiles = filelist_files_ensure(sfile->files); int i, active = 0; - for (i = 0; i < filelist_numfiles(sfile->files); i++) { - if (filelist_is_selected(sfile->files, i, CHECK_ALL)) { + for (i = 0; i < numfiles; i++) { + if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) { active = 1; break; } @@ -818,15 +1378,17 @@ int file_exec(bContext *C, wmOperator *exec_op) file_sfile_to_operator(op, sfile, filepath); if (BLI_exists(sfile->params->dir)) { - fsmenu_insert_entry(fsmenu_get(), FS_CATEGORY_RECENT, sfile->params->dir, FS_INSERT_SAVE | FS_INSERT_FIRST); + fsmenu_insert_entry(ED_fsmenu_get(), FS_CATEGORY_RECENT, sfile->params->dir, NULL, + FS_INSERT_SAVE | FS_INSERT_FIRST); } - BLI_make_file_string(G.main->name, filepath, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); - fsmenu_write_file(fsmenu_get(), filepath); + BLI_make_file_string(G.main->name, filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), + BLENDER_BOOKMARK_FILE); + fsmenu_write_file(ED_fsmenu_get(), filepath); WM_event_fileselect_event(wm, op, EVT_FILESELECT_EXEC); } - + return OPERATOR_FINISHED; } @@ -859,16 +1421,21 @@ int file_parent_exec(bContext *C, wmOperator *UNUSED(unused)) BLI_cleanup_dir(G.main->name, sfile->params->dir); /* if not browsing in .blend file, we still want to check whether the path is a directory */ if (sfile->params->type == FILE_LOADLIB) { - char tdir[FILE_MAX], tgroup[FILE_MAX]; - if (BLO_is_a_library(sfile->params->dir, tdir, tgroup)) { - file_change_dir(C, 0); + char tdir[FILE_MAX]; + if (BLO_library_path_explode(sfile->params->dir, tdir, NULL, NULL)) { + ED_file_change_dir(C, false); } else { - file_change_dir(C, 1); + ED_file_change_dir(C, true); } } else { - file_change_dir(C, 1); + ED_file_change_dir(C, true); + } + if (sfile->params->recursion_level > 1) { + /* Disable 'dirtree' recursion when going up in tree. */ + sfile->params->recursion_level = 0; + filelist_setrecursion(sfile->files, sfile->params->recursion_level); } WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); } @@ -896,9 +1463,10 @@ static int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) { wmWindowManager *wm = CTX_wm_manager(C); SpaceFile *sfile = CTX_wm_space_file(C); - struct FSMenu *fsmenu = fsmenu_get(); + ScrArea *sa = CTX_wm_area(C); + struct FSMenu *fsmenu = ED_fsmenu_get(); - ED_fileselect_clear(wm, sfile); + ED_fileselect_clear(wm, sa, sfile); /* refresh system directory menu */ fsmenu_refresh_system_category(fsmenu); @@ -933,7 +1501,7 @@ int file_previous_exec(bContext *C, wmOperator *UNUSED(unused)) folderlist_popdir(sfile->folders_prev, sfile->params->dir); folderlist_pushdir(sfile->folders_next, sfile->params->dir); - file_change_dir(C, 1); + ED_file_change_dir(C, true); } WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); @@ -965,7 +1533,7 @@ int file_next_exec(bContext *C, wmOperator *UNUSED(unused)) // update folders_prev so we can check for it in folderlist_clear_next() folderlist_pushdir(sfile->folders_prev, sfile->params->dir); - file_change_dir(C, 1); + ED_file_change_dir(C, true); } WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); @@ -988,11 +1556,11 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w if (sfile->smoothscroll_timer == NULL || sfile->smoothscroll_timer != event->customdata) return OPERATOR_PASS_THROUGH; - numfiles = filelist_numfiles(sfile->files); + numfiles = filelist_files_ensure(sfile->files); /* check if we are editing a name */ for (i = 0; i < numfiles; ++i) { - if (filelist_is_selected(sfile->files, i, CHECK_ALL) ) { + if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL) ) { edit_idx = i; break; } @@ -1110,10 +1678,12 @@ int file_directory_new_exec(bContext *C, wmOperator *op) { char name[FILE_MAXFILE]; char path[FILE_MAX]; - int generate_name = 1; + bool generate_name = true; + PropertyRNA *prop; wmWindowManager *wm = CTX_wm_manager(C); SpaceFile *sfile = CTX_wm_space_file(C); + ScrArea *sa = CTX_wm_area(C); if (!sfile->params) { BKE_report(op->reports, RPT_WARNING, "No parent directory given"); @@ -1122,9 +1692,11 @@ int file_directory_new_exec(bContext *C, wmOperator *op) path[0] = '\0'; - if (RNA_struct_find_property(op->ptr, "directory")) { - RNA_string_get(op->ptr, "directory", path); - if (path[0] != '\0') generate_name = 0; + if ((prop = RNA_struct_find_property(op->ptr, "directory"))) { + RNA_property_string_get(op->ptr, prop, path); + if (path[0] != '\0') { + generate_name = false; + } } if (generate_name) { @@ -1134,10 +1706,23 @@ int file_directory_new_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } } + else { /* We assume we are able to generate a valid name! */ + char org_path[FILE_MAX]; + + BLI_strncpy(org_path, path, sizeof(org_path)); + if (BLI_path_make_safe(path)) { + BKE_reportf(op->reports, RPT_WARNING, "'%s' given path is OS-invalid, creating '%s' path instead", + org_path, path); + } + } /* create the file */ - BLI_dir_create_recursive(path); + if (!BLI_dir_create_recursive(path)) { + BKE_report(op->reports, RPT_ERROR, "Could not create new folder"); + return OPERATOR_CANCELLED; + } + /* Should no more be needed, now that BLI_dir_create_recursive returns a success state - but kept just in case. */ if (!BLI_exists(path)) { BKE_report(op->reports, RPT_ERROR, "Could not create new folder"); return OPERATOR_CANCELLED; @@ -1151,11 +1736,11 @@ int file_directory_new_exec(bContext *C, wmOperator *op) sfile->scroll_offset = 0; /* reload dir to make sure we're seeing what's in the directory */ - ED_fileselect_clear(wm, sfile); + ED_fileselect_clear(wm, sa, sfile); if (RNA_boolean_get(op->ptr, "open")) { BLI_strncpy(sfile->params->dir, path, sizeof(sfile->params->dir)); - file_change_dir(C, 1); + ED_file_change_dir(C, true); } WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); @@ -1185,6 +1770,7 @@ void FILE_OT_directory_new(struct wmOperatorType *ot) } +/* TODO This should go to BLI_path_utils. */ static void file_expand_directory(bContext *C) { SpaceFile *sfile = CTX_wm_space_file(C); @@ -1197,7 +1783,7 @@ static void file_expand_directory(bContext *C) else if (sfile->params->dir[0] == '~') { char tmpstr[sizeof(sfile->params->dir) - 1]; BLI_strncpy(tmpstr, sfile->params->dir + 1, sizeof(tmpstr)); - BLI_join_dirfile(sfile->params->dir, sizeof(sfile->params->dir), BLI_getDefaultDocumentFolder(), tmpstr); + BLI_join_dirfile(sfile->params->dir, sizeof(sfile->params->dir), BKE_appdir_folder_default(), tmpstr); } else if (sfile->params->dir[0] == '\0') @@ -1225,6 +1811,7 @@ static void file_expand_directory(bContext *C) } } +/* TODO check we still need this, it's annoying to have OS-specific code here... :/ */ #if defined(WIN32) static bool can_create_dir(const char *dir) { @@ -1260,7 +1847,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN if (BLI_exists(sfile->params->dir)) { /* if directory exists, enter it immediately */ - file_change_dir(C, 1); + ED_file_change_dir(C, true); /* don't do for now because it selects entire text instead of * placing cursor at the end */ @@ -1311,6 +1898,9 @@ void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg matches = file_select_match(sfile, sfile->params->file, matched_file); + /* *After* file_select_match! */ + BLI_filename_make_safe(sfile->params->file); + if (matches) { /* int i, numfiles = filelist_numfiles(sfile->files); */ /* XXX UNUSED */ sfile->params->file[0] = '\0'; @@ -1329,23 +1919,26 @@ void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg BLI_cleanup_dir(G.main->name, filepath); BLI_strncpy(sfile->params->dir, filepath, sizeof(sfile->params->dir)); sfile->params->file[0] = '\0'; - file_change_dir(C, 1); + ED_file_change_dir(C, true); UI_textbutton_activate_but(C, but); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); } else if (sfile->params->type == FILE_LOADLIB) { - char tdir[FILE_MAX], tgroup[FILE_MAX]; + char tdir[FILE_MAX]; BLI_add_slash(filepath); - if (BLO_is_a_library(filepath, tdir, tgroup)) { + if (BLO_library_path_explode(filepath, tdir, NULL, NULL)) { BLI_cleanup_dir(G.main->name, filepath); BLI_strncpy(sfile->params->dir, filepath, sizeof(sfile->params->dir)); sfile->params->file[0] = '\0'; - file_change_dir(C, 0); + ED_file_change_dir(C, false); UI_textbutton_activate_but(C, but); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); } } } + else if (matches > 1) { + file_draw_check(C); + } } } @@ -1365,10 +1958,11 @@ static int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused)) { wmWindowManager *wm = CTX_wm_manager(C); SpaceFile *sfile = CTX_wm_space_file(C); + ScrArea *sa = CTX_wm_area(C); if (sfile->params) { sfile->params->flag ^= FILE_HIDE_DOT; - ED_fileselect_clear(wm, sfile); + ED_fileselect_clear(wm, sa, sfile); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); } @@ -1388,37 +1982,37 @@ void FILE_OT_hidedot(struct wmOperatorType *ot) ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ } -ARegion *file_buttons_region(ScrArea *sa) +ARegion *file_tools_region(ScrArea *sa) { ARegion *ar, *arnew; - - for (ar = sa->regionbase.first; ar; ar = ar->next) - if (ar->regiontype == RGN_TYPE_CHANNELS) - return ar; + + if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOLS)) != NULL) + return ar; /* add subdiv level; after header */ - for (ar = sa->regionbase.first; ar; ar = ar->next) - if (ar->regiontype == RGN_TYPE_HEADER) - break; + ar = BKE_area_find_region_type(sa, RGN_TYPE_HEADER); /* is error! */ - if (ar == NULL) return NULL; - - arnew = MEM_callocN(sizeof(ARegion), "buttons for file panels"); + if (ar == NULL) + return NULL; + arnew = MEM_callocN(sizeof(ARegion), "tools for file"); BLI_insertlinkafter(&sa->regionbase, ar, arnew); - arnew->regiontype = RGN_TYPE_CHANNELS; + arnew->regiontype = RGN_TYPE_TOOLS; arnew->alignment = RGN_ALIGN_LEFT; - - arnew->flag = RGN_FLAG_HIDDEN; - + + ar = MEM_callocN(sizeof(ARegion), "tool props for file"); + BLI_insertlinkafter(&sa->regionbase, arnew, ar); + ar->regiontype = RGN_TYPE_TOOL_PROPS; + ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV; + return arnew; } static int file_bookmark_toggle_exec(bContext *C, wmOperator *UNUSED(unused)) { ScrArea *sa = CTX_wm_area(C); - ARegion *ar = file_buttons_region(sa); + ARegion *ar = file_tools_region(sa); if (ar) ED_region_toggle_hidden(C, ar); @@ -1448,7 +2042,7 @@ static int file_filenum_exec(bContext *C, wmOperator *op) if (sfile->params && (inc != 0)) { BLI_newname(sfile->params->file, inc); ED_area_tag_redraw(sa); - file_draw_check_cb(C, NULL, NULL); + file_draw_check(C); // WM_event_add_notifier(C, NC_WINDOW, NULL); } @@ -1477,12 +2071,12 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op)) SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C); if (sfile->params) { - int idx = sfile->params->active_file; - int numfiles = filelist_numfiles(sfile->files); - if ( (0 <= idx) && (idx < numfiles) ) { - struct direntry *file = filelist_file(sfile->files, idx); - filelist_select_file(sfile->files, idx, FILE_SEL_ADD, EDITING_FILE, CHECK_ALL); - BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE); + int idx = sfile->params->highlight_file; + int numfiles = filelist_files_ensure(sfile->files); + if ((0 <= idx) && (idx < numfiles)) { + FileDirEntry *file = filelist_file(sfile->files, idx); + filelist_entry_select_index_set(sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL); + BLI_strncpy(sfile->params->renameedit, file->relpath, FILE_MAXFILE); sfile->params->renamefile[0] = '\0'; } ED_area_tag_redraw(sa); @@ -1494,20 +2088,34 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op)) static int file_rename_poll(bContext *C) { - int poll = ED_operator_file_active(C); + bool poll = ED_operator_file_active(C); SpaceFile *sfile = CTX_wm_space_file(C); if (sfile && sfile->params) { - if (sfile->params->active_file < 0) { - poll = 0; + int idx = sfile->params->highlight_file; + int numfiles = filelist_files_ensure(sfile->files); + + if ((0 <= idx) && (idx < numfiles)) { + FileDirEntry *file = filelist_file(sfile->files, idx); + if (FILENAME_IS_CURRPAR(file->relpath)) { + poll = false; + } + } + + if (sfile->params->highlight_file < 0) { + poll = false; } else { - char dir[FILE_MAX], group[FILE_MAX]; - if (filelist_islibrary(sfile->files, dir, group)) poll = 0; + char dir[FILE_MAX]; + if (filelist_islibrary(sfile->files, dir, NULL)) { + poll = false; + } } } - else - poll = 0; + else { + poll = false; + } + return poll; } @@ -1530,14 +2138,14 @@ static int file_delete_poll(bContext *C) SpaceFile *sfile = CTX_wm_space_file(C); if (sfile && sfile->params) { - char dir[FILE_MAX], group[FILE_MAX]; - int numfiles = filelist_numfiles(sfile->files); + char dir[FILE_MAX]; + int numfiles = filelist_files_ensure(sfile->files); int i; int num_selected = 0; - if (filelist_islibrary(sfile->files, dir, group)) poll = 0; + if (filelist_islibrary(sfile->files, dir, NULL)) poll = 0; for (i = 0; i < numfiles; i++) { - if (filelist_is_selected(sfile->files, i, CHECK_FILES)) { + if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) { num_selected++; } } @@ -1556,19 +2164,20 @@ int file_delete_exec(bContext *C, wmOperator *UNUSED(op)) char str[FILE_MAX]; wmWindowManager *wm = CTX_wm_manager(C); SpaceFile *sfile = CTX_wm_space_file(C); - struct direntry *file; - int numfiles = filelist_numfiles(sfile->files); + ScrArea *sa = CTX_wm_area(C); + FileDirEntry *file; + int numfiles = filelist_files_ensure(sfile->files); int i; for (i = 0; i < numfiles; i++) { - if (filelist_is_selected(sfile->files, i, CHECK_FILES)) { + if (filelist_entry_select_index_get(sfile->files, i, CHECK_FILES)) { file = filelist_file(sfile->files, i); - BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relname); + BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relpath); BLI_delete(str, false, false); } } - ED_fileselect_clear(wm, sfile); + ED_fileselect_clear(wm, sa, sfile); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); return OPERATOR_FINISHED; |