4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2008 Blender Foundation.
21 * All rights reserved.
24 * Contributor(s): Andrea Weikert (c) 2008 Blender Foundation
26 * ***** END GPL LICENSE BLOCK *****
29 #include "BKE_context.h"
30 #include "BKE_screen.h"
31 #include "BKE_global.h"
32 #include "BKE_report.h"
35 #include "BLI_blenlib.h"
36 #include "BLI_utildefines.h"
37 #include "BLI_storage_types.h"
39 #include "BLI_winstuff.h"
42 #include "ED_screen.h"
43 #include "ED_fileselect.h"
45 #include "MEM_guardedalloc.h"
47 #include "RNA_access.h"
48 #include "RNA_define.h"
50 #include "UI_view2d.h"
55 #include "file_intern.h"
64 #define NOTACTIVEFILE 0
68 /* ---------- FILE SELECTION ------------ */
70 static int find_file_mouse(SpaceFile *sfile, struct ARegion* ar, int clamp_bounds, int x, int y)
74 View2D* v2d = &ar->v2d;
76 UI_view2d_region_to_view(v2d, x, y, &fx, &fy);
78 active_file = ED_fileselect_layout_offset(sfile->layout, clamp_bounds, v2d->tot.xmin + fx, v2d->tot.ymax - fy);
84 static void file_deselect_all(SpaceFile* sfile)
86 int numfiles = filelist_numfiles(sfile->files);
89 for ( i=0; i < numfiles; ++i) {
90 struct direntry* file = filelist_file(sfile->files, i);
91 if (file && (file->flags & ACTIVEFILE)) {
92 file->flags &= ~ACTIVEFILE;
97 typedef enum FileSelect { FILE_SELECT_DIR = 1,
98 FILE_SELECT_FILE = 2 } FileSelect;
101 static void clamp_to_filelist(int numfiles, int *first_file, int *last_file)
103 /* border select before the first file */
104 if ( (*first_file < 0) && (*last_file >=0 ) ) {
107 /* don't select if everything is outside filelist */
108 if ( (*first_file >= numfiles) && ((*last_file < 0) || (*last_file >= numfiles)) ) {
113 /* fix if last file invalid */
114 if ( (*first_file > 0) && (*last_file < 0) )
115 *last_file = numfiles-1;
118 if ( (*first_file >= numfiles) ) {
119 *first_file = numfiles-1;
121 if ( (*last_file >= numfiles) ) {
122 *last_file = numfiles-1;
126 static FileSelect file_select(bContext* C, const rcti* rect, short selecting, short toggle_one, short fill)
128 ARegion *ar= CTX_wm_region(C);
129 SpaceFile *sfile= CTX_wm_space_file(C);
133 FileSelect retval = FILE_SELECT_FILE;
135 FileSelectParams *params = ED_fileselect_get_params(sfile);
136 // FileLayout *layout = ED_fileselect_get_layout(sfile, ar);
138 int numfiles = filelist_numfiles(sfile->files);
140 params->selstate = NOTACTIVEFILE;
141 first_file = find_file_mouse(sfile, ar, 1, rect->xmin, rect->ymax);
142 last_file = find_file_mouse(sfile, ar, 1, rect->xmax, rect->ymin);
144 clamp_to_filelist(numfiles, &first_file, &last_file);
146 if (fill && (last_file >= 0) && (last_file < numfiles) ) {
149 struct direntry* file = filelist_file(sfile->files, f);
150 if (file->flags & ACTIVEFILE)
159 /* select all valid files between first and last indicated */
160 if ( (first_file >= 0) && (first_file < numfiles) && (last_file >= 0) && (last_file < numfiles) ) {
161 for (act_file = first_file; act_file <= last_file; act_file++) {
162 struct direntry* file = filelist_file(sfile->files, act_file);
165 if (file->flags & ACTIVEFILE) {
166 file->flags &= ~ACTIVEFILE;
169 file->flags |= ACTIVEFILE;
170 } else if (selecting)
171 file->flags |= ACTIVEFILE;
173 file->flags &= ~ACTIVEFILE;
177 /* Don't act on multiple selected files */
178 if (first_file != last_file) selecting= 0;
180 /* make the last file active */
181 if (selecting && (last_file >= 0 && last_file < numfiles)) {
182 struct direntry* file = filelist_file(sfile->files, last_file);
183 params->active_file = last_file;
185 if(file && S_ISDIR(file->type)) {
186 /* the path is too long and we are not going up! */
187 if (strcmp(file->relname, "..") && strlen(params->dir) + strlen(file->relname) >= FILE_MAX )
189 // XXX error("Path too long, cannot enter this directory");
191 if (strcmp(file->relname, "..")==0) {
193 BLI_parent_dir(params->dir);
195 BLI_cleanup_dir(G.main->name, params->dir);
196 strcat(params->dir, file->relname);
197 BLI_add_slash(params->dir);
200 file_change_dir(C, 0);
201 retval = FILE_SELECT_DIR;
207 BLI_strncpy(params->file, file->relname, FILE_MAXFILE);
213 /* update operator for name change event */
214 file_draw_check_cb(C, NULL, NULL);
221 static int file_border_select_exec(bContext *C, wmOperator *op)
223 ARegion *ar= CTX_wm_region(C);
227 selecting= (RNA_int_get(op->ptr, "gesture_mode")==GESTURE_MODAL_SELECT);
228 rect.xmin= RNA_int_get(op->ptr, "xmin");
229 rect.ymin= RNA_int_get(op->ptr, "ymin");
230 rect.xmax= RNA_int_get(op->ptr, "xmax");
231 rect.ymax= RNA_int_get(op->ptr, "ymax");
233 BLI_isect_rcti(&(ar->v2d.mask), &rect, &rect);
235 if (FILE_SELECT_DIR == file_select(C, &rect, selecting, 0, 0)) {
236 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
238 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
240 return OPERATOR_FINISHED;
243 void FILE_OT_select_border(wmOperatorType *ot)
246 ot->name= "Activate/Select File";
247 ot->description= "Activate/select the file(s) contained in the border";
248 ot->idname= "FILE_OT_select_border";
251 ot->invoke= WM_border_select_invoke;
252 ot->exec= file_border_select_exec;
253 ot->modal= WM_border_select_modal;
254 ot->poll= ED_operator_file_active;
257 WM_operator_properties_gesture_border(ot, 0);
260 static int file_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
262 ARegion *ar= CTX_wm_region(C);
263 SpaceFile *sfile= CTX_wm_space_file(C);
265 int extend = RNA_boolean_get(op->ptr, "extend");
266 int fill = RNA_boolean_get(op->ptr, "fill");
268 if(ar->regiontype != RGN_TYPE_WINDOW)
269 return OPERATOR_CANCELLED;
271 rect.xmin = rect.xmax = event->x - ar->winrct.xmin;
272 rect.ymin = rect.ymax = event->y - ar->winrct.ymin;
274 if(!BLI_in_rcti(&ar->v2d.mask, rect.xmin, rect.ymin))
275 return OPERATOR_CANCELLED;
277 /* single select, deselect all selected first */
278 if (!extend) file_deselect_all(sfile);
280 if (FILE_SELECT_DIR == file_select(C, &rect, 1, extend, fill ))
281 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
283 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
285 WM_event_add_mousemove(C); /* for directory changes */
286 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
288 return OPERATOR_FINISHED;
291 void FILE_OT_select(wmOperatorType *ot)
294 ot->name= "Activate/Select File";
295 ot->description= "Activate/select file";
296 ot->idname= "FILE_OT_select";
299 ot->invoke= file_select_invoke;
300 ot->poll= ED_operator_file_active;
303 RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend selection instead of deselecting everything first.");
304 RNA_def_boolean(ot->srna, "fill", 0, "Fill", "Select everything beginning with the last selection.");
307 static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op))
309 ScrArea *sa= CTX_wm_area(C);
310 SpaceFile *sfile= CTX_wm_space_file(C);
311 int numfiles = filelist_numfiles(sfile->files);
315 /* if any file is selected, deselect all first */
316 for ( i=0; i < numfiles; ++i) {
317 struct direntry* file = filelist_file(sfile->files, i);
318 if (file && (file->flags & ACTIVEFILE)) {
319 file->flags &= ~ACTIVEFILE;
321 ED_area_tag_redraw(sa);
324 /* select all only if previously no file was selected */
326 for ( i=0; i < numfiles; ++i) {
327 struct direntry* file = filelist_file(sfile->files, i);
328 if(file && !S_ISDIR(file->type)) {
329 file->flags |= ACTIVEFILE;
330 ED_area_tag_redraw(sa);
334 return OPERATOR_FINISHED;
337 void FILE_OT_select_all_toggle(wmOperatorType *ot)
340 ot->name= "Select/Deselect All Files";
341 ot->description= "Select/deselect all files";
342 ot->idname= "FILE_OT_select_all_toggle";
345 ot->exec= file_select_all_exec;
349 ot->poll= ED_operator_file_active;
352 /* ---------- BOOKMARKS ----------- */
354 static int bookmark_select_exec(bContext *C, wmOperator *op)
356 SpaceFile *sfile= CTX_wm_space_file(C);
358 if(RNA_struct_find_property(op->ptr, "dir")) {
360 FileSelectParams* params = sfile->params;
362 RNA_string_get(op->ptr, "dir", entry);
363 BLI_strncpy(params->dir, entry, sizeof(params->dir));
364 BLI_cleanup_dir(G.main->name, params->dir);
365 file_change_dir(C, 1);
367 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
370 return OPERATOR_FINISHED;
373 void FILE_OT_select_bookmark(wmOperatorType *ot)
376 ot->name= "Select Directory";
377 ot->description= "Select a bookmarked directory";
378 ot->idname= "FILE_OT_select_bookmark";
381 ot->exec= bookmark_select_exec;
382 ot->poll= ED_operator_file_active;
384 RNA_def_string(ot->srna, "dir", "", 256, "Dir", "");
387 static int bookmark_add_exec(bContext *C, wmOperator *UNUSED(op))
389 ScrArea *sa= CTX_wm_area(C);
390 SpaceFile *sfile= CTX_wm_space_file(C);
391 struct FSMenu* fsmenu = fsmenu_get();
392 struct FileSelectParams* params= ED_fileselect_get_params(sfile);
394 if (params->dir[0] != '\0') {
397 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir, 0, 1);
398 BLI_make_file_string("/", name, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
399 fsmenu_write_file(fsmenu, name);
402 ED_area_tag_redraw(sa);
403 return OPERATOR_FINISHED;
406 void FILE_OT_bookmark_add(wmOperatorType *ot)
409 ot->name= "Add Bookmark";
410 ot->description= "Add a bookmark for the selected/active directory";
411 ot->idname= "FILE_OT_bookmark_add";
414 ot->exec= bookmark_add_exec;
415 ot->poll= ED_operator_file_active;
418 static int bookmark_delete_exec(bContext *C, wmOperator *op)
420 ScrArea *sa= CTX_wm_area(C);
421 struct FSMenu* fsmenu = fsmenu_get();
422 int nentries = fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS);
424 if(RNA_struct_find_property(op->ptr, "index")) {
425 int index = RNA_int_get(op->ptr, "index");
426 if ( (index >-1) && (index < nentries)) {
429 fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index);
430 BLI_make_file_string("/", name, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
431 fsmenu_write_file(fsmenu, name);
432 ED_area_tag_redraw(sa);
436 return OPERATOR_FINISHED;
439 void FILE_OT_delete_bookmark(wmOperatorType *ot)
442 ot->name= "Delete Bookmark";
443 ot->description= "Delete selected bookmark";
444 ot->idname= "FILE_OT_delete_bookmark";
447 ot->exec= bookmark_delete_exec;
448 ot->poll= ED_operator_file_active;
450 RNA_def_int(ot->srna, "index", -1, -1, 20000, "Index", "", -1, 20000);
453 int file_hilight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
455 FileSelectParams* params;
456 int numfiles, actfile, origfile;
458 if(sfile==NULL || sfile->files==NULL) return 0;
460 numfiles = filelist_numfiles(sfile->files);
461 params = ED_fileselect_get_params(sfile);
463 origfile= params->active_file;
465 mx -= ar->winrct.xmin;
466 my -= ar->winrct.ymin;
468 if(BLI_in_rcti(&ar->v2d.mask, mx, my)) {
469 actfile = find_file_mouse(sfile, ar, 0, mx , my);
471 if((actfile >= 0) && (actfile < numfiles))
472 params->active_file=actfile;
474 params->active_file= -1;
477 params->active_file= -1;
479 return (params->active_file != origfile);
482 static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
484 ARegion *ar= CTX_wm_region(C);
485 SpaceFile *sfile= CTX_wm_space_file(C);
487 if(!file_hilight_set(sfile, ar, event->x, event->y))
488 return OPERATOR_CANCELLED;
490 ED_area_tag_redraw(CTX_wm_area(C));
492 return OPERATOR_FINISHED;
495 void FILE_OT_highlight(struct wmOperatorType *ot)
498 ot->name= "Highlight File";
499 ot->description= "Highlight selected file(s)";
500 ot->idname= "FILE_OT_highlight";
503 ot->invoke= file_highlight_invoke;
504 ot->poll= ED_operator_file_active;
507 int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused))
509 SpaceFile *sfile= CTX_wm_space_file(C);
510 wmOperator *op = sfile->op;
514 WM_event_fileselect_event(C, op, EVT_FILESELECT_CANCEL);
516 return OPERATOR_FINISHED;
519 int file_operator_poll(bContext *C)
521 int poll = ED_operator_file_active(C);
522 SpaceFile *sfile= CTX_wm_space_file(C);
524 if (!sfile || !sfile->op) poll= 0;
529 void FILE_OT_cancel(struct wmOperatorType *ot)
532 ot->name= "Cancel File Load";
533 ot->description= "Cancel loading of selected file";
534 ot->idname= "FILE_OT_cancel";
537 ot->exec= file_cancel_exec;
538 ot->poll= file_operator_poll;
542 void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
544 BLI_join_dirfile(filepath, sfile->params->dir, sfile->params->file);
545 if(RNA_struct_find_property(op->ptr, "relative_path")) {
546 if(RNA_boolean_get(op->ptr, "relative_path")) {
547 BLI_path_rel(filepath, G.main->name);
551 if(RNA_struct_find_property(op->ptr, "filename")) {
552 RNA_string_set(op->ptr, "filename", sfile->params->file);
554 if(RNA_struct_find_property(op->ptr, "directory")) {
555 RNA_string_set(op->ptr, "directory", sfile->params->dir);
557 if(RNA_struct_find_property(op->ptr, "filepath")) {
558 RNA_string_set(op->ptr, "filepath", filepath);
561 /* some ops have multiple files to select */
564 int i, numfiles = filelist_numfiles(sfile->files);
565 struct direntry *file;
566 if(RNA_struct_find_property(op->ptr, "files")) {
567 for (i=0; i<numfiles; i++) {
568 file = filelist_file(sfile->files, i);
569 if(file->flags & ACTIVEFILE) {
570 if ((file->type & S_IFDIR)==0) {
571 RNA_collection_add(op->ptr, "files", &itemptr);
572 RNA_string_set(&itemptr, "name", file->relname);
578 if(RNA_struct_find_property(op->ptr, "dirs")) {
579 for (i=0; i<numfiles; i++) {
580 file = filelist_file(sfile->files, i);
581 if(file->flags & ACTIVEFILE) {
582 if ((file->type & S_IFDIR)) {
583 RNA_collection_add(op->ptr, "dirs", &itemptr);
584 RNA_string_set(&itemptr, "name", file->relname);
592 void file_operator_to_sfile(SpaceFile *sfile, wmOperator *op)
595 if(RNA_struct_find_property(op->ptr, "filename")) {
596 RNA_string_get(op->ptr, "filename", sfile->params->file);
599 if(RNA_struct_find_property(op->ptr, "directory")) {
600 RNA_string_get(op->ptr, "directory", sfile->params->dir);
604 /* If neither of the above are set, split the filepath back */
605 if(RNA_struct_find_property(op->ptr, "filepath")) {
607 char filepath[FILE_MAX];
608 RNA_string_get(op->ptr, "filepath", filepath);
609 BLI_split_dirfile(filepath, sfile->params->dir, sfile->params->file);
613 /* XXX, files and dirs updates missing, not really so important though */
616 void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2))
618 SpaceFile *sfile= CTX_wm_space_file(C);
619 wmOperator *op= sfile->op;
620 if(op) { /* fail on reload */
621 if(op->type->check) {
622 char filepath[FILE_MAX];
623 file_sfile_to_operator(op, sfile, filepath);
626 if(op->type->check(C, op)) {
627 file_operator_to_sfile(sfile, op);
629 /* redraw, else the changed settings wont get updated */
630 ED_area_tag_redraw(CTX_wm_area(C));
636 int file_draw_check_exists(SpaceFile *sfile)
638 if(sfile->op) { /* fails on reload */
639 if(RNA_struct_find_property(sfile->op->ptr, "check_existing")) {
640 if(RNA_boolean_get(sfile->op->ptr, "check_existing")) {
641 char filepath[FILE_MAX];
642 BLI_join_dirfile(filepath, sfile->params->dir, sfile->params->file);
643 if(BLI_exists(filepath) && !BLI_is_dir(filepath)) {
653 /* sends events now, so things get handled on windowqueue level */
654 int file_exec(bContext *C, wmOperator *exec_op)
656 SpaceFile *sfile= CTX_wm_space_file(C);
657 char filepath[FILE_MAX];
660 wmOperator *op= sfile->op;
662 /* when used as a macro, for doubleclick,
663 to prevent closing when doubleclicking on .. item */
664 if (RNA_boolean_get(exec_op->ptr, "need_active")) {
666 struct direntry *file;
668 for (i=0; i<filelist_numfiles(sfile->files); i++) {
669 file = filelist_file(sfile->files, i);
670 if(file->flags & ACTIVEFILE) {
675 return OPERATOR_CANCELLED;
680 file_sfile_to_operator(op, sfile, filepath);
682 fsmenu_insert_entry(fsmenu_get(), FS_CATEGORY_RECENT, sfile->params->dir,0, 1);
683 BLI_make_file_string(G.main->name, filepath, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
684 fsmenu_write_file(fsmenu_get(), filepath);
685 WM_event_fileselect_event(C, op, EVT_FILESELECT_EXEC);
689 return OPERATOR_FINISHED;
692 void FILE_OT_execute(struct wmOperatorType *ot)
695 ot->name= "Execute File Window";
696 ot->description= "Execute selected file";
697 ot->idname= "FILE_OT_execute";
701 ot->poll= file_operator_poll;
703 RNA_def_boolean(ot->srna, "need_active", 0, "Need Active", "Only execute if there's an active selected file in the file list.");
707 int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
709 SpaceFile *sfile= CTX_wm_space_file(C);
712 if (BLI_has_parent(sfile->params->dir)) {
713 BLI_parent_dir(sfile->params->dir);
714 BLI_cleanup_dir(G.main->name, sfile->params->dir);
715 file_change_dir(C, 0);
716 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
720 return OPERATOR_FINISHED;
725 void FILE_OT_parent(struct wmOperatorType *ot)
728 ot->name= "Parent File";
729 ot->description= "Move to parent directory";
730 ot->idname= "FILE_OT_parent";
733 ot->exec= file_parent_exec;
734 ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
738 int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
740 SpaceFile *sfile= CTX_wm_space_file(C);
742 ED_fileselect_clear(C, sfile);
744 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
746 return OPERATOR_FINISHED;
750 void FILE_OT_previous(struct wmOperatorType *ot)
753 ot->name= "Previous Folder";
754 ot->description= "Move to previous folder";
755 ot->idname= "FILE_OT_previous";
758 ot->exec= file_previous_exec;
759 ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
762 int file_previous_exec(bContext *C, wmOperator *UNUSED(unused))
764 SpaceFile *sfile= CTX_wm_space_file(C);
767 if (!sfile->folders_next)
768 sfile->folders_next = folderlist_new();
770 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
771 folderlist_popdir(sfile->folders_prev, sfile->params->dir);
772 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
774 file_change_dir(C, 1);
776 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
778 return OPERATOR_FINISHED;
781 void FILE_OT_next(struct wmOperatorType *ot)
784 ot->name= "Next Folder";
785 ot->description= "Move to next folder";
786 ot->idname= "FILE_OT_next";
789 ot->exec= file_next_exec;
790 ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
793 int file_next_exec(bContext *C, wmOperator *UNUSED(unused))
795 SpaceFile *sfile= CTX_wm_space_file(C);
797 if (!sfile->folders_next)
798 sfile->folders_next = folderlist_new();
800 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
801 folderlist_popdir(sfile->folders_next, sfile->params->dir);
803 // update folder_prev so we can check for it in folderlist_clear_next()
804 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
806 file_change_dir(C, 1);
808 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
810 return OPERATOR_FINISHED;
814 /* only meant for timer usage */
815 static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
817 ScrArea *sa = CTX_wm_area(C);
818 SpaceFile *sfile= CTX_wm_space_file(C);
819 ARegion *ar, *oldar= CTX_wm_region(C);
820 int numfiles, offset;
825 /* escape if not our timer */
826 if(sfile->smoothscroll_timer==NULL || sfile->smoothscroll_timer!=event->customdata)
827 return OPERATOR_PASS_THROUGH;
829 numfiles = filelist_numfiles(sfile->files);
831 /* check if we are editing a name */
832 for (i=0; i < numfiles; ++i)
834 struct direntry *file = filelist_file(sfile->files, i);
835 if (file->flags & EDITING) {
841 /* if we are not editing, we are done */
843 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
844 sfile->smoothscroll_timer=NULL;
845 return OPERATOR_PASS_THROUGH;
848 /* we need the correct area for scrolling */
849 ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
850 if (!ar || ar->regiontype != RGN_TYPE_WINDOW) {
851 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
852 sfile->smoothscroll_timer=NULL;
853 return OPERATOR_PASS_THROUGH;
856 offset = ED_fileselect_layout_offset(sfile->layout, 0, ar->v2d.cur.xmin, -ar->v2d.cur.ymax);
857 if (offset<0) offset=0;
859 /* scroll offset is the first file in the row/column we are editing in */
860 if (sfile->scroll_offset == 0) {
861 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
862 sfile->scroll_offset = (edit_idx/sfile->layout->rows)*sfile->layout->rows;
863 if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->rows;
865 sfile->scroll_offset = (edit_idx/sfile->layout->columns)*sfile->layout->columns;
866 if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->columns;
870 numfiles_layout = ED_fileselect_layout_numfiles(sfile->layout, ar);
872 /* check if we have reached our final scroll position */
873 if ( (sfile->scroll_offset >= offset) && (sfile->scroll_offset < offset + numfiles_layout) ) {
874 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
875 sfile->smoothscroll_timer=NULL;
876 return OPERATOR_FINISHED;
879 /* temporarily set context to the main window region,
880 * so the scroll operators work */
881 CTX_wm_region_set(C, ar);
883 /* scroll one step in the desired direction */
884 if (sfile->scroll_offset < offset) {
885 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
886 WM_operator_name_call(C, "VIEW2D_OT_scroll_left", 0, NULL);
888 WM_operator_name_call(C, "VIEW2D_OT_scroll_up", 0, NULL);
892 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
893 WM_operator_name_call(C, "VIEW2D_OT_scroll_right", 0, NULL);
895 WM_operator_name_call(C, "VIEW2D_OT_scroll_down", 0, NULL);
899 ED_region_tag_redraw(CTX_wm_region(C));
901 /* and restore context */
902 CTX_wm_region_set(C, oldar);
904 return OPERATOR_FINISHED;
908 void FILE_OT_smoothscroll(wmOperatorType *ot)
912 ot->name= "Smooth Scroll";
913 ot->idname= "FILE_OT_smoothscroll";
914 ot->description="Smooth scroll to make editable file visible.";
917 ot->invoke= file_smoothscroll_invoke;
919 ot->poll= ED_operator_file_active;
923 /* create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created.
924 The actual name is returned in 'name', 'folder' contains the complete path, including the new folder name.
926 static int new_folder_path(const char* parent, char *folder, char *name)
931 BLI_strncpy(name, "New Folder", FILE_MAXFILE);
932 BLI_join_dirfile(folder, parent, name);
933 /* check whether folder with the name already exists, in this case
934 add number to the name. Check length of generated name to avoid
935 crazy case of huge number of folders each named 'New Folder (x)' */
936 while (BLI_exists(folder) && (len<FILE_MAXFILE)) {
937 len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
938 BLI_join_dirfile(folder, parent, name);
942 return (len<FILE_MAXFILE);
945 int file_directory_new_exec(bContext *C, wmOperator *op)
947 char name[FILE_MAXFILE];
949 int generate_name= 1;
951 SpaceFile *sfile= CTX_wm_space_file(C);
954 BKE_report(op->reports,RPT_WARNING, "No parent directory given.");
955 return OPERATOR_CANCELLED;
960 if(RNA_struct_find_property(op->ptr, "directory")) {
961 RNA_string_get(op->ptr, "directory", path);
962 if (path[0] != '\0') generate_name= 0;
966 /* create a new, non-existing folder name */
967 if (!new_folder_path(sfile->params->dir, path, name)) {
968 BKE_report(op->reports,RPT_ERROR, "Couldn't create new folder name.");
969 return OPERATOR_CANCELLED;
973 /* create the file */
974 BLI_recurdir_fileops(path);
976 if (!BLI_exists(path)) {
977 BKE_report(op->reports,RPT_ERROR, "Couldn't create new folder.");
978 return OPERATOR_CANCELLED;
981 /* now remember file to jump into editing */
982 BLI_strncpy(sfile->params->renamefile, name, FILE_MAXFILE);
984 /* set timer to smoothly view newly generated file */
985 sfile->smoothscroll_timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER1, 1.0/1000.0); /* max 30 frs/sec */
986 sfile->scroll_offset=0;
988 /* reload dir to make sure we're seeing what's in the directory */
989 ED_fileselect_clear(C, sfile);
990 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
992 return OPERATOR_FINISHED;
996 void FILE_OT_directory_new(struct wmOperatorType *ot)
999 ot->name= "Create New Directory";
1000 ot->description= "Create a new directory";
1001 ot->idname= "FILE_OT_directory_new";
1004 ot->invoke= WM_operator_confirm;
1005 ot->exec= file_directory_new_exec;
1006 ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1008 RNA_def_string_dir_path(ot->srna, "directory", "", FILE_MAX, "Directory", "Name of new directory");
1013 static void file_expand_directory(bContext *C)
1015 SpaceFile *sfile= CTX_wm_space_file(C);
1018 if ( sfile->params->dir[0] == '~' ) {
1019 char tmpstr[sizeof(sfile->params->dir)-1];
1020 strncpy(tmpstr, sfile->params->dir+1, sizeof(tmpstr));
1021 BLI_join_dirfile(sfile->params->dir, BLI_getDefaultDocumentFolder(), tmpstr);
1025 if (sfile->params->dir[0] == '\0')
1026 get_default_root(sfile->params->dir);
1031 int file_directory_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1033 SpaceFile *sfile= CTX_wm_space_file(C);
1036 file_expand_directory(C);
1038 if (!BLI_exists(sfile->params->dir)) {
1039 return WM_operator_confirm_message(C, op, "Create new directory?");
1042 return file_directory_exec(C, op);
1045 return OPERATOR_CANCELLED;
1050 int file_directory_exec(bContext *C, wmOperator *UNUSED(unused))
1052 SpaceFile *sfile= CTX_wm_space_file(C);
1055 file_expand_directory(C);
1057 if (!BLI_exists(sfile->params->dir)) {
1058 BLI_recurdir_fileops(sfile->params->dir);
1061 /* special case, user may have pasted a fulepath into the directory */
1062 if(BLI_exists(sfile->params->dir) && BLI_is_dir(sfile->params->dir) == 0) {
1063 char path[sizeof(sfile->params->dir)];
1064 BLI_strncpy(path, sfile->params->dir, sizeof(path));
1065 BLI_split_dirfile(path, sfile->params->dir, sfile->params->file);
1068 BLI_cleanup_dir(G.main->name, sfile->params->dir);
1069 BLI_add_slash(sfile->params->dir);
1070 file_change_dir(C, 1);
1072 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
1076 return OPERATOR_FINISHED;
1079 int file_filename_exec(bContext *C, wmOperator *UNUSED(unused))
1081 SpaceFile *sfile= CTX_wm_space_file(C);
1084 if (file_select_match(sfile, sfile->params->file))
1086 sfile->params->file[0] = '\0';
1087 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
1091 return OPERATOR_FINISHED;
1094 void FILE_OT_directory(struct wmOperatorType *ot)
1097 ot->name= "Enter Directory Name";
1098 ot->description= "Enter a directory name";
1099 ot->idname= "FILE_OT_directory";
1102 ot->invoke= file_directory_invoke;
1103 ot->exec= file_directory_exec;
1104 ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1107 void FILE_OT_refresh(struct wmOperatorType *ot)
1110 ot->name= "Refresh Filelist";
1111 ot->description= "Refresh the file list";
1112 ot->idname= "FILE_OT_refresh";
1115 ot->exec= file_refresh_exec;
1116 ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1119 int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused))
1121 SpaceFile *sfile= CTX_wm_space_file(C);
1124 sfile->params->flag ^= FILE_HIDE_DOT;
1125 ED_fileselect_clear(C, sfile);
1126 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
1129 return OPERATOR_FINISHED;
1133 void FILE_OT_hidedot(struct wmOperatorType *ot)
1136 ot->name= "Toggle Hide Dot Files";
1137 ot->description= "Toggle hide hidden dot files";
1138 ot->idname= "FILE_OT_hidedot";
1141 ot->exec= file_hidedot_exec;
1142 ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1145 struct ARegion *file_buttons_region(struct ScrArea *sa)
1147 ARegion *ar, *arnew;
1149 for(ar= sa->regionbase.first; ar; ar= ar->next)
1150 if(ar->regiontype==RGN_TYPE_CHANNELS)
1153 /* add subdiv level; after header */
1154 for(ar= sa->regionbase.first; ar; ar= ar->next)
1155 if(ar->regiontype==RGN_TYPE_HEADER)
1159 if(ar==NULL) return NULL;
1161 arnew= MEM_callocN(sizeof(ARegion), "buttons for file panels");
1163 BLI_insertlinkafter(&sa->regionbase, ar, arnew);
1164 arnew->regiontype= RGN_TYPE_CHANNELS;
1165 arnew->alignment= RGN_ALIGN_LEFT;
1167 arnew->flag = RGN_FLAG_HIDDEN;
1172 int file_bookmark_toggle_exec(bContext *C, wmOperator *UNUSED(unused))
1174 ScrArea *sa= CTX_wm_area(C);
1175 ARegion *ar= file_buttons_region(sa);
1178 ED_region_toggle_hidden(C, ar);
1180 return OPERATOR_FINISHED;
1183 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
1186 ot->name= "Toggle Bookmarks";
1187 ot->description= "Toggle bookmarks display";
1188 ot->idname= "FILE_OT_bookmark_toggle";
1191 ot->exec= file_bookmark_toggle_exec;
1192 ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1196 int file_filenum_exec(bContext *C, wmOperator *op)
1198 SpaceFile *sfile= CTX_wm_space_file(C);
1199 ScrArea *sa= CTX_wm_area(C);
1201 int inc = RNA_int_get(op->ptr, "increment");
1202 if(sfile->params && (inc != 0)) {
1203 BLI_newname(sfile->params->file, inc);
1204 ED_area_tag_redraw(sa);
1205 file_draw_check_cb(C, NULL, NULL);
1206 // WM_event_add_notifier(C, NC_WINDOW, NULL);
1209 return OPERATOR_FINISHED;
1213 void FILE_OT_filenum(struct wmOperatorType *ot)
1216 ot->name= "Increment Number in Filename";
1217 ot->description= "Increment number in filename";
1218 ot->idname= "FILE_OT_filenum";
1221 ot->exec= file_filenum_exec;
1222 ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1225 RNA_def_int(ot->srna, "increment", 1, 0, 100, "Increment", "", 0,100);
1228 int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
1230 ScrArea *sa= CTX_wm_area(C);
1231 SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
1234 int idx = sfile->params->active_file;
1235 int numfiles = filelist_numfiles(sfile->files);
1236 if ( (0<=idx) && (idx<numfiles) ) {
1237 struct direntry *file= filelist_file(sfile->files, idx);
1238 file->flags |= EDITING;
1239 BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE);
1240 sfile->params->renamefile[0]= '\0';
1242 ED_area_tag_redraw(sa);
1245 return OPERATOR_FINISHED;
1249 int file_rename_poll(bContext *C)
1251 int poll = ED_operator_file_active(C);
1252 SpaceFile *sfile= CTX_wm_space_file(C);
1254 if (sfile && sfile->params) {
1255 if (sfile->params->active_file < 0) {
1258 char dir[FILE_MAX], group[FILE_MAX];
1259 if (filelist_islibrary(sfile->files, dir, group)) poll= 0;
1267 void FILE_OT_rename(struct wmOperatorType *ot)
1270 ot->name= "Rename File or Directory";
1271 ot->description= "Rename file or file directory";
1272 ot->idname= "FILE_OT_rename";
1275 ot->exec= file_rename_exec;
1276 ot->poll= file_rename_poll;
1280 int file_delete_poll(bContext *C)
1282 int poll = ED_operator_file_active(C);
1283 SpaceFile *sfile= CTX_wm_space_file(C);
1284 struct direntry* file;
1286 if (sfile && sfile->params) {
1287 if (sfile->params->active_file < 0) {
1290 char dir[FILE_MAX], group[FILE_MAX];
1291 if (filelist_islibrary(sfile->files, dir, group)) poll= 0;
1292 file = filelist_file(sfile->files, sfile->params->active_file);
1293 if (file && S_ISDIR(file->type)) poll= 0;
1302 int file_delete_exec(bContext *C, wmOperator *UNUSED(op))
1305 SpaceFile *sfile= CTX_wm_space_file(C);
1306 struct direntry* file;
1309 file = filelist_file(sfile->files, sfile->params->active_file);
1310 BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relname);
1311 BLI_delete(str, 0, 0);
1312 ED_fileselect_clear(C, sfile);
1313 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
1315 return OPERATOR_FINISHED;
1319 void FILE_OT_delete(struct wmOperatorType *ot)
1322 ot->name= "Delete File";
1323 ot->description= "Delete selected file";
1324 ot->idname= "FILE_OT_delete";
1327 ot->invoke= WM_operator_confirm;
1328 ot->exec= file_delete_exec;
1329 ot->poll= file_delete_poll; /* <- important, handler is on window level */
1333 void ED_operatormacros_file(void)
1336 wmOperatorTypeMacro *otmacro;
1338 ot= WM_operatortype_append_macro("FILE_OT_select_execute", "Select and Execute", OPTYPE_UNDO|OPTYPE_REGISTER);
1339 WM_operatortype_macro_define(ot, "FILE_OT_select");
1340 otmacro= WM_operatortype_macro_define(ot, "FILE_OT_execute");
1341 RNA_boolean_set(otmacro->ptr, "need_active", 1);