Blender  V2.93
file_ops.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19 
24 #include "BLI_utildefines.h"
25 
26 #include "BLI_blenlib.h"
27 #include "BLI_linklist.h"
28 #include "BLI_math.h"
29 
30 #include "BLO_readfile.h"
31 
32 #include "BKE_appdir.h"
33 #include "BKE_context.h"
34 #include "BKE_global.h"
35 #include "BKE_main.h"
36 #include "BKE_report.h"
37 #include "BKE_screen.h"
38 
39 #ifdef WIN32
40 # include "BLI_winstuff.h"
41 #endif
42 
43 #include "ED_asset.h"
44 #include "ED_fileselect.h"
45 #include "ED_screen.h"
46 #include "ED_select_utils.h"
47 
48 #include "UI_interface.h"
49 #include "UI_interface_icons.h"
50 #include "UI_resources.h"
51 
52 #include "MEM_guardedalloc.h"
53 
54 #include "RNA_access.h"
55 #include "RNA_define.h"
56 
57 #include "UI_view2d.h"
58 
59 #include "WM_api.h"
60 #include "WM_types.h"
61 
62 #include "file_intern.h"
63 #include "filelist.h"
64 #include "fsmenu.h"
65 
66 #include <ctype.h>
67 #include <errno.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 
72 /* -------------------------------------------------------------------- */
77  ARegion *region,
78  const rcti *rect_region)
79 {
80  FileSelection sel;
81 
82  View2D *v2d = &region->v2d;
83  rcti rect_view;
84  rctf rect_view_fl;
85  rctf rect_region_fl;
86 
87  BLI_rctf_rcti_copy(&rect_region_fl, rect_region);
88 
89  /* Okay, manipulating v2d rects here is hacky... */
90  v2d->mask.ymax -= sfile->layout->offset_top;
91  v2d->cur.ymax -= sfile->layout->offset_top;
92  UI_view2d_region_to_view_rctf(v2d, &rect_region_fl, &rect_view_fl);
93  v2d->mask.ymax += sfile->layout->offset_top;
94  v2d->cur.ymax += sfile->layout->offset_top;
95 
96  BLI_rcti_init(&rect_view,
97  (int)(v2d->tot.xmin + rect_view_fl.xmin),
98  (int)(v2d->tot.xmin + rect_view_fl.xmax),
99  (int)(v2d->tot.ymax - rect_view_fl.ymin),
100  (int)(v2d->tot.ymax - rect_view_fl.ymax));
101 
102  sel = ED_fileselect_layout_offset_rect(sfile->layout, &rect_view);
103 
104  return sel;
105 }
106 
107 static void file_deselect_all(SpaceFile *sfile, uint flag)
108 {
109  FileSelection sel;
110  sel.first = 0;
111  sel.last = filelist_files_ensure(sfile->files) - 1;
112 
114 }
115 
116 typedef enum FileSelect {
121 
122 static void clamp_to_filelist(int numfiles, FileSelection *sel)
123 {
124  /* box select before the first file */
125  if ((sel->first < 0) && (sel->last >= 0)) {
126  sel->first = 0;
127  }
128  /* don't select if everything is outside filelist */
129  if ((sel->first >= numfiles) && ((sel->last < 0) || (sel->last >= numfiles))) {
130  sel->first = -1;
131  sel->last = -1;
132  }
133 
134  /* fix if last file invalid */
135  if ((sel->first > 0) && (sel->last < 0)) {
136  sel->last = numfiles - 1;
137  }
138 
139  /* clamp */
140  if ((sel->first >= numfiles)) {
141  sel->first = numfiles - 1;
142  }
143  if ((sel->last >= numfiles)) {
144  sel->last = numfiles - 1;
145  }
146 }
147 
148 static FileSelection file_selection_get(bContext *C, const rcti *rect, bool fill)
149 {
150  ARegion *region = CTX_wm_region(C);
151  SpaceFile *sfile = CTX_wm_space_file(C);
152  int numfiles = filelist_files_ensure(sfile->files);
153  FileSelection sel;
154 
155  sel = find_file_mouse_rect(sfile, region, rect);
156  if (!((sel.first == -1) && (sel.last == -1))) {
157  clamp_to_filelist(numfiles, &sel);
158  }
159 
160  /* if desired, fill the selection up from the last selected file to the current one */
161  if (fill && (sel.last >= 0) && (sel.last < numfiles)) {
162  int f;
163  /* Try to find a smaller-index selected item. */
164  for (f = sel.last; f >= 0; f--) {
166  break;
167  }
168  }
169  if (f >= 0) {
170  sel.first = f + 1;
171  }
172  /* If none found, try to find a higher-index selected item. */
173  else {
174  for (f = sel.first; f < numfiles; f++) {
176  break;
177  }
178  }
179  if (f < numfiles) {
180  sel.last = f - 1;
181  }
182  }
183  }
184  return sel;
185 }
186 
187 static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
188 {
189  Main *bmain = CTX_data_main(C);
191  SpaceFile *sfile = CTX_wm_space_file(C);
193  int numfiles = filelist_files_ensure(sfile->files);
194  const FileDirEntry *file;
195 
196  /* make the selected file active */
197  if ((selected_idx >= 0) && (selected_idx < numfiles) &&
198  (file = filelist_file(sfile->files, selected_idx))) {
199  params->highlight_file = selected_idx;
200  params->active_file = selected_idx;
201 
202  if (file->typeflag & FILE_TYPE_DIR) {
203  const bool is_parent_dir = FILENAME_IS_PARENT(file->relpath);
204 
205  if (do_diropen == false) {
206  retval = FILE_SELECT_DIR;
207  }
208  /* the path is too long and we are not going up! */
209  else if (!is_parent_dir && strlen(params->dir) + strlen(file->relpath) >= FILE_MAX) {
210  // XXX error("Path too long, cannot enter this directory");
211  }
212  else {
213  if (is_parent_dir) {
214  /* avoids /../../ */
216 
217  if (params->recursion_level > 1) {
218  /* Disable 'dirtree' recursion when going up in tree. */
219  params->recursion_level = 0;
220  filelist_setrecursion(sfile->files, params->recursion_level);
221  }
222  }
223  else {
225  strcat(params->dir, file->relpath);
227  }
228 
230  retval = FILE_SELECT_DIR;
231  }
232  }
233  else {
234  retval = FILE_SELECT_FILE;
235  }
236  fileselect_file_set(sfile, selected_idx);
237  }
238  return retval;
239 }
240 
244 static bool file_is_any_selected(struct FileList *files)
245 {
246  const int numfiles = filelist_files_ensure(files);
247  int i;
248 
249  /* Is any file selected ? */
250  for (i = 0; i < numfiles; i++) {
252  return true;
253  }
254  }
255 
256  return false;
257 }
258 
260 {
261  const int numfiles = filelist_files_ensure(files);
262  FileSelection selection = {-1, -1};
263 
264  /* Iterate over the files once but in two loops, one to find the first selected file, and the
265  * other to find the last. */
266 
267  int file_index;
268  for (file_index = 0; file_index < numfiles; file_index++) {
269  if (filelist_entry_is_selected(files, file_index)) {
270  /* First selected entry found. */
271  selection.first = file_index;
272  break;
273  }
274  }
275 
276  for (; file_index < numfiles; file_index++) {
277  if (filelist_entry_is_selected(files, file_index)) {
278  selection.last = file_index;
279  /* Keep looping, we may find more selected files. */
280  }
281  }
282 
283  return selection;
284 }
285 
289 static void file_ensure_inside_viewbounds(ARegion *region, SpaceFile *sfile, const int file)
290 {
291  FileLayout *layout = ED_fileselect_get_layout(sfile, region);
292  rctf *cur = &region->v2d.cur;
293  rcti rect;
294  bool changed = true;
295 
296  file_tile_boundbox(region, layout, file, &rect);
297 
298  /* down - also use if tile is higher than viewbounds so view is aligned to file name */
299  if (cur->ymin > rect.ymin || layout->tile_h > region->winy) {
300  cur->ymin = rect.ymin - (2 * layout->tile_border_y);
301  cur->ymax = cur->ymin + region->winy;
302  }
303  /* up */
304  else if ((cur->ymax - layout->offset_top) < rect.ymax) {
305  cur->ymax = rect.ymax + layout->tile_border_y + layout->offset_top;
306  cur->ymin = cur->ymax - region->winy;
307  }
308  /* left - also use if tile is wider than viewbounds so view is aligned to file name */
309  else if (cur->xmin > rect.xmin || layout->tile_w > region->winx) {
310  cur->xmin = rect.xmin - layout->tile_border_x;
311  cur->xmax = cur->xmin + region->winx;
312  }
313  /* right */
314  else if (cur->xmax < rect.xmax) {
315  cur->xmax = rect.xmax + (2 * layout->tile_border_x);
316  cur->xmin = cur->xmax - region->winx;
317  }
318  else {
319  BLI_assert(cur->xmin <= rect.xmin && cur->xmax >= rect.xmax && cur->ymin <= rect.ymin &&
320  (cur->ymax - layout->offset_top) >= rect.ymax);
321  changed = false;
322  }
323 
324  if (changed) {
326  }
327 }
328 
330  SpaceFile *sfile,
331  FileSelection *sel)
332 {
333  const FileLayout *layout = ED_fileselect_get_layout(sfile, region);
334 
335  if (((layout->flag & FILE_LAYOUT_HOR) && region->winx <= (1.2f * layout->tile_w)) &&
336  ((layout->flag & FILE_LAYOUT_VER) && region->winy <= (2.0f * layout->tile_h))) {
337  return;
338  }
339 
340  /* Adjust view to display selection. Doing iterations for first and last
341  * selected item makes view showing as much of the selection possible.
342  * Not really useful if tiles are (almost) bigger than viewbounds though. */
343  file_ensure_inside_viewbounds(region, sfile, sel->last);
344  file_ensure_inside_viewbounds(region, sfile, sel->first);
345 }
346 
348  bContext *C, const rcti *rect, FileSelType select, bool fill, bool do_diropen)
349 {
350  SpaceFile *sfile = CTX_wm_space_file(C);
353  FileSelection sel = file_selection_get(C, rect, fill); /* get the selection */
354  const FileCheckType check_type = (params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_ALL;
355 
356  /* flag the files as selected in the filelist */
358  sfile->files, &sel, select, FILE_SEL_SELECTED, check_type);
359 
360  /* Don't act on multiple selected files */
361  if (sel.first != sel.last) {
362  select = 0;
363  }
364 
365  /* Do we have a valid selection and are we actually selecting */
366  if ((sel.last >= 0) && (select != FILE_SEL_REMOVE)) {
367  /* Check last selection, if selected, act on the file or dir */
368  if (filelist_entry_select_index_get(sfile->files, sel.last, check_type)) {
369  retval = file_select_do(C, sel.last, do_diropen);
370  }
371  }
372 
373  if (select != FILE_SEL_ADD && !file_is_any_selected(sfile->files)) {
374  params->active_file = -1;
375  }
376  else if (sel.last >= 0) {
377  ARegion *region = CTX_wm_region(C);
378  file_ensure_selection_inside_viewbounds(region, sfile, &sel);
379  }
380 
381  /* update operator for name change event */
383 
384  return retval;
385 }
386 
389 /* -------------------------------------------------------------------- */
394  ARegion *region,
395  const FileSelection *sel,
396  const int mouse_xy[2])
397 {
398  FileLayout *layout = ED_fileselect_get_layout(sfile, region);
399  rcti bounds_first, bounds_last;
400  int dist_first, dist_last;
401  float mouseco_view[2];
402 
403  UI_view2d_region_to_view(&region->v2d, UNPACK2(mouse_xy), &mouseco_view[0], &mouseco_view[1]);
404 
405  file_tile_boundbox(region, layout, sel->first, &bounds_first);
406  file_tile_boundbox(region, layout, sel->last, &bounds_last);
407 
408  /* are first and last in the same column (horizontal layout)/row (vertical layout)? */
409  if ((layout->flag & FILE_LAYOUT_HOR && bounds_first.xmin == bounds_last.xmin) ||
410  (layout->flag & FILE_LAYOUT_VER && bounds_first.ymin != bounds_last.ymin)) {
411  /* use vertical distance */
412  const int my_loc = (int)mouseco_view[1];
413  dist_first = BLI_rcti_length_y(&bounds_first, my_loc);
414  dist_last = BLI_rcti_length_y(&bounds_last, my_loc);
415  }
416  else {
417  /* use horizontal distance */
418  const int mx_loc = (int)mouseco_view[0];
419  dist_first = BLI_rcti_length_x(&bounds_first, mx_loc);
420  dist_last = BLI_rcti_length_x(&bounds_last, mx_loc);
421  }
422 
423  return (dist_first < dist_last) ? sel->first : sel->last;
424 }
425 
426 static int file_box_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
427 {
428  ARegion *region = CTX_wm_region(C);
429  SpaceFile *sfile = CTX_wm_space_file(C);
431  FileSelection sel;
432  rcti rect;
433 
434  int result;
435 
436  result = WM_gesture_box_modal(C, op, event);
437 
440 
441  ED_fileselect_layout_isect_rect(sfile->layout, &region->v2d, &rect, &rect);
442 
443  sel = file_selection_get(C, &rect, 0);
444  if ((sel.first != params->sel_first) || (sel.last != params->sel_last)) {
445  int idx;
446 
451 
452  for (idx = sel.last; idx >= 0; idx--) {
453  const FileDirEntry *file = filelist_file(sfile->files, idx);
454 
455  /* Don't highlight read-only file (".." or ".") on box select. */
456  if (FILENAME_IS_CURRPAR(file->relpath)) {
459  }
460 
461  /* make sure highlight_file is no readonly file */
462  if (sel.last == idx) {
463  params->highlight_file = idx;
464  }
465  }
466  }
467  params->sel_first = sel.first;
468  params->sel_last = sel.last;
469  params->active_file = file_box_select_find_last_selected(sfile, region, &sel, event->mval);
470  }
471  else {
472  params->highlight_file = -1;
473  params->sel_first = params->sel_last = -1;
474  fileselect_file_set(sfile, params->active_file);
477  }
478 
479  return result;
480 }
481 
483 {
484  ARegion *region = CTX_wm_region(C);
485  SpaceFile *sfile = CTX_wm_space_file(C);
486  rcti rect;
487  FileSelect ret;
488 
490 
491  const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
492  const bool select = (sel_op != SEL_OP_SUB);
493  if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
495  }
496 
497  ED_fileselect_layout_isect_rect(sfile->layout, &region->v2d, &rect, &rect);
498 
499  ret = file_select(C, &rect, select ? FILE_SEL_ADD : FILE_SEL_REMOVE, false, false);
500 
501  /* unselect '..' parent entry - it's not supposed to be selected if more than
502  * one file is selected */
504 
505  if (FILE_SELECT_DIR == ret) {
507  }
508  else if (FILE_SELECT_FILE == ret) {
510  }
511  return OPERATOR_FINISHED;
512 }
513 
515 {
516  /* identifiers */
517  ot->name = "Box Select";
518  ot->description = "Activate/select the file(s) contained in the border";
519  ot->idname = "FILE_OT_select_box";
520 
521  /* api callbacks */
527 
528  /* properties */
531 }
532 
535 /* -------------------------------------------------------------------- */
539 static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
540 {
541  ARegion *region = CTX_wm_region(C);
542  SpaceFile *sfile = CTX_wm_space_file(C);
543  FileSelect ret;
544  rcti rect;
545  const bool extend = RNA_boolean_get(op->ptr, "extend");
546  const bool fill = RNA_boolean_get(op->ptr, "fill");
547  const bool do_diropen = RNA_boolean_get(op->ptr, "open");
548  const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
549 
550  if (region->regiontype != RGN_TYPE_WINDOW) {
551  return OPERATOR_CANCELLED;
552  }
553 
554  rect.xmin = rect.xmax = event->mval[0];
555  rect.ymin = rect.ymax = event->mval[1];
556 
557  if (!ED_fileselect_layout_is_inside_pt(sfile->layout, &region->v2d, rect.xmin, rect.ymin)) {
559  }
560 
562  if (sfile && params) {
563  int idx = params->highlight_file;
564  int numfiles = filelist_files_ensure(sfile->files);
565 
566  if ((idx >= 0) && (idx < numfiles)) {
567  /* single select, deselect all selected first */
568  if (!extend) {
570  }
571  }
572  }
573 
574  ret = file_select(C, &rect, extend ? FILE_SEL_TOGGLE : FILE_SEL_ADD, fill, do_diropen);
575 
576  if (extend) {
577  /* unselect '..' parent entry - it's not supposed to be selected if more
578  * than one file is selected */
580  }
581 
582  if (ret == FILE_SELECT_NOTHING) {
583  if (deselect_all) {
585  }
586  }
587  else if (ret == FILE_SELECT_DIR) {
589  }
590  else if (ret == FILE_SELECT_FILE) {
592  }
593 
594  WM_event_add_mousemove(CTX_wm_window(C)); /* for directory changes */
596 
597  return OPERATOR_FINISHED;
598 }
599 
601 {
602  PropertyRNA *prop;
603 
604  /* identifiers */
605  ot->name = "Select";
606  ot->idname = "FILE_OT_select";
607  ot->description = "Handle mouse clicks to select and activate items";
608 
609  /* api callbacks */
612 
613  /* properties */
614  prop = RNA_def_boolean(ot->srna,
615  "extend",
616  false,
617  "Extend",
618  "Extend selection instead of deselecting everything first");
620  prop = RNA_def_boolean(
621  ot->srna, "fill", false, "Fill", "Select everything beginning with the last selection");
623  prop = RNA_def_boolean(ot->srna, "open", true, "Open", "Open a directory when selecting it");
625  prop = RNA_def_boolean(ot->srna,
626  "deselect_all",
627  false,
628  "Deselect On Nothing",
629  "Deselect all when nothing under the cursor");
631 }
632 
635 /* -------------------------------------------------------------------- */
643  ARegion *region,
644  SpaceFile *sfile,
645  const int direction,
646  const int numfiles,
647  const int active_old,
648  const int active_new,
649  const int other_site,
650  const bool has_selection,
651  const bool extend,
652  const bool fill)
653 {
655  struct FileList *files = sfile->files;
656  const int last_sel = params->active_file; /* store old value */
657  int active = active_old; /* could use active_old instead, just for readability */
658  bool deselect = false;
659 
661 
662  if (numfiles == 0) {
663  /* No files visible, nothing to do. */
664  return false;
665  }
666 
667  if (has_selection) {
668  if (extend && filelist_entry_select_index_get(files, active_old, CHECK_ALL) &&
669  filelist_entry_select_index_get(files, active_new, CHECK_ALL)) {
670  /* conditions for deselecting: initial file is selected, new file is
671  * selected and either other_side isn't selected/found or we use fill */
672  deselect = (fill || other_site == -1 ||
673  !filelist_entry_select_index_get(files, other_site, CHECK_ALL));
674 
675  /* don't change highlight_file here since we either want to deselect active or we want
676  * to walk through a block of selected files without selecting/deselecting anything */
677  params->active_file = active_new;
678  /* but we want to change active if we use fill
679  * (needed to get correct selection bounds) */
680  if (deselect && fill) {
681  active = active_new;
682  }
683  }
684  else {
685  /* regular selection change */
686  params->active_file = active = active_new;
687  }
688  }
689  else {
690  /* select last file */
691  if (ELEM(direction, UI_SELECT_WALK_UP, UI_SELECT_WALK_LEFT)) {
692  params->active_file = active = numfiles - 1;
693  }
694  /* select first file */
695  else if (ELEM(direction, UI_SELECT_WALK_DOWN, UI_SELECT_WALK_RIGHT)) {
696  params->active_file = active = 0;
697  }
698  else {
699  BLI_assert(0);
700  }
701  }
702 
703  if (active < 0) {
704  return false;
705  }
706 
707  if (extend) {
708  /* highlight the active walker file for extended selection for better visual feedback */
709  params->highlight_file = params->active_file;
710 
711  /* unselect '..' parent entry - it's not supposed to be selected if more
712  * than one file is selected */
714  }
715  else {
716  /* deselect all first */
718 
719  /* highlight file under mouse pos */
720  params->highlight_file = -1;
722  }
723 
724  /* do the actual selection */
725  if (fill) {
726  FileSelection sel = {MIN2(active, last_sel), MAX2(active, last_sel)};
727 
728  /* fill selection between last and first selected file */
730  files, &sel, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
731  /* entire sel is cleared here, so select active again */
732  if (deselect) {
734  }
735 
736  /* unselect '..' parent entry - it's not supposed to be selected if more
737  * than one file is selected */
738  if ((sel.last - sel.first) > 1) {
740  }
741  }
742  else {
745  }
746 
747  BLI_assert(IN_RANGE(active, -1, numfiles));
748  fileselect_file_set(sfile, params->active_file);
749 
750  /* ensure newly selected file is inside viewbounds */
751  file_ensure_inside_viewbounds(region, sfile, params->active_file);
752 
753  /* selection changed */
754  return true;
755 }
756 
761  SpaceFile *sfile,
763  const int direction,
764  const bool extend,
765  const bool fill)
766 {
767  wmWindow *win = CTX_wm_window(C);
768  ARegion *region = CTX_wm_region(C);
769  struct FileList *files = sfile->files;
770  const int numfiles = filelist_files_ensure(files);
771  const bool has_selection = file_is_any_selected(files);
772  const int active_old = params->active_file;
773  int active_new = -1;
774  int other_site = -1; /* file on the other site of active_old */
775 
776  /* *** get all needed files for handling selection *** */
777 
778  if (numfiles == 0) {
779  /* No files visible, nothing to do. */
780  return false;
781  }
782 
783  if (has_selection) {
784  FileLayout *layout = ED_fileselect_get_layout(sfile, region);
785  const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->flow_columns;
786 
787  if ((layout->flag & FILE_LAYOUT_HOR && direction == UI_SELECT_WALK_UP) ||
788  (layout->flag & FILE_LAYOUT_VER && direction == UI_SELECT_WALK_LEFT)) {
789  active_new = active_old - 1;
790  other_site = active_old + 1;
791  }
792  else if ((layout->flag & FILE_LAYOUT_HOR && direction == UI_SELECT_WALK_DOWN) ||
793  (layout->flag & FILE_LAYOUT_VER && direction == UI_SELECT_WALK_RIGHT)) {
794  active_new = active_old + 1;
795  other_site = active_old - 1;
796  }
797  else if ((layout->flag & FILE_LAYOUT_HOR && direction == UI_SELECT_WALK_LEFT) ||
798  (layout->flag & FILE_LAYOUT_VER && direction == UI_SELECT_WALK_UP)) {
799  active_new = active_old - idx_shift;
800  other_site = active_old + idx_shift;
801  }
802  else if ((layout->flag & FILE_LAYOUT_HOR && direction == UI_SELECT_WALK_RIGHT) ||
803  (layout->flag & FILE_LAYOUT_VER && direction == UI_SELECT_WALK_DOWN)) {
804 
805  active_new = active_old + idx_shift;
806  other_site = active_old - idx_shift;
807  }
808  else {
809  BLI_assert(0);
810  }
811 
812  if (!IN_RANGE(active_new, -1, numfiles)) {
813  if (extend) {
814  /* extend to invalid file -> abort */
815  return false;
816  }
817  /* if we don't extend, selecting '..' (index == 0) is allowed so
818  * using key selection to go to parent directory is possible */
819  if (active_new != 0) {
820  /* select initial file */
821  active_new = active_old;
822  }
823  }
824  if (!IN_RANGE(other_site, 0, numfiles)) {
825  other_site = -1;
826  }
827  }
828 
830  region,
831  sfile,
832  direction,
833  numfiles,
834  active_old,
835  active_new,
836  other_site,
837  has_selection,
838  extend,
839  fill);
840 }
841 
842 static int file_walk_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
843 {
844  SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
846  const int direction = RNA_enum_get(op->ptr, "direction");
847  const bool extend = RNA_boolean_get(op->ptr, "extend");
848  const bool fill = RNA_boolean_get(op->ptr, "fill");
849 
850  if (file_walk_select_do(C, sfile, params, direction, extend, fill)) {
852  return OPERATOR_FINISHED;
853  }
854 
855  return OPERATOR_CANCELLED;
856 }
857 
859 {
860  PropertyRNA *prop;
861 
862  /* identifiers */
863  ot->name = "Walk Select/Deselect File";
864  ot->description = "Select/Deselect files by walking through them";
865  ot->idname = "FILE_OT_select_walk";
866 
867  /* api callbacks */
870 
871  /* properties */
873  prop = RNA_def_boolean(ot->srna,
874  "extend",
875  false,
876  "Extend",
877  "Extend selection instead of deselecting everything first");
879  prop = RNA_def_boolean(
880  ot->srna, "fill", false, "Fill", "Select everything beginning with the last selection");
882 }
883 
886 /* -------------------------------------------------------------------- */
891 {
893  SpaceFile *sfile = CTX_wm_space_file(C);
895  FileSelection sel;
896  const int numfiles = filelist_files_ensure(sfile->files);
897  int action = RNA_enum_get(op->ptr, "action");
898 
899  if (action == SEL_TOGGLE) {
900  action = file_is_any_selected(sfile->files) ? SEL_DESELECT : SEL_SELECT;
901  }
902 
903  sel.first = 0;
904  sel.last = numfiles - 1;
905 
906  FileCheckType check_type;
907  FileSelType filesel_type;
908 
909  switch (action) {
910  case SEL_SELECT:
911  case SEL_INVERT: {
912  check_type = (params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_FILES;
913  filesel_type = (action == SEL_INVERT) ? FILE_SEL_TOGGLE : FILE_SEL_ADD;
914  break;
915  }
916  case SEL_DESELECT: {
917  check_type = CHECK_ALL;
918  filesel_type = FILE_SEL_REMOVE;
919  break;
920  }
921  default: {
922  BLI_assert(0);
923  return OPERATOR_CANCELLED;
924  }
925  }
926 
928  sfile->files, &sel, filesel_type, FILE_SEL_SELECTED, check_type);
929 
930  params->active_file = -1;
931  if (action != SEL_DESELECT) {
932  for (int i = 0; i < numfiles; i++) {
933  if (filelist_entry_select_index_get(sfile->files, i, check_type)) {
934  params->active_file = i;
935  break;
936  }
937  }
938  }
939 
943 
944  return OPERATOR_FINISHED;
945 }
946 
948 {
949  /* identifiers */
950  ot->name = "(De)select All Files";
951  ot->description = "Select or deselect all files";
952  ot->idname = "FILE_OT_select_all";
953 
954  /* api callbacks */
957 
958  /* properties */
960 }
961 
964 /* -------------------------------------------------------------------- */
969 {
970  SpaceFile *sfile = CTX_wm_space_file(C);
973 
974  if (sel.first == -1 && sel.last == -1 && params->active_file == -1) {
975  /* Nothing was selected. */
976  return OPERATOR_CANCELLED;
977  }
978 
979  /* Extend the selection area with the active file, as it may not be selected but still is
980  * important to have in view. */
981  if (sel.first == -1 || params->active_file < sel.first) {
982  sel.first = params->active_file;
983  }
984  if (sel.last == -1 || params->active_file > sel.last) {
985  sel.last = params->active_file;
986  }
987 
989  ARegion *region = CTX_wm_region(C);
990  file_ensure_selection_inside_viewbounds(region, sfile, &sel);
991 
995 
996  return OPERATOR_FINISHED;
997 }
998 
1000 {
1001  /* identifiers */
1002  ot->name = "Frame Selected";
1003  ot->description = "Scroll the selected files into view";
1004  ot->idname = "FILE_OT_view_selected";
1005 
1006  /* api callbacks */
1009 }
1010 
1013 /* -------------------------------------------------------------------- */
1017 /* Note we could get rid of this one, but it's used by some addon so...
1018  * Does not hurt keeping it around for now. */
1019 /* TODO disallow bookmark editing in assets mode? */
1021 {
1022  Main *bmain = CTX_data_main(C);
1023  SpaceFile *sfile = CTX_wm_space_file(C);
1024  PropertyRNA *prop;
1025 
1026  if ((prop = RNA_struct_find_property(op->ptr, "dir"))) {
1028  char entry[256];
1029 
1030  RNA_property_string_get(op->ptr, prop, entry);
1031  BLI_strncpy(params->dir, entry, sizeof(params->dir));
1034 
1036  }
1037 
1038  return OPERATOR_FINISHED;
1039 }
1040 
1042 {
1043  PropertyRNA *prop;
1044 
1045  /* identifiers */
1046  ot->name = "Select Directory";
1047  ot->description = "Select a bookmarked directory";
1048  ot->idname = "FILE_OT_select_bookmark";
1049 
1050  /* api callbacks */
1053 
1054  /* properties */
1055  prop = RNA_def_string(ot->srna, "dir", NULL, FILE_MAXDIR, "Directory", "");
1057 }
1058 
1061 /* -------------------------------------------------------------------- */
1066 {
1067  ScrArea *area = CTX_wm_area(C);
1068  SpaceFile *sfile = CTX_wm_space_file(C);
1069  struct FSMenu *fsmenu = ED_fsmenu_get();
1071 
1072  if (params->dir[0] != '\0') {
1073  char name[FILE_MAX];
1074 
1076  fsmenu, FS_CATEGORY_BOOKMARKS, params->dir, NULL, ICON_FILE_FOLDER, FS_INSERT_SAVE);
1077  BLI_join_dirfile(name,
1078  sizeof(name),
1081  fsmenu_write_file(fsmenu, name);
1082  }
1083 
1086  return OPERATOR_FINISHED;
1087 }
1088 
1090 {
1091  /* identifiers */
1092  ot->name = "Add Bookmark";
1093  ot->description = "Add a bookmark for the selected/active directory";
1094  ot->idname = "FILE_OT_bookmark_add";
1095 
1096  /* api callbacks */
1099 }
1100 
1103 /* -------------------------------------------------------------------- */
1108 {
1109  ScrArea *area = CTX_wm_area(C);
1110  SpaceFile *sfile = CTX_wm_space_file(C);
1111  struct FSMenu *fsmenu = ED_fsmenu_get();
1112  int nentries = ED_fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS);
1113 
1114  PropertyRNA *prop = RNA_struct_find_property(op->ptr, "index");
1115 
1116  if (prop) {
1117  int index;
1118  if (RNA_property_is_set(op->ptr, prop)) {
1119  index = RNA_property_int_get(op->ptr, prop);
1120  }
1121  else { /* if index unset, use active bookmark... */
1122  index = sfile->bookmarknr;
1123  }
1124  if ((index > -1) && (index < nentries)) {
1125  char name[FILE_MAX];
1126 
1128  BLI_join_dirfile(name,
1129  sizeof(name),
1132  fsmenu_write_file(fsmenu, name);
1135  }
1136  }
1137 
1138  return OPERATOR_FINISHED;
1139 }
1140 
1142 {
1143  PropertyRNA *prop;
1144 
1145  /* identifiers */
1146  ot->name = "Delete Bookmark";
1147  ot->description = "Delete selected bookmark";
1148  ot->idname = "FILE_OT_bookmark_delete";
1149 
1150  /* api callbacks */
1153 
1154  /* properties */
1155  prop = RNA_def_int(ot->srna, "index", -1, -1, 20000, "Index", "", -1, 20000);
1157 }
1158 
1161 /* -------------------------------------------------------------------- */
1166 {
1167  ScrArea *area = CTX_wm_area(C);
1168  struct FSMenu *fsmenu = ED_fsmenu_get();
1169  struct FSMenuEntry *fsme_next, *fsme = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS);
1170  int index;
1171  bool changed = false;
1172 
1173  for (index = 0; fsme; fsme = fsme_next) {
1174  fsme_next = fsme->next;
1175 
1176  if (!BLI_is_dir(fsme->path)) {
1178  changed = true;
1179  }
1180  else {
1181  index++;
1182  }
1183  }
1184 
1185  if (changed) {
1186  char name[FILE_MAX];
1187 
1189  sizeof(name),
1192  fsmenu_write_file(fsmenu, name);
1196  }
1197 
1198  return OPERATOR_FINISHED;
1199 }
1200 
1202 {
1203  /* identifiers */
1204  ot->name = "Cleanup Bookmarks";
1205  ot->description = "Delete all invalid bookmarks";
1206  ot->idname = "FILE_OT_bookmark_cleanup";
1207 
1208  /* api callbacks */
1211 
1212  /* properties */
1213 }
1214 
1217 /* -------------------------------------------------------------------- */
1221 enum {
1226 };
1227 
1229 {
1230  ScrArea *area = CTX_wm_area(C);
1231  SpaceFile *sfile = CTX_wm_space_file(C);
1232  struct FSMenu *fsmenu = ED_fsmenu_get();
1233  struct FSMenuEntry *fsmentry = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS);
1234  const struct FSMenuEntry *fsmentry_org = fsmentry;
1235 
1236  char fname[FILE_MAX];
1237 
1238  const int direction = RNA_enum_get(op->ptr, "direction");
1239  const int totitems = ED_fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS);
1240  const int act_index = sfile->bookmarknr;
1241  int new_index;
1242 
1243  if (totitems < 2) {
1244  return OPERATOR_CANCELLED;
1245  }
1246 
1247  switch (direction) {
1249  new_index = 0;
1250  break;
1252  new_index = totitems - 1;
1253  break;
1254  case FILE_BOOKMARK_MOVE_UP:
1256  default:
1257  new_index = (totitems + act_index + direction) % totitems;
1258  break;
1259  }
1260 
1261  if (new_index == act_index) {
1262  return OPERATOR_CANCELLED;
1263  }
1264 
1265  BLI_linklist_move_item((LinkNode **)&fsmentry, act_index, new_index);
1266  if (fsmentry != fsmentry_org) {
1267  ED_fsmenu_set_category(fsmenu, FS_CATEGORY_BOOKMARKS, fsmentry);
1268  }
1269 
1270  /* Need to update active bookmark number. */
1271  sfile->bookmarknr = new_index;
1272 
1273  BLI_join_dirfile(fname,
1274  sizeof(fname),
1277  fsmenu_write_file(fsmenu, fname);
1278 
1280  return OPERATOR_FINISHED;
1281 }
1282 
1284 {
1285  SpaceFile *sfile = CTX_wm_space_file(C);
1286 
1287  return sfile->bookmarknr != -1;
1288 }
1289 
1291 {
1292  static const EnumPropertyItem slot_move[] = {
1293  {FILE_BOOKMARK_MOVE_TOP, "TOP", 0, "Top", "Top of the list"},
1294  {FILE_BOOKMARK_MOVE_UP, "UP", 0, "Up", ""},
1295  {FILE_BOOKMARK_MOVE_DOWN, "DOWN", 0, "Down", ""},
1296  {FILE_BOOKMARK_MOVE_BOTTOM, "BOTTOM", 0, "Bottom", "Bottom of the list"},
1297  {0, NULL, 0, NULL, NULL}};
1298 
1299  /* identifiers */
1300  ot->name = "Move Bookmark";
1301  ot->idname = "FILE_OT_bookmark_move";
1302  ot->description = "Move the active bookmark up/down in the list";
1303 
1304  /* api callbacks */
1308 
1309  /* flags */
1310  ot->flag = OPTYPE_REGISTER; /* No undo! */
1311 
1312  RNA_def_enum(ot->srna,
1313  "direction",
1314  slot_move,
1315  0,
1316  "Direction",
1317  "Direction to move the active bookmark towards");
1318 }
1319 
1322 /* -------------------------------------------------------------------- */
1327 {
1328  ScrArea *area = CTX_wm_area(C);
1329  char name[FILE_MAX];
1330  struct FSMenu *fsmenu = ED_fsmenu_get();
1331 
1332  while (ED_fsmenu_get_entry(fsmenu, FS_CATEGORY_RECENT, 0) != NULL) {
1334  }
1335  BLI_join_dirfile(name,
1336  sizeof(name),
1339  fsmenu_write_file(fsmenu, name);
1341 
1342  return OPERATOR_FINISHED;
1343 }
1344 
1346 {
1347  /* identifiers */
1348  ot->name = "Reset Recent";
1349  ot->description = "Reset recent files";
1350  ot->idname = "FILE_OT_reset_recent";
1351 
1352  /* api callbacks */
1355 }
1356 
1359 /* -------------------------------------------------------------------- */
1363 int file_highlight_set(SpaceFile *sfile, ARegion *region, int mx, int my)
1364 {
1365  View2D *v2d = &region->v2d;
1367  int numfiles, origfile;
1368 
1369  if (sfile == NULL || sfile->files == NULL) {
1370  return 0;
1371  }
1372 
1373  numfiles = filelist_files_ensure(sfile->files);
1375 
1376  origfile = params->highlight_file;
1377 
1378  mx -= region->winrct.xmin;
1379  my -= region->winrct.ymin;
1380 
1381  if (ED_fileselect_layout_is_inside_pt(sfile->layout, v2d, mx, my)) {
1382  float fx, fy;
1383  int highlight_file;
1384 
1385  UI_view2d_region_to_view(v2d, mx, my, &fx, &fy);
1386 
1387  highlight_file = ED_fileselect_layout_offset(
1388  sfile->layout, (int)(v2d->tot.xmin + fx), (int)(v2d->tot.ymax - fy));
1389 
1390  if ((highlight_file >= 0) && (highlight_file < numfiles)) {
1391  params->highlight_file = highlight_file;
1392  }
1393  else {
1394  params->highlight_file = -1;
1395  }
1396  }
1397  else {
1398  params->highlight_file = -1;
1399  }
1400 
1401  return (params->highlight_file != origfile);
1402 }
1403 
1404 static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1405 {
1406  ARegion *region = CTX_wm_region(C);
1407  SpaceFile *sfile = CTX_wm_space_file(C);
1408 
1409  if (!file_highlight_set(sfile, region, event->x, event->y)) {
1410  return OPERATOR_PASS_THROUGH;
1411  }
1412 
1414 
1415  return OPERATOR_PASS_THROUGH;
1416 }
1417 
1419 {
1420  /* identifiers */
1421  ot->name = "Highlight File";
1422  ot->description = "Highlight selected file(s)";
1423  ot->idname = "FILE_OT_highlight";
1424 
1425  /* api callbacks */
1428 }
1429 
1432 /* -------------------------------------------------------------------- */
1437  wmOperator *UNUSED(op),
1438  const wmEvent *event)
1439 {
1440  const ARegion *region = CTX_wm_region(C);
1441  SpaceFile *sfile = CTX_wm_space_file(C);
1442 
1444  &region->v2d, sfile->layout, event->mval[0], event->mval[1])) {
1447  &region->v2d, params, sfile->layout, event->mval[0]);
1448 
1449  if (column_type != COLUMN_NONE) {
1450  const FileAttributeColumn *column = &sfile->layout->attribute_columns[column_type];
1451 
1453  if (params->sort == column->sort_type) {
1454  /* Already sorting by selected column -> toggle sort invert (three state logic). */
1455  params->flag ^= FILE_SORT_INVERT;
1456  }
1457  else {
1458  params->sort = column->sort_type;
1459  params->flag &= ~FILE_SORT_INVERT;
1460  }
1461 
1463  }
1464  }
1465 
1466  return OPERATOR_PASS_THROUGH;
1467 }
1468 
1470 {
1471  /* identifiers */
1472  ot->name = "Sort from Column";
1473  ot->description = "Change sorting to use column under cursor";
1474  ot->idname = "FILE_OT_sort_column_ui_context";
1475 
1476  /* api callbacks */
1479 
1480  ot->flag = OPTYPE_INTERNAL;
1481 }
1482 
1485 /* -------------------------------------------------------------------- */
1490 {
1491  bool poll = ED_operator_file_active(C);
1492  SpaceFile *sfile = CTX_wm_space_file(C);
1493 
1494  if (!sfile || !sfile->op) {
1495  poll = 0;
1496  }
1497 
1498  return poll;
1499 }
1500 
1502 {
1504  SpaceFile *sfile = CTX_wm_space_file(C);
1505  wmOperator *op = sfile->op;
1506 
1507  sfile->op = NULL;
1508 
1510 
1511  return OPERATOR_FINISHED;
1512 }
1513 
1515 {
1516  /* identifiers */
1517  ot->name = "Cancel File Load";
1518  ot->description = "Cancel loading of selected file";
1519  ot->idname = "FILE_OT_cancel";
1520 
1521  /* api callbacks */
1524 }
1525 
1528 /* -------------------------------------------------------------------- */
1532 void file_sfile_to_operator_ex(Main *bmain, wmOperator *op, SpaceFile *sfile, char *filepath)
1533 {
1535  PropertyRNA *prop;
1536 
1537  /* XXX, not real length */
1538  BLI_join_dirfile(filepath, FILE_MAX, params->dir, params->file);
1539 
1540  if ((prop = RNA_struct_find_property(op->ptr, "relative_path"))) {
1541  if (RNA_property_boolean_get(op->ptr, prop)) {
1542  BLI_path_rel(filepath, BKE_main_blendfile_path(bmain));
1543  }
1544  }
1545 
1546  if ((prop = RNA_struct_find_property(op->ptr, "filename"))) {
1547  RNA_property_string_set(op->ptr, prop, params->file);
1548  }
1549  if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
1550  RNA_property_string_set(op->ptr, prop, params->dir);
1551  }
1552  if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
1553  RNA_property_string_set(op->ptr, prop, filepath);
1554  }
1555 
1556  /* some ops have multiple files to select */
1557  /* this is called on operators check() so clear collections first since
1558  * they may be already set. */
1559  {
1560  int i, numfiles = filelist_files_ensure(sfile->files);
1561 
1562  if ((prop = RNA_struct_find_property(op->ptr, "files"))) {
1563  PointerRNA itemptr;
1564  int num_files = 0;
1566  for (i = 0; i < numfiles; i++) {
1568  FileDirEntry *file = filelist_file(sfile->files, i);
1569  /* Cannot (currently) mix regular items and alias/shortcuts in multiple selection. */
1570  if (!file->redirection_path) {
1571  RNA_property_collection_add(op->ptr, prop, &itemptr);
1572  RNA_string_set(&itemptr, "name", file->relpath);
1573  num_files++;
1574  }
1575  }
1576  }
1577  /* make sure the file specified in the filename button is added even if no
1578  * files selected */
1579  if (0 == num_files) {
1580  RNA_property_collection_add(op->ptr, prop, &itemptr);
1581  RNA_string_set(&itemptr, "name", params->file);
1582  }
1583  }
1584 
1585  if ((prop = RNA_struct_find_property(op->ptr, "dirs"))) {
1586  PointerRNA itemptr;
1587  int num_dirs = 0;
1589  for (i = 0; i < numfiles; i++) {
1591  FileDirEntry *file = filelist_file(sfile->files, i);
1592  RNA_property_collection_add(op->ptr, prop, &itemptr);
1593  RNA_string_set(&itemptr, "name", file->relpath);
1594  num_dirs++;
1595  }
1596  }
1597 
1598  /* make sure the directory specified in the button is added even if no
1599  * directory selected */
1600  if (0 == num_dirs) {
1601  RNA_property_collection_add(op->ptr, prop, &itemptr);
1602  RNA_string_set(&itemptr, "name", params->dir);
1603  }
1604  }
1605  }
1606 }
1608 {
1609  char filepath_dummy[FILE_MAX];
1610 
1611  file_sfile_to_operator_ex(bmain, op, sfile, filepath_dummy);
1612 }
1613 
1615 {
1617  PropertyRNA *prop;
1618 
1619  /* If neither of the above are set, split the filepath back */
1620  if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
1621  char filepath[FILE_MAX];
1622  RNA_property_string_get(op->ptr, prop, filepath);
1624  filepath, params->dir, params->file, sizeof(params->dir), sizeof(params->file));
1625  }
1626  else {
1627  if ((prop = RNA_struct_find_property(op->ptr, "filename"))) {
1628  RNA_property_string_get(op->ptr, prop, params->file);
1629  }
1630  if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
1631  RNA_property_string_get(op->ptr, prop, params->dir);
1632  }
1633  }
1634 
1635  /* we could check for relative_path property which is used when converting
1636  * in the other direction but doesn't hurt to do this every time */
1638 
1639  /* XXX, files and dirs updates missing, not really so important though */
1640 }
1641 
1645 void file_sfile_filepath_set(SpaceFile *sfile, const char *filepath)
1646 {
1648  BLI_assert(BLI_exists(filepath));
1649 
1650  if (BLI_is_dir(filepath)) {
1651  BLI_strncpy(params->dir, filepath, sizeof(params->dir));
1652  }
1653  else {
1654  if ((params->flag & FILE_DIRSEL_ONLY) == 0) {
1656  filepath, params->dir, params->file, sizeof(params->dir), sizeof(params->file));
1657  }
1658  else {
1659  BLI_split_dir_part(filepath, params->dir, sizeof(params->dir));
1660  }
1661  }
1662 }
1663 
1665 {
1666  /* May happen when manipulating non-active spaces. */
1667  if (UNLIKELY(area->spacetype != SPACE_FILE)) {
1668  return;
1669  }
1670  SpaceFile *sfile = area->spacedata.first;
1671  wmOperator *op = sfile->op;
1672  if (op) { /* fail on reload */
1673  if (op->type->check) {
1674  Main *bmain = CTX_data_main(C);
1675  file_sfile_to_operator(bmain, op, sfile);
1676 
1677  /* redraw */
1678  if (op->type->check(C, op)) {
1679  file_operator_to_sfile(bmain, sfile, op);
1680 
1681  /* redraw, else the changed settings wont get updated */
1683  }
1684  }
1685  }
1686 }
1687 
1689 {
1690  ScrArea *area = CTX_wm_area(C);
1692 }
1693 
1694 /* for use with; UI_block_func_set */
1695 void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2))
1696 {
1697  file_draw_check(C);
1698 }
1699 
1701 {
1702  if (sfile->op) { /* fails on reload */
1704  if (params && (params->flag & FILE_CHECK_EXISTING)) {
1705  char filepath[FILE_MAX];
1706  BLI_join_dirfile(filepath, sizeof(filepath), params->dir, params->file);
1707  if (BLI_is_file(filepath)) {
1708  return true;
1709  }
1710  }
1711  }
1712 
1713  return false;
1714 }
1715 
1718 /* -------------------------------------------------------------------- */
1722 static int file_exec(bContext *C, wmOperator *exec_op)
1723 {
1724  Main *bmain = CTX_data_main(C);
1726  SpaceFile *sfile = CTX_wm_space_file(C);
1728  struct FileDirEntry *file = filelist_file(sfile->files, params->active_file);
1729  char filepath[FILE_MAX];
1730 
1731  if (file && file->redirection_path) {
1732  /* redirection_path is an absolute path that takes precedence
1733  * over using params->dir + params->file. */
1734  BLI_split_dirfile(file->redirection_path,
1735  params->dir,
1736  params->file,
1737  sizeof(params->dir),
1738  sizeof(params->file));
1739  /* Update relpath with redirected filename as well so that the alternative
1740  * combination of params->dir + relpath remains valid as well. */
1741  MEM_freeN(file->relpath);
1742  file->relpath = BLI_strdup(params->file);
1743  }
1744 
1745  /* directory change */
1746  if (file && (file->typeflag & FILE_TYPE_DIR)) {
1747  if (!file->relpath) {
1748  return OPERATOR_CANCELLED;
1749  }
1750 
1751  if (FILENAME_IS_PARENT(file->relpath)) {
1753  }
1754  else {
1756  BLI_path_append(params->dir, sizeof(params->dir) - 1, file->relpath);
1758  }
1760  }
1761  /* opening file - sends events now, so things get handled on windowqueue level */
1762  else if (sfile->op) {
1763  wmOperator *op = sfile->op;
1764 
1765  /* When used as a macro, for double-click, to prevent closing when double-clicking on item. */
1766  if (RNA_boolean_get(exec_op->ptr, "need_active")) {
1767  const int numfiles = filelist_files_ensure(sfile->files);
1768  int i, active = 0;
1769 
1770  for (i = 0; i < numfiles; i++) {
1772  active = 1;
1773  break;
1774  }
1775  }
1776  if (active == 0) {
1777  return OPERATOR_CANCELLED;
1778  }
1779  }
1780 
1781  sfile->op = NULL;
1782 
1783  file_sfile_to_operator_ex(bmain, op, sfile, filepath);
1784 
1785  if (BLI_exists(params->dir)) {
1788  params->dir,
1789  NULL,
1790  ICON_FILE_FOLDER,
1792  }
1793 
1794  BLI_join_dirfile(filepath,
1795  sizeof(filepath),
1798  fsmenu_write_file(ED_fsmenu_get(), filepath);
1800  }
1801 
1802  return OPERATOR_FINISHED;
1803 }
1804 
1805 static int file_exec_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1806 {
1807  ARegion *region = CTX_wm_region(C);
1808  SpaceFile *sfile = CTX_wm_space_file(C);
1809 
1811  sfile->layout, &region->v2d, event->mval[0], event->mval[1])) {
1813  }
1814 
1815  return file_exec(C, op);
1816 }
1817 
1819 {
1820  PropertyRNA *prop;
1821 
1822  /* identifiers */
1823  ot->name = "Execute File Window";
1824  ot->description = "Execute selected file";
1825  ot->idname = "FILE_OT_execute";
1826 
1827  /* api callbacks */
1829  ot->exec = file_exec;
1830  /* Important since handler is on window level.
1831  *
1832  * Avoid using #file_operator_poll since this is also used for entering directories
1833  * which is used even when the file manager doesn't have an operator. */
1835 
1836  /* properties */
1837  prop = RNA_def_boolean(ot->srna,
1838  "need_active",
1839  0,
1840  "Need Active",
1841  "Only execute if there's an active selected file in the file list");
1843 }
1844 
1847 /* -------------------------------------------------------------------- */
1852 {
1854  SpaceFile *sfile = CTX_wm_space_file(C);
1855  struct FSMenu *fsmenu = ED_fsmenu_get();
1856 
1857  ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
1858 
1859  /* refresh system directory menu */
1861 
1862  /* Update bookmarks 'valid' state. */
1863  fsmenu_refresh_bookmarks_status(wm, fsmenu);
1864 
1866 
1867  return OPERATOR_FINISHED;
1868 }
1869 
1871 {
1872  /* identifiers */
1873  ot->name = "Refresh File List";
1874  ot->description = "Refresh the file list";
1875  ot->idname = "FILE_OT_refresh";
1876 
1877  /* api callbacks */
1879  ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1880 }
1881 
1884 /* -------------------------------------------------------------------- */
1889 {
1890  Main *bmain = CTX_data_main(C);
1891  SpaceFile *sfile = CTX_wm_space_file(C);
1893 
1894  if (params) {
1895  if (BLI_path_parent_dir(params->dir)) {
1898  if (params->recursion_level > 1) {
1899  /* Disable 'dirtree' recursion when going up in tree. */
1900  params->recursion_level = 0;
1901  filelist_setrecursion(sfile->files, params->recursion_level);
1902  }
1904  }
1905  }
1906 
1907  return OPERATOR_FINISHED;
1908 }
1909 
1911 {
1912  /* identifiers */
1913  ot->name = "Parent File";
1914  ot->description = "Move to parent directory";
1915  ot->idname = "FILE_OT_parent";
1916 
1917  /* api callbacks */
1919  ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1920 }
1921 
1924 /* -------------------------------------------------------------------- */
1929 {
1930  SpaceFile *sfile = CTX_wm_space_file(C);
1932 
1933  if (params) {
1934  folderlist_pushdir(sfile->folders_next, params->dir);
1935  folderlist_popdir(sfile->folders_prev, params->dir);
1936  folderlist_pushdir(sfile->folders_next, params->dir);
1937 
1939  }
1941 
1942  return OPERATOR_FINISHED;
1943 }
1944 
1946 {
1947  /* identifiers */
1948  ot->name = "Previous Folder";
1949  ot->description = "Move to previous folder";
1950  ot->idname = "FILE_OT_previous";
1951 
1952  /* api callbacks */
1954  ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1955 }
1956 
1959 /* -------------------------------------------------------------------- */
1964 {
1965  SpaceFile *sfile = CTX_wm_space_file(C);
1967  if (params) {
1968  folderlist_pushdir(sfile->folders_prev, params->dir);
1969  folderlist_popdir(sfile->folders_next, params->dir);
1970 
1971  /* update folders_prev so we can check for it in #folderlist_clear_next() */
1972  folderlist_pushdir(sfile->folders_prev, params->dir);
1973 
1975  }
1977 
1978  return OPERATOR_FINISHED;
1979 }
1980 
1982 {
1983  /* identifiers */
1984  ot->name = "Next Folder";
1985  ot->description = "Move to next folder";
1986  ot->idname = "FILE_OT_next";
1987 
1988  /* api callbacks */
1989  ot->exec = file_next_exec;
1990  ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1991 }
1992 
1995 /* -------------------------------------------------------------------- */
1999 /* only meant for timer usage */
2001 {
2002  ScrArea *area = CTX_wm_area(C);
2003  SpaceFile *sfile = CTX_wm_space_file(C);
2004  ARegion *region, *region_ctx = CTX_wm_region(C);
2005  const bool is_horizontal = (sfile->layout->flag & FILE_LAYOUT_HOR) != 0;
2006  int i;
2007 
2008  /* escape if not our timer */
2009  if (sfile->smoothscroll_timer == NULL || sfile->smoothscroll_timer != event->customdata) {
2010  return OPERATOR_PASS_THROUGH;
2011  }
2012 
2013  const int numfiles = filelist_files_ensure(sfile->files);
2014 
2015  /* Due to async nature of file listing, we may execute this code before `file_refresh()`
2016  * editing entry is available in our listing,
2017  * so we also have to handle switching to rename mode here. */
2019  if ((params->rename_flag &
2022  }
2023 
2024  /* check if we are editing a name */
2025  int edit_idx = -1;
2026  for (i = 0; i < numfiles; i++) {
2029  edit_idx = i;
2030  break;
2031  }
2032  }
2033 
2034  /* if we are not editing, we are done */
2035  if (edit_idx == -1) {
2036  /* Do not invalidate timer if filerename is still pending,
2037  * we might still be building the filelist and yet have to find edited entry. */
2038  if (params->rename_flag == 0) {
2040  sfile->smoothscroll_timer = NULL;
2041  }
2042  return OPERATOR_PASS_THROUGH;
2043  }
2044 
2045  /* we need the correct area for scrolling */
2047  if (!region || region->regiontype != RGN_TYPE_WINDOW) {
2049  sfile->smoothscroll_timer = NULL;
2050  return OPERATOR_PASS_THROUGH;
2051  }
2052 
2053  /* Number of items in a block (i.e. lines in a column in horizontal layout, or columns in a line
2054  * in vertical layout).
2055  */
2056  const int items_block_size = is_horizontal ? sfile->layout->rows : sfile->layout->flow_columns;
2057 
2058  /* Scroll offset is the first file in the row/column we are editing in. */
2059  if (sfile->scroll_offset == 0) {
2060  sfile->scroll_offset = (edit_idx / items_block_size) * items_block_size;
2061  }
2062 
2063  const int numfiles_layout = ED_fileselect_layout_numfiles(sfile->layout, region);
2064  const int first_visible_item = ED_fileselect_layout_offset(
2065  sfile->layout, (int)region->v2d.cur.xmin, (int)-region->v2d.cur.ymax);
2066  const int last_visible_item = first_visible_item + numfiles_layout + 1;
2067 
2068  /* Note: the special case for vertical layout is because filename is at the bottom of items then,
2069  * so we artificially move current row back one step, to ensure we show bottom of
2070  * active item rather than its top (important in case visible height is low). */
2071  const int middle_offset = max_ii(
2072  0, (first_visible_item + last_visible_item) / 2 - (is_horizontal ? 0 : items_block_size));
2073 
2074  const int min_middle_offset = numfiles_layout / 2;
2075  const int max_middle_offset = ((numfiles / items_block_size) * items_block_size +
2076  ((numfiles % items_block_size) != 0 ? items_block_size : 0)) -
2077  (numfiles_layout / 2);
2078  /* Actual (physical) scrolling info, in pixels, used to detect whether we are fully at the
2079  * beginning/end of the view. */
2080  /* Note that there is a weird glitch, that sometimes tot rctf is smaller than cur rctf...
2081  * that is why we still need to keep the min/max_middle_offset checks too. :( */
2082  const float min_tot_scroll = is_horizontal ? region->v2d.tot.xmin : -region->v2d.tot.ymax;
2083  const float max_tot_scroll = is_horizontal ? region->v2d.tot.xmax : -region->v2d.tot.ymin;
2084  const float min_curr_scroll = is_horizontal ? region->v2d.cur.xmin : -region->v2d.cur.ymax;
2085  const float max_curr_scroll = is_horizontal ? region->v2d.cur.xmax : -region->v2d.cur.ymin;
2086 
2087  /* Check if we have reached our final scroll position. */
2088  /* Filelist has to be ready, otherwise it makes no sense to stop scrolling yet. */
2089  const bool is_ready = filelist_is_ready(sfile->files);
2090  /* Edited item must be in the 'middle' of shown area (kind of approximated).
2091  * Note that we have to do the check in 'block space', not in 'item space' here. */
2092  const bool is_centered = (abs(middle_offset / items_block_size -
2093  sfile->scroll_offset / items_block_size) == 0);
2094  /* OR edited item must be towards the beginning, and we are scrolled fully to the start. */
2095  const bool is_full_start = ((sfile->scroll_offset < min_middle_offset) &&
2096  (min_curr_scroll - min_tot_scroll < 1.0f) &&
2097  (middle_offset - min_middle_offset < items_block_size));
2098  /* OR edited item must be towards the end, and we are scrolled fully to the end.
2099  * This one is crucial (unlike the one for the beginning), because without it we won't scroll
2100  * fully to the end, and last column or row will end up only partially drawn. */
2101  const bool is_full_end = ((sfile->scroll_offset > max_middle_offset) &&
2102  (max_tot_scroll - max_curr_scroll < 1.0f) &&
2103  (max_middle_offset - middle_offset < items_block_size));
2104 
2105  if (is_ready && (is_centered || is_full_start || is_full_end)) {
2107  sfile->smoothscroll_timer = NULL;
2108  /* Post-scroll (after rename has been validated by user) is done,
2109  * rename process is totally finished, cleanup. */
2110  if ((params->rename_flag & FILE_PARAMS_RENAME_POSTSCROLL_ACTIVE) != 0) {
2111  params->renamefile[0] = '\0';
2112  params->rename_flag = 0;
2113  }
2114  return OPERATOR_FINISHED;
2115  }
2116 
2117  /* Temporarily set context to the main window region,
2118  * so that the pan operator works. */
2119  CTX_wm_region_set(C, region);
2120 
2121  /* scroll one step in the desired direction */
2122  PointerRNA op_ptr;
2123  int deltax = 0;
2124  int deltay = 0;
2125 
2126  /* We adjust speed of scrolling to avoid tens of seconds of it in e.g. directories with tens of
2127  * thousands of folders... See T65782. */
2128  /* This will slow down scrolling when approaching final goal, also avoids going too far and
2129  * having to bounce back... */
2130 
2131  /* Number of blocks (columns in horizontal layout, rows otherwise) between current middle of
2132  * screen, and final goal position. */
2133  const int diff_offset = sfile->scroll_offset / items_block_size -
2134  middle_offset / items_block_size;
2135  /* convert diff_offset into pixels. */
2136  const int diff_offset_delta = abs(diff_offset) *
2137  (is_horizontal ?
2138  sfile->layout->tile_w + 2 * sfile->layout->tile_border_x :
2139  sfile->layout->tile_h + 2 * sfile->layout->tile_border_y);
2140  const int scroll_delta = max_ii(2, diff_offset_delta / 15);
2141 
2142  if (diff_offset < 0) {
2143  if (is_horizontal) {
2144  deltax = -scroll_delta;
2145  }
2146  else {
2147  deltay = scroll_delta;
2148  }
2149  }
2150  else {
2151  if (is_horizontal) {
2152  deltax = scroll_delta;
2153  }
2154  else {
2155  deltay = -scroll_delta;
2156  }
2157  }
2158  WM_operator_properties_create(&op_ptr, "VIEW2D_OT_pan");
2159  RNA_int_set(&op_ptr, "deltax", deltax);
2160  RNA_int_set(&op_ptr, "deltay", deltay);
2161 
2162  WM_operator_name_call(C, "VIEW2D_OT_pan", WM_OP_EXEC_DEFAULT, &op_ptr);
2163  WM_operator_properties_free(&op_ptr);
2164 
2165  ED_region_tag_redraw(region);
2166 
2167  /* and restore context */
2168  CTX_wm_region_set(C, region_ctx);
2169 
2170  return OPERATOR_FINISHED;
2171 }
2172 
2174 {
2175  /* identifiers */
2176  ot->name = "Smooth Scroll";
2177  ot->idname = "FILE_OT_smoothscroll";
2178  ot->description = "Smooth scroll to make editable file visible";
2179 
2180  /* api callbacks */
2182 
2184 }
2185 
2188 /* -------------------------------------------------------------------- */
2193 {
2194  Main *bmain = CTX_data_main(C);
2195  SpaceFile *sfile = CTX_wm_space_file(C);
2196 
2197  if (sfile) {
2198  char filepath[FILE_MAX];
2199 
2200  RNA_string_get(op->ptr, "filepath", filepath);
2201  if (!BLI_exists(filepath)) {
2202  BKE_report(op->reports, RPT_ERROR, "File does not exist");
2203  return OPERATOR_CANCELLED;
2204  }
2205 
2206  file_sfile_filepath_set(sfile, filepath);
2207 
2208  if (sfile->op) {
2209  file_sfile_to_operator(bmain, sfile->op, sfile);
2210  file_draw_check(C);
2211  }
2212 
2214  return OPERATOR_FINISHED;
2215  }
2216 
2217  return OPERATOR_CANCELLED;
2218 }
2219 
2221 {
2222  ot->name = "File Selector Drop";
2223  ot->idname = "FILE_OT_filepath_drop";
2224 
2227 
2228  RNA_def_string_file_path(ot->srna, "filepath", "Path", FILE_MAX, "", "");
2229 }
2230 
2233 /* -------------------------------------------------------------------- */
2242 static int new_folder_path(const char *parent, char *folder, char *name)
2243 {
2244  int i = 1;
2245  int len = 0;
2246 
2247  BLI_strncpy(name, "New Folder", FILE_MAXFILE);
2248  BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
2249  /* check whether folder with the name already exists, in this case
2250  * add number to the name. Check length of generated name to avoid
2251  * crazy case of huge number of folders each named 'New Folder (x)' */
2252  while (BLI_exists(folder) && (len < FILE_MAXFILE)) {
2253  len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
2254  BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
2255  i++;
2256  }
2257 
2258  return (len < FILE_MAXFILE);
2259 }
2260 
2262 {
2263  char name[FILE_MAXFILE];
2264  char path[FILE_MAX];
2265  bool generate_name = true;
2266  PropertyRNA *prop;
2267 
2269  SpaceFile *sfile = CTX_wm_space_file(C);
2271  const bool do_diropen = RNA_boolean_get(op->ptr, "open");
2272 
2273  if (!params) {
2274  BKE_report(op->reports, RPT_WARNING, "No parent directory given");
2275  return OPERATOR_CANCELLED;
2276  }
2277 
2278  path[0] = '\0';
2279 
2280  if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
2281  RNA_property_string_get(op->ptr, prop, path);
2282  if (path[0] != '\0') {
2283  generate_name = false;
2284  }
2285  }
2286 
2287  if (generate_name) {
2288  /* create a new, non-existing folder name */
2289  if (!new_folder_path(params->dir, path, name)) {
2290  BKE_report(op->reports, RPT_ERROR, "Could not create new folder name");
2291  return OPERATOR_CANCELLED;
2292  }
2293  }
2294  else { /* We assume we are able to generate a valid name! */
2295  char org_path[FILE_MAX];
2296 
2297  BLI_strncpy(org_path, path, sizeof(org_path));
2298  if (BLI_path_make_safe(path)) {
2299  BKE_reportf(op->reports,
2300  RPT_WARNING,
2301  "'%s' given path is OS-invalid, creating '%s' path instead",
2302  org_path,
2303  path);
2304  }
2305  }
2306 
2307  /* create the file */
2308  errno = 0;
2309  if (!BLI_dir_create_recursive(path) ||
2310  /* Should no more be needed,
2311  * now that BLI_dir_create_recursive returns a success state - but kept just in case. */
2312  !BLI_exists(path)) {
2313  BKE_reportf(op->reports,
2314  RPT_ERROR,
2315  "Could not create new folder: %s",
2316  errno ? strerror(errno) : "unknown error");
2317  return OPERATOR_CANCELLED;
2318  }
2319 
2320  /* If we don't enter the directory directly, remember file to jump into editing. */
2321  if (do_diropen == false) {
2322  BLI_strncpy(params->renamefile, name, FILE_MAXFILE);
2323  params->rename_flag = FILE_PARAMS_RENAME_PENDING;
2324  }
2325 
2326  /* Set timer to smoothly view newly generated file. */
2327  if (sfile->smoothscroll_timer != NULL) {
2329  }
2330  sfile->smoothscroll_timer = WM_event_add_timer(wm, CTX_wm_window(C), TIMER1, 1.0 / 1000.0);
2331  sfile->scroll_offset = 0;
2332 
2333  /* reload dir to make sure we're seeing what's in the directory */
2334  ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
2335 
2336  if (do_diropen) {
2337  BLI_strncpy(params->dir, path, sizeof(params->dir));
2339  }
2340 
2342 
2343  return OPERATOR_FINISHED;
2344 }
2345 
2347 {
2348  PropertyRNA *prop;
2349 
2350  /* identifiers */
2351  ot->name = "Create New Directory";
2352  ot->description = "Create a new directory";
2353  ot->idname = "FILE_OT_directory_new";
2354 
2355  /* api callbacks */
2358  ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
2359 
2360  prop = RNA_def_string_dir_path(
2361  ot->srna, "directory", NULL, FILE_MAX, "Directory", "Name of new directory");
2363  prop = RNA_def_boolean(ot->srna, "open", false, "Open", "Open new directory");
2366 }
2367 
2370 /* -------------------------------------------------------------------- */
2374 /* TODO This should go to BLI_path_utils. */
2376 {
2377  Main *bmain = CTX_data_main(C);
2378  SpaceFile *sfile = CTX_wm_space_file(C);
2380 
2381  if (params) {
2382  if (BLI_path_is_rel(params->dir)) {
2383  /* Use of 'default' folder here is just to avoid an error message on '//' prefix. */
2384  BLI_path_abs(params->dir,
2385  G.relbase_valid ? BKE_main_blendfile_path(bmain) : BKE_appdir_folder_default());
2386  }
2387  else if (params->dir[0] == '~') {
2388  char tmpstr[sizeof(params->dir) - 1];
2389  BLI_strncpy(tmpstr, params->dir + 1, sizeof(tmpstr));
2390  BLI_join_dirfile(params->dir, sizeof(params->dir), BKE_appdir_folder_default(), tmpstr);
2391  }
2392 
2393  else if (params->dir[0] == '\0')
2394 #ifndef WIN32
2395  {
2396  params->dir[0] = '/';
2397  params->dir[1] = '\0';
2398  }
2399 #else
2400  {
2402  }
2403  /* change "C:" --> "C:\", T28102. */
2404  else if ((isalpha(params->dir[0]) && (params->dir[1] == ':')) && (params->dir[2] == '\0')) {
2405  params->dir[2] = '\\';
2406  params->dir[3] = '\0';
2407  }
2408  else if (BLI_path_is_unc(params->dir)) {
2409  BLI_path_normalize_unc(params->dir, FILE_MAX_LIBEXTRA);
2410  }
2411 #endif
2412  }
2413 }
2414 
2415 /* TODO check we still need this, it's annoying to have OS-specific code here... :/ */
2416 #if defined(WIN32)
2417 static bool can_create_dir(const char *dir)
2418 {
2419  /* for UNC paths we need to check whether the parent of the new
2420  * directory is a proper directory itself and not a share or the
2421  * UNC root (server name) itself. Calling BLI_is_dir does this
2422  */
2423  if (BLI_path_is_unc(dir)) {
2424  char parent[PATH_MAX];
2425  BLI_strncpy(parent, dir, PATH_MAX);
2426  BLI_path_parent_dir(parent);
2427  return BLI_is_dir(parent);
2428  }
2429  return true;
2430 }
2431 #endif
2432 
2433 void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UNUSED(arg_but))
2434 {
2435  Main *bmain = CTX_data_main(C);
2436  SpaceFile *sfile = CTX_wm_space_file(C);
2438 
2439  if (params) {
2440  char old_dir[sizeof(params->dir)];
2441 
2442  BLI_strncpy(old_dir, params->dir, sizeof(old_dir));
2443 
2445 
2446  /* special case, user may have pasted a filepath into the directory */
2447  if (!filelist_is_dir(sfile->files, params->dir)) {
2448  char tdir[FILE_MAX_LIBEXTRA];
2449  char *group, *name;
2450 
2451  if (BLI_is_file(params->dir)) {
2452  char path[sizeof(params->dir)];
2453  BLI_strncpy(path, params->dir, sizeof(path));
2455  path, params->dir, params->file, sizeof(params->dir), sizeof(params->file));
2456  }
2457  else if (BLO_library_path_explode(params->dir, tdir, &group, &name)) {
2458  if (group) {
2459  BLI_path_append(tdir, sizeof(tdir), group);
2460  }
2461  BLI_strncpy(params->dir, tdir, sizeof(params->dir));
2462  if (name) {
2463  BLI_strncpy(params->file, name, sizeof(params->file));
2464  }
2465  else {
2466  params->file[0] = '\0';
2467  }
2468  }
2469  }
2470 
2472 
2473  if (filelist_is_dir(sfile->files, params->dir)) {
2474  if (!STREQ(params->dir, old_dir)) { /* Avoids flickering when nothing's changed. */
2475  /* if directory exists, enter it immediately */
2477  }
2478 
2479  /* don't do for now because it selects entire text instead of
2480  * placing cursor at the end */
2481  /* UI_textbutton_activate_but(C, but); */
2482  }
2483 #if defined(WIN32)
2484  else if (!can_create_dir(params->dir)) {
2485  const char *lastdir = folderlist_peeklastdir(sfile->folders_prev);
2486  if (lastdir) {
2487  BLI_strncpy(params->dir, lastdir, sizeof(params->dir));
2488  }
2489  }
2490 #endif
2491  else {
2492  const char *lastdir = folderlist_peeklastdir(sfile->folders_prev);
2493  char tdir[FILE_MAX_LIBEXTRA];
2494 
2495  /* If we are 'inside' a blend library, we cannot do anything... */
2496  if (lastdir && BLO_library_path_explode(lastdir, tdir, NULL, NULL)) {
2497  BLI_strncpy(params->dir, lastdir, sizeof(params->dir));
2498  }
2499  else {
2500  /* if not, ask to create it and enter if confirmed */
2501  wmOperatorType *ot = WM_operatortype_find("FILE_OT_directory_new", false);
2502  PointerRNA ptr;
2504  RNA_string_set(&ptr, "directory", params->dir);
2505  RNA_boolean_set(&ptr, "open", true);
2506  /* Enable confirmation prompt, else it's too easy
2507  * to accidentally create new directories. */
2508  RNA_boolean_set(&ptr, "confirm", true);
2509 
2510  if (lastdir) {
2511  BLI_strncpy(params->dir, lastdir, sizeof(params->dir));
2512  }
2513 
2516  }
2517  }
2518 
2520  }
2521 }
2522 
2523 void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg_but)
2524 {
2525  Main *bmain = CTX_data_main(C);
2526  SpaceFile *sfile = CTX_wm_space_file(C);
2528  uiBut *but = arg_but;
2529  char matched_file[FILE_MAX];
2530 
2531  if (params) {
2532  char filepath[sizeof(params->dir)];
2533  int matches;
2534  matched_file[0] = '\0';
2535  filepath[0] = '\0';
2536 
2538 
2539  matches = file_select_match(sfile, params->file, matched_file);
2540 
2541  /* *After* file_select_match! */
2543 
2544  if (matches) {
2545  /* replace the pattern (or filename that the user typed in,
2546  * with the first selected file of the match */
2547  BLI_strncpy(params->file, matched_file, sizeof(params->file));
2548 
2550  }
2551 
2552  if (matches == 1) {
2553  BLI_join_dirfile(filepath, sizeof(params->dir), params->dir, params->file);
2554 
2555  /* if directory, open it and empty filename field */
2556  if (filelist_is_dir(sfile->files, filepath)) {
2558  BLI_strncpy(params->dir, filepath, sizeof(params->dir));
2559  params->file[0] = '\0';
2563  }
2564  }
2565  else if (matches > 1) {
2566  file_draw_check(C);
2567  }
2568  }
2569 }
2570 
2573 /* -------------------------------------------------------------------- */
2578 {
2580  SpaceFile *sfile = CTX_wm_space_file(C);
2582 
2583  if (params) {
2584  params->flag ^= FILE_HIDE_DOT;
2585  ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
2587  }
2588 
2589  return OPERATOR_FINISHED;
2590 }
2591 
2593 {
2594  /* identifiers */
2595  ot->name = "Toggle Hide Dot Files";
2596  ot->description = "Toggle hide hidden dot files";
2597  ot->idname = "FILE_OT_hidedot";
2598 
2599  /* api callbacks */
2601  ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
2602 }
2603 
2606 /* -------------------------------------------------------------------- */
2611 {
2612  SpaceFile *sfile = CTX_wm_space_file(C);
2613 
2614  if (!ED_operator_file_active(C)) {
2615  return false;
2616  }
2617 
2619  return params && (params->flag & FILE_CHECK_EXISTING);
2620 }
2621 
2625 static void filenum_newname(char *name, size_t name_size, int add)
2626 {
2627  char head[FILE_MAXFILE], tail[FILE_MAXFILE];
2628  char name_temp[FILE_MAXFILE];
2629  int pic;
2630  ushort digits;
2631 
2632  pic = BLI_path_sequence_decode(name, head, tail, &digits);
2633 
2634  /* are we going from 100 -> 99 or from 10 -> 9 */
2635  if (add < 0 && digits > 0) {
2636  int i, exp;
2637  exp = 1;
2638  for (i = digits; i > 1; i--) {
2639  exp *= 10;
2640  }
2641  if (pic >= exp && (pic + add) < exp) {
2642  digits--;
2643  }
2644  }
2645 
2646  pic += add;
2647  if (pic < 0) {
2648  pic = 0;
2649  }
2650  BLI_path_sequence_encode(name_temp, head, tail, digits, pic);
2651  BLI_strncpy(name, name_temp, name_size);
2652 }
2653 
2655 {
2656  SpaceFile *sfile = CTX_wm_space_file(C);
2658  ScrArea *area = CTX_wm_area(C);
2659 
2660  int inc = RNA_int_get(op->ptr, "increment");
2661  if (params && (inc != 0)) {
2662  filenum_newname(params->file, sizeof(params->file), inc);
2664  file_draw_check(C);
2665  // WM_event_add_notifier(C, NC_WINDOW, NULL);
2666  }
2667 
2668  return OPERATOR_FINISHED;
2669 }
2670 
2672 {
2673  /* identifiers */
2674  ot->name = "Increment Number in Filename";
2675  ot->description = "Increment number in filename";
2676  ot->idname = "FILE_OT_filenum";
2677 
2678  /* api callbacks */
2681 
2682  /* props */
2683  RNA_def_int(ot->srna, "increment", 1, -100, 100, "Increment", "", -100, 100);
2684 }
2685 
2688 /* -------------------------------------------------------------------- */
2692 static void file_rename_state_activate(SpaceFile *sfile, int file_idx, bool require_selected)
2693 {
2694  const int numfiles = filelist_files_ensure(sfile->files);
2695 
2696  if ((file_idx >= 0) && (file_idx < numfiles)) {
2697  FileDirEntry *file = filelist_file(sfile->files, file_idx);
2698 
2699  if ((require_selected == false) ||
2702 
2704  sfile->files, file_idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
2705  BLI_strncpy(params->renamefile, file->relpath, FILE_MAXFILE);
2706  /* We can skip the pending state,
2707  * as we can directly set FILE_SEL_EDITING on the expected entry here. */
2708  params->rename_flag = FILE_PARAMS_RENAME_ACTIVE;
2709  }
2710  }
2711 }
2712 
2713 static int file_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
2714 {
2715  ScrArea *area = CTX_wm_area(C);
2716  SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
2718 
2719  if (params) {
2720  file_rename_state_activate(sfile, params->active_file, true);
2722  }
2723 
2724  return OPERATOR_FINISHED;
2725 }
2726 
2728 {
2729  ScrArea *area = CTX_wm_area(C);
2730  SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
2732 
2733  if (params) {
2734  file_rename_state_activate(sfile, params->highlight_file, false);
2736  }
2737 
2738  return OPERATOR_FINISHED;
2739 }
2740 
2742 {
2743  /* identifiers */
2744  ot->name = "Rename File or Directory";
2745  ot->description = "Rename file or file directory";
2746  ot->idname = "FILE_OT_rename";
2747 
2748  /* api callbacks */
2752 }
2753 
2756 /* -------------------------------------------------------------------- */
2761 {
2762  bool poll = ED_operator_file_active(C);
2763  SpaceFile *sfile = CTX_wm_space_file(C);
2765 
2766  if (sfile && params) {
2767  char dir[FILE_MAX_LIBEXTRA];
2768  int numfiles = filelist_files_ensure(sfile->files);
2769  int i;
2770  int num_selected = 0;
2771 
2772  if (filelist_islibrary(sfile->files, dir, NULL)) {
2773  poll = 0;
2774  }
2775  for (i = 0; i < numfiles; i++) {
2777  num_selected++;
2778  }
2779  }
2780  if (num_selected <= 0) {
2781  poll = 0;
2782  }
2783  }
2784  else {
2785  poll = 0;
2786  }
2787 
2788  return poll;
2789 }
2790 
2792  FileDirEntry *file,
2793  const char **r_error_message)
2794 {
2795  if (file->typeflag & FILE_TYPE_ASSET) {
2796  ID *id = filelist_file_get_id(file);
2797  if (!id) {
2798  *r_error_message = "File is not a local data-block asset.";
2799  return false;
2800  }
2801  ED_asset_clear_id(id);
2802  }
2803  else {
2804  char str[FILE_MAX];
2805  BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
2806  if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) {
2807  return false;
2808  }
2809  }
2810 
2811  return true;
2812 }
2813 
2815 {
2817  SpaceFile *sfile = CTX_wm_space_file(C);
2819  int numfiles = filelist_files_ensure(sfile->files);
2820 
2821  const char *error_message = NULL;
2822  bool report_error = false;
2823  errno = 0;
2824  for (int i = 0; i < numfiles; i++) {
2826  FileDirEntry *file = filelist_file(sfile->files, i);
2827  if (!file_delete_single(params, file, &error_message)) {
2828  report_error = true;
2829  }
2830  }
2831  }
2832 
2833  if (report_error) {
2834  if (error_message != NULL) {
2835  BKE_reportf(op->reports, RPT_ERROR, "Could not delete file or directory: %s", error_message);
2836  }
2837  else {
2838  BKE_reportf(op->reports,
2839  RPT_ERROR,
2840  "Could not delete file or directory: %s",
2841  errno ? strerror(errno) : "unknown error");
2842  }
2843  }
2844 
2845  ED_fileselect_clear(wm, CTX_data_scene(C), sfile);
2847 
2848  return OPERATOR_FINISHED;
2849 }
2850 
2852 {
2853  /* identifiers */
2854  ot->name = "Delete Selected Files";
2855  ot->description = "Move selected files to the trash or recycle bin";
2856  ot->idname = "FILE_OT_delete";
2857 
2858  /* api callbacks */
2861  ot->poll = file_delete_poll; /* <- important, handler is on window level */
2862 }
2863 
2866 /* -------------------------------------------------------------------- */
2871 {
2872  ScrArea *area = CTX_wm_area(C);
2873  SpaceFile *sfile = CTX_wm_space_file(C);
2875 
2876  ARegion *region_ctx = CTX_wm_region(C);
2877 
2878  if (area) {
2879  LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
2880  CTX_wm_region_set(C, region);
2881  if (UI_textbutton_activate_rna(C, region, params, "filter_search")) {
2882  break;
2883  }
2884  }
2885  }
2886 
2887  CTX_wm_region_set(C, region_ctx);
2888 
2889  return OPERATOR_FINISHED;
2890 }
2891 
2893 {
2894  /* identifiers */
2895  ot->name = "Filter";
2896  ot->description = "Start entering filter text";
2897  ot->idname = "FILE_OT_start_filter";
2898 
2899  /* api callbacks */
2902 }
2903 
2906 /* -------------------------------------------------------------------- */
2911 {
2912  // wmOperatorType *ot;
2913  // wmOperatorTypeMacro *otmacro;
2914 
2915  /* future macros */
2916 }
2917 
@ BLENDER_USER_CONFIG
Definition: BKE_appdir.h:81
const char * BKE_appdir_folder_id_create(const int folder_id, const char *subfolder)
Definition: appdir.c:735
const char * BKE_appdir_folder_default(void)
Definition: appdir.c:157
#define BLENDER_BOOKMARK_FILE
Definition: BKE_appdir.h:102
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:714
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1034
void CTX_wm_region_set(bContext *C, struct ARegion *region)
Definition: context.c:985
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:689
struct SpaceLink * CTX_wm_space_data(const bContext *C)
Definition: context.c:719
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:725
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1018
struct SpaceFile * CTX_wm_space_file(const bContext *C)
Definition: context.c:818
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:699
const char * BKE_main_blendfile_path(const struct Main *bmain) ATTR_NONNULL()
void BKE_report(ReportList *reports, ReportType type, const char *message)
Definition: report.c:104
void BKE_reportf(ReportList *reports, ReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
struct ARegion * BKE_area_find_region_type(const struct ScrArea *area, int type)
#define BLI_assert(a)
Definition: BLI_assert.h:58
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:349
bool BLI_is_file(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:444
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:436
bool BLI_dir_create_recursive(const char *dir) ATTR_NONNULL()
Definition: fileops.c:1329
int BLI_delete_soft(const char *file, const char **error_message) ATTR_NONNULL()
Definition: fileops.c:1056
#define PATH_MAX
Definition: BLI_fileops.h:44
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
MINLINE int max_ii(int a, int b)
bool BLI_path_is_unc(const char *path)
Definition: path_util.c:417
bool BLI_path_make_safe(char *path) ATTR_NONNULL(1)
Definition: path_util.c:374
bool BLI_path_is_rel(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:411
void BLI_path_normalize_dir(const char *relabase, char *dir) ATTR_NONNULL(2)
Definition: path_util.c:266
void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
Definition: path_util.c:1682
#define FILE_MAXFILE
#define FILE_MAX
void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file) ATTR_NONNULL()
Definition: path_util.c:1716
void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2)
Definition: path_util.c:173
int BLI_path_sequence_decode(const char *string, char *head, char *tail, unsigned short *r_num_len)
Definition: path_util.c:83
void BLI_join_dirfile(char *__restrict dst, const size_t maxlen, const char *__restrict dir, const char *__restrict file) ATTR_NONNULL()
Definition: path_util.c:1737
#define FILENAME_IS_CURRPAR(_n)
void BLI_path_sequence_encode(char *string, const char *head, const char *tail, unsigned short numlen, int pic)
Definition: path_util.c:154
int BLI_path_slash_ensure(char *string) ATTR_NONNULL()
Definition: path_util.c:1981
bool BLI_filename_make_safe(char *fname) ATTR_NONNULL(1)
Definition: path_util.c:299
void BLI_path_rel(char *file, const char *relfile) ATTR_NONNULL()
Definition: path_util.c:519
bool BLI_path_parent_dir(char *path) ATTR_NONNULL()
Definition: path_util.c:708
void BLI_split_dirfile(const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
Definition: path_util.c:1654
#define FILE_MAXDIR
bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL()
Definition: path_util.c:1016
#define FILENAME_IS_PARENT(_n)
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition: rct.c:446
int BLI_rcti_length_x(const rcti *rect, const int x)
Definition: rct.c:174
int BLI_rcti_length_y(const rcti *rect, const int y)
Definition: rct.c:185
void BLI_rctf_rcti_copy(struct rctf *dst, const struct rcti *src)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:70
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
unsigned int uint
Definition: BLI_sys_types.h:83
unsigned short ushort
Definition: BLI_sys_types.h:84
#define UNPACK2(a)
#define IN_RANGE(a, b, c)
#define UNUSED(x)
#define MAX2(a, b)
#define UNLIKELY(x)
#define ELEM(...)
#define MIN2(a, b)
#define STREQ(a, b)
Compatibility-like things for windows.
void BLI_windows_get_default_root_dir(char *root_dir)
external readfile function prototypes.
bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, char **r_name)
Definition: readfile.c:1701
@ RGN_TYPE_WINDOW
@ FILE_SORT_DEFAULT
@ FILE_TYPE_ASSET
@ FILE_TYPE_DIR
@ SPACE_FILE
@ FILE_PARAMS_RENAME_POSTSCROLL_PENDING
@ FILE_PARAMS_RENAME_ACTIVE
@ FILE_PARAMS_RENAME_POSTSCROLL_ACTIVE
@ FILE_PARAMS_RENAME_PENDING
@ FILE_SEL_EDITING
@ FILE_SEL_HIGHLIGHTED
@ FILE_SEL_SELECTED
@ FILE_CHECK_EXISTING
@ FILE_SORT_INVERT
@ FILE_DIRSEL_ONLY
@ FILE_HIDE_DOT
#define FILE_MAX_LIBEXTRA
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_file_change_dir(struct bContext *C)
Definition: filesel.c:1085
#define FILE_LAYOUT_HOR
Definition: ED_fileselect.h:44
#define FILE_LAYOUT_VER
Definition: ED_fileselect.h:45
@ FS_CATEGORY_RECENT
@ FS_CATEGORY_BOOKMARKS
struct FSMenu * ED_fsmenu_get(void)
Definition: fsmenu.c:78
FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const struct rcti *rect)
int ED_fileselect_layout_offset(FileLayout *layout, int x, int y)
Definition: filesel.c:717
bool ED_fileselect_layout_is_inside_pt(const FileLayout *layout, const struct View2D *v2d, int x, int y)
struct FSMenuEntry * ED_fsmenu_get_category(struct FSMenu *fsmenu, FSMenuCategory category)
Definition: fsmenu.c:86
bool ED_fileselect_layout_isect_rect(const FileLayout *layout, const struct View2D *v2d, const struct rcti *rect, struct rcti *r_dst)
@ FS_INSERT_SAVE
@ FS_INSERT_FIRST
int ED_fsmenu_get_nentries(struct FSMenu *fsmenu, FSMenuCategory category)
Definition: fsmenu.c:234
int ED_fileselect_layout_numfiles(FileLayout *layout, struct ARegion *region)
Definition: filesel.c:634
struct FileSelectParams * ED_fileselect_get_active_params(const struct SpaceFile *sfile)
FileAttributeColumnType
Definition: ED_fileselect.h:47
@ COLUMN_NONE
Definition: ED_fileselect.h:48
void ED_fsmenu_set_category(struct FSMenu *fsmenu, FSMenuCategory category, struct FSMenuEntry *fsm_head)
Definition: fsmenu.c:213
void ED_fileselect_clear(struct wmWindowManager *wm, struct Scene *owner_scene, struct SpaceFile *sfile)
Definition: filesel.c:1185
struct FSMenuEntry * ED_fsmenu_get_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx)
Definition: fsmenu.c:246
FileLayout * ED_fileselect_get_layout(struct SpaceFile *sfile, struct ARegion *region)
Definition: filesel.c:1037
void ED_area_tag_redraw(ScrArea *area)
Definition: area.c:745
bool ED_operator_file_active(struct bContext *C)
Definition: screen_ops.c:277
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:667
void ED_area_tag_refresh(ScrArea *area)
Definition: area.c:774
#define SEL_OP_USE_PRE_DESELECT(sel_op)
@ UI_SELECT_WALK_RIGHT
@ UI_SELECT_WALK_UP
@ UI_SELECT_WALK_LEFT
@ UI_SELECT_WALK_DOWN
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
eSelectOp
@ SEL_OP_SUB
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition: RNA_types.h:204
#define C
Definition: RandGen.cpp:39
bool UI_textbutton_activate_but(const struct bContext *C, uiBut *but)
bool UI_textbutton_activate_rna(const struct bContext *C, struct ARegion *region, const void *rna_poin_data, const char *rna_prop_id)
void UI_view2d_region_to_view_rctf(const struct View2D *v2d, const struct rctf *rect_src, struct rctf *rect_dst) ATTR_NONNULL()
void UI_view2d_region_to_view(const struct View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
void UI_view2d_curRect_validate(struct View2D *v2d)
Definition: view2d.c:851
@ OPTYPE_INTERNAL
Definition: WM_types.h:175
@ OPTYPE_REGISTER
Definition: WM_types.h:153
@ WM_OP_INVOKE_DEFAULT
Definition: WM_types.h:197
@ WM_OP_EXEC_DEFAULT
Definition: WM_types.h:204
#define ND_SPACE_FILE_PARAMS
Definition: WM_types.h:418
#define NC_SPACE
Definition: WM_types.h:293
#define ND_SPACE_FILE_LIST
Definition: WM_types.h:419
bool ED_asset_clear_id(ID *id)
Definition: asset_edit.cc:51
FILE * file
#define str(s)
void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds)
Definition: file_utils.c:36
int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matched_file)
Definition: filesel.c:1092
void file_params_renamefile_activate(struct SpaceFile *sfile, struct FileSelectParams *params)
Definition: filesel.c:1237
bool file_attribute_column_header_is_inside(const struct View2D *v2d, const FileLayout *layout, int x, int y)
FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d, const FileSelectParams *params, FileLayout *layout, int x)
Definition: filesel.c:820
void fileselect_file_set(SpaceFile *sfile, const int index)
Definition: filesel.c:625
void FILE_OT_filenum(struct wmOperatorType *ot)
Definition: file_ops.c:2671
void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg_but)
Definition: file_ops.c:2523
void FILE_OT_select_box(wmOperatorType *ot)
Definition: file_ops.c:514
void ED_operatormacros_file(void)
Definition: file_ops.c:2910
static int file_next_exec(bContext *C, wmOperator *UNUSED(unused))
Definition: file_ops.c:1963
void file_draw_check(bContext *C)
Definition: file_ops.c:1688
static int file_walk_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
Definition: file_ops.c:842
void FILE_OT_bookmark_add(wmOperatorType *ot)
Definition: file_ops.c:1089
void FILE_OT_rename(struct wmOperatorType *ot)
Definition: file_ops.c:2741
static void file_deselect_all(SpaceFile *sfile, uint flag)
Definition: file_ops.c:107
void FILE_OT_select_bookmark(wmOperatorType *ot)
Definition: file_ops.c:1041
static int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused))
Definition: file_ops.c:1501
static bool file_walk_select_do(bContext *C, SpaceFile *sfile, FileSelectParams *params, const int direction, const bool extend, const bool fill)
Definition: file_ops.c:760
static int new_folder_path(const char *parent, char *folder, char *name)
Definition: file_ops.c:2242
static FileSelection find_file_mouse_rect(SpaceFile *sfile, ARegion *region, const rcti *rect_region)
Definition: file_ops.c:76
static int file_box_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
Definition: file_ops.c:426
static int file_start_filter_exec(bContext *C, wmOperator *UNUSED(op))
Definition: file_ops.c:2870
static bool file_bookmark_move_poll(bContext *C)
Definition: file_ops.c:1283
static int bookmark_add_exec(bContext *C, wmOperator *UNUSED(op))
Definition: file_ops.c:1065
static int file_box_select_exec(bContext *C, wmOperator *op)
Definition: file_ops.c:482
void FILE_OT_cancel(struct wmOperatorType *ot)
Definition: file_ops.c:1514
static int file_directory_new_exec(bContext *C, wmOperator *op)
Definition: file_ops.c:2261
static int file_exec_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition: file_ops.c:1805
static bool file_is_any_selected(struct FileList *files)
Definition: file_ops.c:244
static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
Definition: file_ops.c:2727
void FILE_OT_highlight(struct wmOperatorType *ot)
Definition: file_ops.c:1418
void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UNUSED(arg_but))
Definition: file_ops.c:2433
void FILE_OT_sort_column_ui_context(wmOperatorType *ot)
Definition: file_ops.c:1469
static int file_box_select_find_last_selected(SpaceFile *sfile, ARegion *region, const FileSelection *sel, const int mouse_xy[2])
Definition: file_ops.c:393
static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
Definition: file_ops.c:187
void file_operator_to_sfile(Main *bmain, SpaceFile *sfile, wmOperator *op)
Definition: file_ops.c:1614
static int file_previous_exec(bContext *C, wmOperator *UNUSED(op))
Definition: file_ops.c:1928
void FILE_OT_directory_new(struct wmOperatorType *ot)
Definition: file_ops.c:2346
void FILE_OT_filepath_drop(wmOperatorType *ot)
Definition: file_ops.c:2220
static void file_rename_state_activate(SpaceFile *sfile, int file_idx, bool require_selected)
Definition: file_ops.c:2692
void FILE_OT_parent(struct wmOperatorType *ot)
Definition: file_ops.c:1910
void FILE_OT_bookmark_delete(wmOperatorType *ot)
Definition: file_ops.c:1141
void FILE_OT_select_walk(wmOperatorType *ot)
Definition: file_ops.c:858
static int file_column_sort_ui_context_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
Definition: file_ops.c:1436
static int filepath_drop_exec(bContext *C, wmOperator *op)
Definition: file_ops.c:2192
static FileSelection file_selection_get(bContext *C, const rcti *rect, bool fill)
Definition: file_ops.c:148
static int file_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
Definition: file_ops.c:968
static int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
Definition: file_ops.c:1888
static void clamp_to_filelist(int numfiles, FileSelection *sel)
Definition: file_ops.c:122
void FILE_OT_bookmark_cleanup(wmOperatorType *ot)
Definition: file_ops.c:1201
static int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
Definition: file_ops.c:1851
void file_draw_check_ex(bContext *C, ScrArea *area)
Definition: file_ops.c:1664
void FILE_OT_hidedot(struct wmOperatorType *ot)
Definition: file_ops.c:2592
void FILE_OT_smoothscroll(wmOperatorType *ot)
Definition: file_ops.c:2173
void FILE_OT_refresh(struct wmOperatorType *ot)
Definition: file_ops.c:1870
void FILE_OT_bookmark_move(wmOperatorType *ot)
Definition: file_ops.c:1290
static FileSelection file_current_selection_range_get(struct FileList *files)
Definition: file_ops.c:259
static void filenum_newname(char *name, size_t name_size, int add)
Definition: file_ops.c:2625
static int bookmark_cleanup_exec(bContext *C, wmOperator *UNUSED(op))
Definition: file_ops.c:1165
void FILE_OT_select_all(wmOperatorType *ot)
Definition: file_ops.c:947
static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select, bool fill, bool do_diropen)
Definition: file_ops.c:347
static int file_delete_exec(bContext *C, wmOperator *op)
Definition: file_ops.c:2814
static int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused))
Definition: file_ops.c:2577
static void file_ensure_inside_viewbounds(ARegion *region, SpaceFile *sfile, const int file)
Definition: file_ops.c:289
static bool file_operator_poll(bContext *C)
Definition: file_ops.c:1489
void FILE_OT_reset_recent(wmOperatorType *ot)
Definition: file_ops.c:1345
static bool file_filenum_poll(bContext *C)
Definition: file_ops.c:2610
int file_highlight_set(SpaceFile *sfile, ARegion *region, int mx, int my)
Definition: file_ops.c:1363
void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2))
Definition: file_ops.c:1695
void FILE_OT_select(wmOperatorType *ot)
Definition: file_ops.c:600
void file_sfile_filepath_set(SpaceFile *sfile, const char *filepath)
Definition: file_ops.c:1645
void FILE_OT_delete(struct wmOperatorType *ot)
Definition: file_ops.c:2851
void FILE_OT_execute(struct wmOperatorType *ot)
Definition: file_ops.c:1818
static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
Definition: file_ops.c:1404
static bool file_delete_single(const FileSelectParams *params, FileDirEntry *file, const char **r_error_message)
Definition: file_ops.c:2791
static void file_ensure_selection_inside_viewbounds(ARegion *region, SpaceFile *sfile, FileSelection *sel)
Definition: file_ops.c:329
static int file_select_all_exec(bContext *C, wmOperator *op)
Definition: file_ops.c:890
@ FILE_BOOKMARK_MOVE_UP
Definition: file_ops.c:1223
@ FILE_BOOKMARK_MOVE_DOWN
Definition: file_ops.c:1224
@ FILE_BOOKMARK_MOVE_BOTTOM
Definition: file_ops.c:1225
@ FILE_BOOKMARK_MOVE_TOP
Definition: file_ops.c:1222
void file_sfile_to_operator_ex(Main *bmain, wmOperator *op, SpaceFile *sfile, char *filepath)
Definition: file_ops.c:1532
void FILE_OT_start_filter(struct wmOperatorType *ot)
Definition: file_ops.c:2892
static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
Definition: file_ops.c:2000
static int file_exec(bContext *C, wmOperator *exec_op)
Definition: file_ops.c:1722
static void file_expand_directory(bContext *C)
Definition: file_ops.c:2375
static int file_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
Definition: file_ops.c:2713
void file_sfile_to_operator(Main *bmain, wmOperator *op, SpaceFile *sfile)
Definition: file_ops.c:1607
bool file_draw_check_exists(SpaceFile *sfile)
Definition: file_ops.c:1700
static int reset_recent_exec(bContext *C, wmOperator *UNUSED(op))
Definition: file_ops.c:1326
void FILE_OT_next(struct wmOperatorType *ot)
Definition: file_ops.c:1981
void FILE_OT_view_selected(wmOperatorType *ot)
Definition: file_ops.c:999
static int bookmark_delete_exec(bContext *C, wmOperator *op)
Definition: file_ops.c:1107
FileSelect
Definition: file_ops.c:116
@ FILE_SELECT_NOTHING
Definition: file_ops.c:117
@ FILE_SELECT_DIR
Definition: file_ops.c:118
@ FILE_SELECT_FILE
Definition: file_ops.c:119
void FILE_OT_previous(struct wmOperatorType *ot)
Definition: file_ops.c:1945
static int bookmark_select_exec(bContext *C, wmOperator *op)
Definition: file_ops.c:1020
static int bookmark_move_exec(bContext *C, wmOperator *op)
Definition: file_ops.c:1228
static bool file_delete_poll(bContext *C)
Definition: file_ops.c:2760
static bool file_walk_select_selection_set(wmWindow *win, ARegion *region, 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)
Definition: file_ops.c:642
static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition: file_ops.c:539
static int file_filenum_exec(bContext *C, wmOperator *op)
Definition: file_ops.c:2654
bool filelist_is_ready(struct FileList *filelist)
Definition: filelist.c:1910
void filelist_entry_select_index_set(FileList *filelist, const int index, FileSelType select, uint flag, FileCheckType check)
Definition: filelist.c:2656
bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group)
Definition: filelist.c:2731
const char * folderlist_peeklastdir(ListBase *folderlist)
Definition: filelist.c:143
void filelist_entry_parent_select_set(FileList *filelist, FileSelType select, uint flag, FileCheckType check)
Definition: filelist.c:2720
void filelist_entries_select_index_range_set(FileList *filelist, FileSelection *sel, FileSelType select, uint flag, FileCheckType check)
Definition: filelist.c:2666
void folderlist_popdir(struct ListBase *folderlist, char *dir)
Definition: filelist.c:99
uint filelist_entry_select_index_get(FileList *filelist, const int index, FileCheckType check)
Definition: filelist.c:2693
void folderlist_pushdir(ListBase *folderlist, const char *dir)
Definition: filelist.c:119
ID * filelist_file_get_id(const FileDirEntry *file)
Definition: filelist.c:2071
void filelist_setrecursion(struct FileList *filelist, const int recursion_level)
Definition: filelist.c:1892
uint filelist_entry_select_set(const FileList *filelist, const FileDirEntry *entry, FileSelType select, uint flag, FileCheckType check)
Definition: filelist.c:2608
FileDirEntry * filelist_file(struct FileList *filelist, int index)
Definition: filelist.c:2041
int filelist_files_ensure(FileList *filelist)
Definition: filelist.c:1931
bool filelist_entry_is_selected(FileList *filelist, const int index)
Definition: filelist.c:2704
bool filelist_is_dir(struct FileList *filelist, const char *path)
Definition: filelist.c:1868
uint filelist_entry_select_get(FileList *filelist, FileDirEntry *entry, FileCheckType check)
Definition: filelist.c:2679
FileSelType
Definition: filelist.h:38
@ FILE_SEL_REMOVE
Definition: filelist.h:39
@ FILE_SEL_ADD
Definition: filelist.h:40
@ FILE_SEL_TOGGLE
Definition: filelist.h:41
FileCheckType
Definition: filelist.h:44
@ CHECK_FILES
Definition: filelist.h:46
@ CHECK_DIRS
Definition: filelist.h:45
@ CHECK_ALL
Definition: filelist.h:47
void fsmenu_remove_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx)
Definition: fsmenu.c:499
void fsmenu_refresh_system_category(struct FSMenu *fsmenu)
Definition: fsmenu.c:1042
void fsmenu_insert_entry(struct FSMenu *fsmenu, FSMenuCategory category, const char *path, const char *name, int icon, FSMenuInsert flag)
Definition: fsmenu.c:381
void fsmenu_refresh_bookmarks_status(wmWindowManager *wm, FSMenu *fsmenu)
Definition: fsmenu.c:1215
void fsmenu_write_file(struct FSMenu *fsmenu, const char *filename)
Definition: fsmenu.c:532
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
static void add(GHash *messages, MemArena *memarena, const Message *msg)
Definition: msgfmt.c:268
INLINE Rall1d< T, V, S > exp(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:295
static void area(int d1, int d2, int e1, int e2, float weights[2])
bool active
all scheduled work for the GPU.
return ret
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
Definition: rna_access.c:6550
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
Definition: rna_access.c:6272
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:6655
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
Definition: rna_access.c:6319
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:866
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2331
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2607
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
Definition: rna_access.c:6514
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6308
void RNA_property_string_get(PointerRNA *ptr, PropertyRNA *prop, char *value)
Definition: rna_access.c:3310
void RNA_property_collection_add(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *r_ptr)
Definition: rna_access.c:3966
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6261
void RNA_property_collection_clear(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:4157
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6402
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
Definition: rna_access.c:3401
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3481
PropertyRNA * RNA_def_string_file_path(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, int maxlen, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3699
PropertyRNA * RNA_def_string_dir_path(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, int maxlen, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3723
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, int maxlen, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3675
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1512
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value, int hardmin, int hardmax, const char *ui_name, const char *ui_description, int softmin, int softmax)
Definition: rna_define.c:3585
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3771
short regiontype
struct FSMenuEntry * next
char name[256]
Definition: fsmenu.c:68
FileAttributeColumn attribute_columns[ATTRIBUTE_COLUMN_MAX]
Definition: ED_fileselect.h:92
int tile_border_y
Definition: ED_fileselect.h:78
int flow_columns
Definition: ED_fileselect.h:84
int tile_border_x
Definition: ED_fileselect.h:77
Definition: DNA_ID.h:273
Definition: BKE_main.h:116
struct wmTimer * smoothscroll_timer
short bookmarknr
struct FileLayout * layout
struct wmOperator * op
ListBase * folders_prev
struct FileList * files
ListBase * folders_next
float xmax
Definition: DNA_vec_types.h:85
float xmin
Definition: DNA_vec_types.h:85
float ymax
Definition: DNA_vec_types.h:86
float ymin
Definition: DNA_vec_types.h:86
int ymin
Definition: DNA_vec_types.h:80
int ymax
Definition: DNA_vec_types.h:80
int xmin
Definition: DNA_vec_types.h:79
int xmax
Definition: DNA_vec_types.h:79
int y
Definition: WM_types.h:581
int mval[2]
Definition: WM_types.h:583
int x
Definition: WM_types.h:581
void * customdata
Definition: WM_types.h:631
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:752
const char * name
Definition: WM_types.h:721
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:768
const char * idname
Definition: WM_types.h:723
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:776
void(* cancel)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:760
struct StructRNA * srna
Definition: WM_types.h:802
const char * description
Definition: WM_types.h:726
bool(* check)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:744
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:736
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: util_avxb.h:167
__forceinline const avxi abs(const avxi &a)
Definition: util_avxi.h:186
#define G(x, y, z)
uint len
int WM_operator_name_call(bContext *C, const char *opstring, short context, PointerRNA *properties)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval)
int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, short context, PointerRNA *properties)
void WM_event_add_mousemove(wmWindow *win)
@ TIMER1
@ EVT_FILESELECT_CANCEL
@ EVT_FILESELECT_EXEC
PointerRNA * ptr
Definition: wm_files.c:3157
wmOperatorType * ot
Definition: wm_files.c:3156
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_border_to_rcti(struct wmOperator *op, rcti *rect)
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_confirm_or_exec(wmOperatorType *ot)
void WM_operator_properties_select_operation_simple(wmOperatorType *ot)
void WM_operator_properties_select_walk_direction(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
int WM_operator_confirm(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
int WM_operator_confirm_or_exec(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
bool WM_operator_winactive(bContext *C)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
Definition: wm_operators.c:584
void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
Definition: wm_operators.c:590
void WM_operator_properties_free(PointerRNA *ptr)
Definition: wm_operators.c:711
void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
Definition: wm_window.c:1669
wmTimer * WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
Definition: wm_window.c:1632