Refactor: BLI_path_util (part 2)
[blender.git] / source / blender / editors / space_file / file_ops.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Andrea Weikert (c) 2008 Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_file/file_ops.c
28  *  \ingroup spfile
29  */
30
31 #include "BLI_blenlib.h"
32 #include "BLI_utildefines.h"
33 #include "BLI_fileops_types.h"
34
35 #include "BLO_readfile.h"
36
37 #include "BKE_appdir.h"
38 #include "BKE_context.h"
39 #include "BKE_screen.h"
40 #include "BKE_global.h"
41 #include "BKE_report.h"
42 #include "BKE_main.h"
43
44 #ifdef WIN32
45 #  include "BLI_winstuff.h"
46 #endif
47
48 #include "ED_screen.h"
49 #include "ED_fileselect.h"
50
51 #include "UI_interface.h"
52
53 #include "MEM_guardedalloc.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57
58 #include "UI_view2d.h"
59
60 #include "WM_api.h"
61 #include "WM_types.h"
62
63 #include "file_intern.h"
64 #include "filelist.h"
65 #include "fsmenu.h"
66
67 #include <stdlib.h>
68 #include <string.h>
69 #include <stdio.h>
70 #include <ctype.h>
71
72 /* ---------- FILE SELECTION ------------ */
73 static FileSelection find_file_mouse_rect(SpaceFile *sfile, ARegion *ar, const rcti *rect_region)
74 {
75         FileSelection sel;
76         
77         View2D *v2d = &ar->v2d;
78         rcti rect_view;
79         rctf rect_view_fl;
80         rctf rect_region_fl;
81
82         BLI_rctf_rcti_copy(&rect_region_fl, rect_region);
83
84         UI_view2d_region_to_view_rctf(v2d, &rect_region_fl, &rect_view_fl);
85
86         BLI_rcti_init(&rect_view,
87                       (int)(v2d->tot.xmin + rect_view_fl.xmin),
88                       (int)(v2d->tot.xmin + rect_view_fl.xmax),
89                       (int)(v2d->tot.ymax - rect_view_fl.ymin),
90                       (int)(v2d->tot.ymax - rect_view_fl.ymax));
91
92         sel  = ED_fileselect_layout_offset_rect(sfile->layout, &rect_view);
93         
94         return sel;
95 }
96
97 static void file_deselect_all(SpaceFile *sfile, unsigned int flag)
98 {
99         FileSelection sel;
100         sel.first = 0;
101         sel.last = filelist_numfiles(sfile->files) - 1;
102         
103         filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, flag, CHECK_ALL);
104 }
105
106 typedef enum FileSelect { 
107         FILE_SELECT_NOTHING = 0,
108         FILE_SELECT_DIR = 1, 
109         FILE_SELECT_FILE = 2 
110 } FileSelect;
111
112 static void clamp_to_filelist(int numfiles, FileSelection *sel)
113 {
114         /* border select before the first file */
115         if ( (sel->first < 0) && (sel->last >= 0) ) {
116                 sel->first = 0;
117         }
118         /* don't select if everything is outside filelist */
119         if ( (sel->first >= numfiles) && ((sel->last < 0) || (sel->last >= numfiles)) ) {
120                 sel->first = -1;
121                 sel->last = -1;
122         }
123         
124         /* fix if last file invalid */
125         if ( (sel->first > 0) && (sel->last < 0) )
126                 sel->last = numfiles - 1;
127
128         /* clamp */
129         if ( (sel->first >= numfiles) ) {
130                 sel->first = numfiles - 1;
131         }
132         if ( (sel->last >= numfiles) ) {
133                 sel->last = numfiles - 1;
134         }
135 }
136
137 static FileSelection file_selection_get(bContext *C, const rcti *rect, bool fill)
138 {
139         ARegion *ar = CTX_wm_region(C);
140         SpaceFile *sfile = CTX_wm_space_file(C);
141         int numfiles = filelist_numfiles(sfile->files);
142         FileSelection sel;
143
144         sel = find_file_mouse_rect(sfile, ar, rect);
145         if (!((sel.first == -1) && (sel.last == -1)) ) {
146                 clamp_to_filelist(numfiles, &sel);
147         }
148
149
150         /* if desired, fill the selection up from the last selected file to the current one */
151         if (fill && (sel.last >= 0) && (sel.last < numfiles) ) {
152                 int f = sel.last;
153                 while (f >= 0) {
154                         if (filelist_is_selected(sfile->files, f, CHECK_ALL) )
155                                 break;
156                         f--;
157                 }
158                 if (f >= 0) {
159                         sel.first = f + 1;
160                 }
161         }
162         return sel;
163 }
164
165 static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
166 {
167         FileSelect retval = FILE_SELECT_NOTHING;
168         SpaceFile *sfile = CTX_wm_space_file(C);
169         FileSelectParams *params = ED_fileselect_get_params(sfile);
170         int numfiles = filelist_numfiles(sfile->files);
171         struct direntry *file;
172
173         /* make the selected file active */
174         if ((selected_idx >= 0) &&
175             (selected_idx < numfiles) &&
176             (file = filelist_file(sfile->files, selected_idx)))
177         {
178                 params->active_file = selected_idx;
179
180                 if (S_ISDIR(file->type)) {
181                         if (do_diropen == false) {
182                                 params->file[0] = '\0';
183                                 retval = FILE_SELECT_DIR;
184                         }
185                         /* the path is too long and we are not going up! */
186                         else if (strcmp(file->relname, "..") && strlen(params->dir) + strlen(file->relname) >= FILE_MAX) {
187                                 // XXX error("Path too long, cannot enter this directory");
188                         }
189                         else {
190                                 if (strcmp(file->relname, "..") == 0) {
191                                         /* avoids /../../ */
192                                         BLI_parent_dir(params->dir);
193                                 }
194                                 else {
195                                         BLI_cleanup_dir(G.main->name, params->dir);
196                                         strcat(params->dir, file->relname);
197                                         BLI_add_slash(params->dir);
198                                 }
199
200                                 file_change_dir(C, 0);
201                                 retval = FILE_SELECT_DIR;
202                         }
203                 }
204                 else {
205                         if (file->relname) {
206                                 BLI_strncpy(params->file, file->relname, FILE_MAXFILE);
207                         }
208                         retval = FILE_SELECT_FILE;
209                 }
210         }
211         return retval;
212 }
213
214
215 static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select, bool fill, bool do_diropen)
216 {
217         SpaceFile *sfile = CTX_wm_space_file(C);
218         FileSelect retval = FILE_SELECT_NOTHING;
219         FileSelection sel = file_selection_get(C, rect, fill); /* get the selection */
220         const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_ALL;
221         
222         /* flag the files as selected in the filelist */
223         filelist_select(sfile->files, &sel, select, SELECTED_FILE, check_type);
224         
225         /* Don't act on multiple selected files */
226         if (sel.first != sel.last) select = 0;
227
228         /* Do we have a valid selection and are we actually selecting */
229         if ((sel.last >= 0) && ((select == FILE_SEL_ADD) || (select == FILE_SEL_TOGGLE))) {
230                 /* Check last selection, if selected, act on the file or dir */
231                 if (filelist_is_selected(sfile->files, sel.last, check_type)) {
232                         retval = file_select_do(C, sel.last, do_diropen);
233                 }
234         }
235
236         /* update operator for name change event */
237         file_draw_check_cb(C, NULL, NULL);
238         
239         return retval;
240 }
241
242 static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
243 {
244         ARegion *ar = CTX_wm_region(C);
245         SpaceFile *sfile = CTX_wm_space_file(C);
246         FileSelectParams *params = ED_fileselect_get_params(sfile);
247         FileSelection sel;
248         rcti rect;
249
250         int result;
251
252         result = WM_border_select_modal(C, op, event);
253
254         if (result == OPERATOR_RUNNING_MODAL) {
255
256                 WM_operator_properties_border_to_rcti(op, &rect);
257
258                 BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect);
259
260                 sel = file_selection_get(C, &rect, 0);
261                 if ( (sel.first != params->sel_first) || (sel.last != params->sel_last) ) {
262                         file_deselect_all(sfile, HILITED_FILE);
263                         filelist_select(sfile->files, &sel, FILE_SEL_ADD, HILITED_FILE, CHECK_ALL);
264                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
265                 }
266                 params->sel_first = sel.first; params->sel_last = sel.last;
267
268         }
269         else {
270                 params->active_file = -1;
271                 params->sel_first = params->sel_last = -1;
272                 file_deselect_all(sfile, HILITED_FILE);
273                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
274         }
275
276         return result;
277 }
278
279 static int file_border_select_exec(bContext *C, wmOperator *op)
280 {
281         ARegion *ar = CTX_wm_region(C);
282         rcti rect;
283         FileSelect ret;
284         const bool select = (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_SELECT);
285         const bool extend = RNA_boolean_get(op->ptr, "extend");
286
287         WM_operator_properties_border_to_rcti(op, &rect);
288
289         if (!extend) {
290                 SpaceFile *sfile = CTX_wm_space_file(C);
291
292                 file_deselect_all(sfile, SELECTED_FILE);
293         }
294
295         BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect);
296
297         ret = file_select(C, &rect, select ? FILE_SEL_ADD : FILE_SEL_REMOVE, false, false);
298         if (FILE_SELECT_DIR == ret) {
299                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
300         }
301         else if (FILE_SELECT_FILE == ret) {
302                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
303         }
304         return OPERATOR_FINISHED;
305 }
306
307 void FILE_OT_select_border(wmOperatorType *ot)
308 {
309         /* identifiers */
310         ot->name = "Activate/Select File";
311         ot->description = "Activate/select the file(s) contained in the border";
312         ot->idname = "FILE_OT_select_border";
313         
314         /* api callbacks */
315         ot->invoke = WM_border_select_invoke;
316         ot->exec = file_border_select_exec;
317         ot->modal = file_border_select_modal;
318         ot->poll = ED_operator_file_active;
319         ot->cancel = WM_border_select_cancel;
320
321         /* properties */
322         WM_operator_properties_gesture_border(ot, 1);
323 }
324
325 static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
326 {
327         ARegion *ar = CTX_wm_region(C);
328         SpaceFile *sfile = CTX_wm_space_file(C);
329         FileSelect ret;
330         rcti rect;
331         const bool extend = RNA_boolean_get(op->ptr, "extend");
332         const bool fill = RNA_boolean_get(op->ptr, "fill");
333         const bool do_diropen = RNA_boolean_get(op->ptr, "open");
334
335         if (ar->regiontype != RGN_TYPE_WINDOW)
336                 return OPERATOR_CANCELLED;
337
338         rect.xmin = rect.xmax = event->mval[0];
339         rect.ymin = rect.ymax = event->mval[1];
340
341         if (!BLI_rcti_isect_pt(&ar->v2d.mask, rect.xmin, rect.ymin))
342                 return OPERATOR_CANCELLED;
343
344         /* single select, deselect all selected first */
345         if (!extend) file_deselect_all(sfile, SELECTED_FILE);
346
347         ret = file_select(C, &rect, extend ? FILE_SEL_TOGGLE : FILE_SEL_ADD, fill, do_diropen);
348         if (FILE_SELECT_DIR == ret)
349                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
350         else if (FILE_SELECT_FILE == ret)
351                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
352
353         WM_event_add_mousemove(C); /* for directory changes */
354         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
355
356         return OPERATOR_FINISHED;
357 }
358
359 void FILE_OT_select(wmOperatorType *ot)
360 {
361         PropertyRNA *prop;
362
363         /* identifiers */
364         ot->name = "Activate/Select File";
365         ot->description = "Activate/select file";
366         ot->idname = "FILE_OT_select";
367         
368         /* api callbacks */
369         ot->invoke = file_select_invoke;
370         ot->poll = ED_operator_file_active;
371
372         /* properties */
373         prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting everything first");
374         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
375         prop = RNA_def_boolean(ot->srna, "fill", false, "Fill", "Select everything beginning with the last selection");
376         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
377         prop = RNA_def_boolean(ot->srna, "open", true, "Open", "Open a directory when selecting it");
378         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
379 }
380
381 static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op))
382 {
383         ScrArea *sa = CTX_wm_area(C);
384         SpaceFile *sfile = CTX_wm_space_file(C);
385         FileSelection sel;
386         int numfiles = filelist_numfiles(sfile->files);
387         int i;
388         bool is_selected = false;
389
390         sel.first = 0; 
391         sel.last = numfiles - 1;
392
393         /* Is any file selected ? */
394         for (i = 0; i < numfiles; ++i) {
395                 if (filelist_is_selected(sfile->files, i, CHECK_ALL)) {
396                         is_selected = true;
397                         break;
398                 }
399         }
400         /* select all only if previously no file was selected */
401         if (is_selected) {
402                 filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, SELECTED_FILE, CHECK_ALL);
403         }
404         else {
405                 const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_FILES;
406                 filelist_select(sfile->files, &sel, FILE_SEL_ADD, SELECTED_FILE, check_type);
407         }
408         ED_area_tag_redraw(sa);
409         return OPERATOR_FINISHED;
410 }
411
412 void FILE_OT_select_all_toggle(wmOperatorType *ot)
413 {
414         /* identifiers */
415         ot->name = "(De)select All Files";
416         ot->description = "Select or deselect all files";
417         ot->idname = "FILE_OT_select_all_toggle";
418         
419         /* api callbacks */
420         ot->exec = file_select_all_exec;
421         ot->poll = ED_operator_file_active;
422
423         /* properties */
424 }
425
426 /* ---------- BOOKMARKS ----------- */
427
428 static int bookmark_select_exec(bContext *C, wmOperator *op)
429 {
430         SpaceFile *sfile = CTX_wm_space_file(C);
431
432         if (RNA_struct_find_property(op->ptr, "dir")) {
433                 char entry[256];
434                 FileSelectParams *params = sfile->params;
435
436                 RNA_string_get(op->ptr, "dir", entry);
437                 BLI_strncpy(params->dir, entry, sizeof(params->dir));
438                 BLI_cleanup_dir(G.main->name, params->dir);
439                 file_change_dir(C, 1);
440
441                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
442         }
443         
444         return OPERATOR_FINISHED;
445 }
446
447 void FILE_OT_select_bookmark(wmOperatorType *ot)
448 {
449         PropertyRNA *prop;
450
451         /* identifiers */
452         ot->name = "Select Directory";
453         ot->description = "Select a bookmarked directory";
454         ot->idname = "FILE_OT_select_bookmark";
455         
456         /* api callbacks */
457         ot->exec = bookmark_select_exec;
458         ot->poll = ED_operator_file_active;
459
460         /* properties */
461         prop = RNA_def_string(ot->srna, "dir", NULL, FILE_MAXDIR, "Dir", "");
462         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
463 }
464
465 static int bookmark_add_exec(bContext *C, wmOperator *UNUSED(op))
466 {
467         ScrArea *sa = CTX_wm_area(C);
468         SpaceFile *sfile = CTX_wm_space_file(C);
469         struct FSMenu *fsmenu = fsmenu_get();
470         struct FileSelectParams *params = ED_fileselect_get_params(sfile);
471
472         if (params->dir[0] != '\0') {
473                 char name[FILE_MAX];
474         
475                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir, FS_INSERT_SAVE);
476                 BLI_make_file_string("/", name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
477                 fsmenu_write_file(fsmenu, name);
478         }
479
480         ED_area_tag_redraw(sa);
481         return OPERATOR_FINISHED;
482 }
483
484 void FILE_OT_bookmark_add(wmOperatorType *ot)
485 {
486         /* identifiers */
487         ot->name = "Add Bookmark";
488         ot->description = "Add a bookmark for the selected/active directory";
489         ot->idname = "FILE_OT_bookmark_add";
490         
491         /* api callbacks */
492         ot->exec = bookmark_add_exec;
493         ot->poll = ED_operator_file_active;
494 }
495
496 static int bookmark_delete_exec(bContext *C, wmOperator *op)
497 {
498         ScrArea *sa = CTX_wm_area(C);
499         struct FSMenu *fsmenu = fsmenu_get();
500         int nentries = fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS);
501         
502         if (RNA_struct_find_property(op->ptr, "index")) {
503                 int index = RNA_int_get(op->ptr, "index");
504                 if ((index > -1) && (index < nentries)) {
505                         char name[FILE_MAX];
506                         
507                         fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index);
508                         BLI_make_file_string("/", name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
509                         fsmenu_write_file(fsmenu, name);
510                         ED_area_tag_redraw(sa);
511                 }
512         }
513
514         return OPERATOR_FINISHED;
515 }
516
517 void FILE_OT_bookmark_delete(wmOperatorType *ot)
518 {
519         PropertyRNA *prop;
520
521         /* identifiers */
522         ot->name = "Delete Bookmark";
523         ot->description = "Delete selected bookmark";
524         ot->idname = "FILE_OT_bookmark_delete";
525         
526         /* api callbacks */
527         ot->exec = bookmark_delete_exec;
528         ot->poll = ED_operator_file_active;
529
530         /* properties */
531         prop = RNA_def_int(ot->srna, "index", -1, -1, 20000, "Index", "", -1, 20000);
532         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
533 }
534
535 static int reset_recent_exec(bContext *C, wmOperator *UNUSED(op))
536 {
537         ScrArea *sa = CTX_wm_area(C);
538         char name[FILE_MAX];
539         struct FSMenu *fsmenu = fsmenu_get();
540         
541         while (fsmenu_get_entry(fsmenu, FS_CATEGORY_RECENT, 0) != NULL) {
542                 fsmenu_remove_entry(fsmenu, FS_CATEGORY_RECENT, 0);
543         }
544         BLI_make_file_string("/", name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
545         fsmenu_write_file(fsmenu, name);
546         ED_area_tag_redraw(sa);
547                 
548         return OPERATOR_FINISHED;
549 }
550
551 void FILE_OT_reset_recent(wmOperatorType *ot)
552 {
553         /* identifiers */
554         ot->name = "Reset Recent";
555         ot->description = "Reset Recent files";
556         ot->idname = "FILE_OT_reset_recent";
557         
558         /* api callbacks */
559         ot->exec = reset_recent_exec;
560         ot->poll = ED_operator_file_active;
561
562 }
563
564 int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
565 {
566         View2D *v2d = &ar->v2d;
567         FileSelectParams *params;
568         int numfiles, origfile;
569
570         if (sfile == NULL || sfile->files == NULL) return 0;
571
572         numfiles = filelist_numfiles(sfile->files);
573         params = ED_fileselect_get_params(sfile);
574
575         origfile = params->active_file;
576
577         mx -= ar->winrct.xmin;
578         my -= ar->winrct.ymin;
579
580         if (BLI_rcti_isect_pt(&ar->v2d.mask, mx, my)) {
581                 float fx, fy;
582                 int active_file;
583
584                 UI_view2d_region_to_view(v2d, mx, my, &fx, &fy);
585
586                 active_file = ED_fileselect_layout_offset(sfile->layout, (int)(v2d->tot.xmin + fx), (int)(v2d->tot.ymax - fy));
587
588                 if ((active_file >= 0) && (active_file < numfiles))
589                         params->active_file = active_file;
590                 else
591                         params->active_file = -1;
592         }
593         else
594                 params->active_file = -1;
595
596         return (params->active_file != origfile);
597 }
598
599 static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
600 {
601         ARegion *ar = CTX_wm_region(C);
602         SpaceFile *sfile = CTX_wm_space_file(C);
603
604         if (!file_highlight_set(sfile, ar, event->x, event->y))
605                 return OPERATOR_CANCELLED;
606
607         ED_area_tag_redraw(CTX_wm_area(C));
608         
609         return OPERATOR_FINISHED;
610 }
611
612 void FILE_OT_highlight(struct wmOperatorType *ot)
613 {
614         /* identifiers */
615         ot->name = "Highlight File";
616         ot->description = "Highlight selected file(s)";
617         ot->idname = "FILE_OT_highlight";
618         
619         /* api callbacks */
620         ot->invoke = file_highlight_invoke;
621         ot->poll = ED_operator_file_active;
622 }
623
624 int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused))
625 {
626         wmWindowManager *wm = CTX_wm_manager(C);
627         SpaceFile *sfile = CTX_wm_space_file(C);
628         wmOperator *op = sfile->op;
629         
630         sfile->op = NULL;
631
632         WM_event_fileselect_event(wm, op, EVT_FILESELECT_CANCEL);
633         
634         return OPERATOR_FINISHED;
635 }
636
637 static int file_operator_poll(bContext *C)
638 {
639         int poll = ED_operator_file_active(C);
640         SpaceFile *sfile = CTX_wm_space_file(C);
641
642         if (!sfile || !sfile->op) poll = 0;
643
644         return poll;
645 }
646
647 void FILE_OT_cancel(struct wmOperatorType *ot)
648 {
649         /* identifiers */
650         ot->name = "Cancel File Load";
651         ot->description = "Cancel loading of selected file";
652         ot->idname = "FILE_OT_cancel";
653         
654         /* api callbacks */
655         ot->exec = file_cancel_exec;
656         ot->poll = file_operator_poll;
657 }
658
659
660 void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
661 {
662         BLI_join_dirfile(filepath, FILE_MAX, sfile->params->dir, sfile->params->file); /* XXX, not real length */
663         if (RNA_struct_find_property(op->ptr, "relative_path")) {
664                 if (RNA_boolean_get(op->ptr, "relative_path")) {
665                         BLI_path_rel(filepath, G.main->name);
666                 }
667         }
668
669         if (RNA_struct_find_property(op->ptr, "filename")) {
670                 RNA_string_set(op->ptr, "filename", sfile->params->file);
671         }
672         if (RNA_struct_find_property(op->ptr, "directory")) {
673                 RNA_string_set(op->ptr, "directory", sfile->params->dir);
674         }
675         if (RNA_struct_find_property(op->ptr, "filepath")) {
676                 RNA_string_set(op->ptr, "filepath", filepath);
677         }
678         
679         /* some ops have multiple files to select */
680         /* this is called on operators check() so clear collections first since
681          * they may be already set. */
682         {
683                 PointerRNA itemptr;
684                 PropertyRNA *prop_files = RNA_struct_find_property(op->ptr, "files");
685                 PropertyRNA *prop_dirs = RNA_struct_find_property(op->ptr, "dirs");
686                 int i, numfiles = filelist_numfiles(sfile->files);
687
688                 if (prop_files) {
689                         int num_files = 0;
690                         RNA_property_collection_clear(op->ptr, prop_files);
691                         for (i = 0; i < numfiles; i++) {
692                                 if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
693                                         struct direntry *file = filelist_file(sfile->files, i);
694                                         RNA_property_collection_add(op->ptr, prop_files, &itemptr);
695                                         RNA_string_set(&itemptr, "name", file->relname);
696                                         num_files++;
697                                 }
698                         }
699                         /* make sure the file specified in the filename button is added even if no files selected */
700                         if (0 == num_files) {
701                                 RNA_property_collection_add(op->ptr, prop_files, &itemptr);
702                                 RNA_string_set(&itemptr, "name", sfile->params->file);
703                         }
704                 }
705
706                 if (prop_dirs) {
707                         int num_dirs = 0;
708                         RNA_property_collection_clear(op->ptr, prop_dirs);
709                         for (i = 0; i < numfiles; i++) {
710                                 if (filelist_is_selected(sfile->files, i, CHECK_DIRS)) {
711                                         struct direntry *file = filelist_file(sfile->files, i);
712                                         RNA_property_collection_add(op->ptr, prop_dirs, &itemptr);
713                                         RNA_string_set(&itemptr, "name", file->relname);
714                                         num_dirs++;
715                                 }
716                         }
717                         
718                         /* make sure the directory specified in the button is added even if no directory selected */
719                         if (0 == num_dirs) {
720                                 RNA_property_collection_add(op->ptr, prop_dirs, &itemptr);
721                                 RNA_string_set(&itemptr, "name", sfile->params->dir);
722                         }
723                 }
724
725
726         }
727 }
728
729 void file_operator_to_sfile(SpaceFile *sfile, wmOperator *op)
730 {
731         PropertyRNA *prop;
732
733         /* If neither of the above are set, split the filepath back */
734         if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
735                 char filepath[FILE_MAX];
736                 RNA_property_string_get(op->ptr, prop, filepath);
737                 BLI_split_dirfile(filepath, sfile->params->dir, sfile->params->file, sizeof(sfile->params->dir), sizeof(sfile->params->file));
738         }
739         else {
740                 if ((prop = RNA_struct_find_property(op->ptr, "filename"))) {
741                         RNA_property_string_get(op->ptr, prop, sfile->params->file);
742                 }
743                 if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
744                         RNA_property_string_get(op->ptr, prop, sfile->params->dir);
745                 }
746         }
747         
748         /* we could check for relative_path property which is used when converting
749          * in the other direction but doesnt hurt to do this every time */
750         BLI_path_abs(sfile->params->dir, G.main->name);
751
752         /* XXX, files and dirs updates missing, not really so important though */
753 }
754
755 void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2))
756 {
757         SpaceFile *sfile = CTX_wm_space_file(C);
758         wmOperator *op = sfile->op;
759         if (op) { /* fail on reload */
760                 if (op->type->check) {
761                         char filepath[FILE_MAX];
762                         file_sfile_to_operator(op, sfile, filepath);
763                         
764                         /* redraw */
765                         if (op->type->check(C, op)) {
766                                 file_operator_to_sfile(sfile, op);
767         
768                                 /* redraw, else the changed settings wont get updated */
769                                 ED_area_tag_redraw(CTX_wm_area(C));
770                         }
771                 }
772         }
773 }
774
775 bool file_draw_check_exists(SpaceFile *sfile)
776 {
777         if (sfile->op) { /* fails on reload */
778                 if (RNA_struct_find_property(sfile->op->ptr, "check_existing")) {
779                         if (RNA_boolean_get(sfile->op->ptr, "check_existing")) {
780                                 char filepath[FILE_MAX];
781                                 BLI_join_dirfile(filepath, sizeof(filepath), sfile->params->dir, sfile->params->file);
782                                 if (BLI_is_file(filepath)) {
783                                         return true;
784                                 }
785                         }
786                 }
787         }
788
789         return false;
790 }
791
792 /* sends events now, so things get handled on windowqueue level */
793 int file_exec(bContext *C, wmOperator *exec_op)
794 {
795         wmWindowManager *wm = CTX_wm_manager(C);
796         SpaceFile *sfile = CTX_wm_space_file(C);
797         char filepath[FILE_MAX];
798         
799         if (sfile->op) {
800                 wmOperator *op = sfile->op;
801         
802                 /* when used as a macro, for doubleclick, 
803                  * to prevent closing when doubleclicking on .. item */
804                 if (RNA_boolean_get(exec_op->ptr, "need_active")) {
805                         int i, active = 0;
806                         
807                         for (i = 0; i < filelist_numfiles(sfile->files); i++) {
808                                 if (filelist_is_selected(sfile->files, i, CHECK_ALL)) {
809                                         active = 1;
810                                         break;
811                                 }
812                         }
813                         if (active == 0)
814                                 return OPERATOR_CANCELLED;
815                 }
816                 
817                 sfile->op = NULL;
818
819                 file_sfile_to_operator(op, sfile, filepath);
820
821                 if (BLI_exists(sfile->params->dir)) {
822                         fsmenu_insert_entry(fsmenu_get(), FS_CATEGORY_RECENT, sfile->params->dir, FS_INSERT_SAVE | FS_INSERT_FIRST);
823                 }
824
825                 BLI_make_file_string(G.main->name, filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
826                 fsmenu_write_file(fsmenu_get(), filepath);
827                 WM_event_fileselect_event(wm, op, EVT_FILESELECT_EXEC);
828
829         }
830                                 
831         return OPERATOR_FINISHED;
832 }
833
834 void FILE_OT_execute(struct wmOperatorType *ot)
835 {
836         PropertyRNA *prop;
837
838         /* identifiers */
839         ot->name = "Execute File Window";
840         ot->description = "Execute selected file";
841         ot->idname = "FILE_OT_execute";
842         
843         /* api callbacks */
844         ot->exec = file_exec;
845         ot->poll = file_operator_poll; 
846
847         /* properties */
848         prop = RNA_def_boolean(ot->srna, "need_active", 0, "Need Active",
849                                "Only execute if there's an active selected file in the file list");
850         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
851 }
852
853
854 int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
855 {
856         SpaceFile *sfile = CTX_wm_space_file(C);
857         
858         if (sfile->params) {
859                 if (BLI_parent_dir(sfile->params->dir)) {
860                         BLI_cleanup_dir(G.main->name, sfile->params->dir);
861                         /* if not browsing in .blend file, we still want to check whether the path is a directory */
862                         if (sfile->params->type == FILE_LOADLIB) {
863                                 char tdir[FILE_MAX], tgroup[FILE_MAX];
864                                 if (BLO_is_a_library(sfile->params->dir, tdir, tgroup)) {
865                                         file_change_dir(C, 0);
866                                 }
867                                 else {
868                                         file_change_dir(C, 1);
869                                 }
870                         }
871                         else {
872                                 file_change_dir(C, 1);
873                         }
874                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
875                 }
876         }
877         
878         return OPERATOR_FINISHED;
879
880 }
881
882
883 void FILE_OT_parent(struct wmOperatorType *ot)
884 {
885         /* identifiers */
886         ot->name = "Parent File";
887         ot->description = "Move to parent directory";
888         ot->idname = "FILE_OT_parent";
889         
890         /* api callbacks */
891         ot->exec = file_parent_exec;
892         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
893 }
894
895
896 static int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
897 {
898         wmWindowManager *wm = CTX_wm_manager(C);
899         SpaceFile *sfile = CTX_wm_space_file(C);
900         struct FSMenu *fsmenu = fsmenu_get();
901
902         ED_fileselect_clear(wm, sfile);
903
904         /* refresh system directory menu */
905         fsmenu_refresh_system_category(fsmenu);
906
907         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
908
909         return OPERATOR_FINISHED;
910
911 }
912
913 void FILE_OT_previous(struct wmOperatorType *ot)
914 {
915         /* identifiers */
916         ot->name = "Previous Folder";
917         ot->description = "Move to previous folder";
918         ot->idname = "FILE_OT_previous";
919         
920         /* api callbacks */
921         ot->exec = file_previous_exec;
922         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
923 }
924
925 int file_previous_exec(bContext *C, wmOperator *UNUSED(unused))
926 {
927         SpaceFile *sfile = CTX_wm_space_file(C);
928
929         if (sfile->params) {
930                 if (!sfile->folders_next)
931                         sfile->folders_next = folderlist_new();
932
933                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
934                 folderlist_popdir(sfile->folders_prev, sfile->params->dir);
935                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
936
937                 file_change_dir(C, 1);
938         }
939         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
940
941         return OPERATOR_FINISHED;
942 }
943
944 void FILE_OT_next(struct wmOperatorType *ot)
945 {
946         /* identifiers */
947         ot->name = "Next Folder";
948         ot->description = "Move to next folder";
949         ot->idname = "FILE_OT_next";
950         
951         /* api callbacks */
952         ot->exec = file_next_exec;
953         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
954 }
955
956 int file_next_exec(bContext *C, wmOperator *UNUSED(unused))
957 {
958         SpaceFile *sfile = CTX_wm_space_file(C);
959         if (sfile->params) {
960                 if (!sfile->folders_next)
961                         sfile->folders_next = folderlist_new();
962
963                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
964                 folderlist_popdir(sfile->folders_next, sfile->params->dir);
965
966                 // update folders_prev so we can check for it in folderlist_clear_next()
967                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
968
969                 file_change_dir(C, 1);
970         }
971         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
972
973         return OPERATOR_FINISHED;
974 }
975
976
977 /* only meant for timer usage */
978 static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
979 {
980         ScrArea *sa = CTX_wm_area(C);
981         SpaceFile *sfile = CTX_wm_space_file(C);
982         ARegion *ar, *oldar = CTX_wm_region(C);
983         int offset;
984         int numfiles, numfiles_layout;
985         int edit_idx = 0;
986         int i;
987
988         /* escape if not our timer */
989         if (sfile->smoothscroll_timer == NULL || sfile->smoothscroll_timer != event->customdata)
990                 return OPERATOR_PASS_THROUGH;
991         
992         numfiles = filelist_numfiles(sfile->files);
993
994         /* check if we are editing a name */
995         for (i = 0; i < numfiles; ++i) {
996                 if (filelist_is_selected(sfile->files, i, CHECK_ALL) ) {
997                         edit_idx = i;
998                         break;
999                 }
1000         }
1001
1002         /* if we are not editing, we are done */
1003         if (0 == edit_idx) {
1004                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
1005                 sfile->smoothscroll_timer = NULL;
1006                 return OPERATOR_PASS_THROUGH;
1007         }
1008
1009         /* we need the correct area for scrolling */
1010         ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
1011         if (!ar || ar->regiontype != RGN_TYPE_WINDOW) {
1012                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
1013                 sfile->smoothscroll_timer = NULL;
1014                 return OPERATOR_PASS_THROUGH;
1015         }
1016
1017         offset = ED_fileselect_layout_offset(sfile->layout, (int)ar->v2d.cur.xmin, (int)-ar->v2d.cur.ymax);
1018         if (offset < 0) offset = 0;
1019
1020         /* scroll offset is the first file in the row/column we are editing in */
1021         if (sfile->scroll_offset == 0) {
1022                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
1023                         sfile->scroll_offset = (edit_idx / sfile->layout->rows) * sfile->layout->rows;
1024                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->rows;
1025                 }
1026                 else {
1027                         sfile->scroll_offset = (edit_idx / sfile->layout->columns) * sfile->layout->columns;
1028                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->columns;
1029                 }
1030         }
1031         
1032         numfiles_layout = ED_fileselect_layout_numfiles(sfile->layout, ar);
1033         
1034         /* check if we have reached our final scroll position */
1035         if ( (sfile->scroll_offset >= offset) && (sfile->scroll_offset < offset + numfiles_layout) ) {
1036                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
1037                 sfile->smoothscroll_timer = NULL;
1038                 return OPERATOR_FINISHED;
1039         }
1040
1041         /* temporarily set context to the main window region, 
1042          * so the scroll operators work */
1043         CTX_wm_region_set(C, ar);
1044         
1045         /* scroll one step in the desired direction */
1046         if (sfile->scroll_offset < offset) {
1047                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
1048                         WM_operator_name_call(C, "VIEW2D_OT_scroll_left", 0, NULL);
1049                 }
1050                 else {
1051                         WM_operator_name_call(C, "VIEW2D_OT_scroll_up", 0, NULL);
1052                 }
1053                 
1054         }
1055         else {
1056                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
1057                         WM_operator_name_call(C, "VIEW2D_OT_scroll_right", 0, NULL);
1058                 }
1059                 else {
1060                         WM_operator_name_call(C, "VIEW2D_OT_scroll_down", 0, NULL);
1061                 }
1062         }
1063         
1064         ED_region_tag_redraw(ar);
1065         
1066         /* and restore context */
1067         CTX_wm_region_set(C, oldar);
1068         
1069         return OPERATOR_FINISHED;
1070 }
1071
1072
1073 void FILE_OT_smoothscroll(wmOperatorType *ot)
1074 {
1075         
1076         /* identifiers */
1077         ot->name = "Smooth Scroll";
1078         ot->idname = "FILE_OT_smoothscroll";
1079         ot->description = "Smooth scroll to make editable file visible";
1080         
1081         /* api callbacks */
1082         ot->invoke = file_smoothscroll_invoke;
1083         
1084         ot->poll = ED_operator_file_active;
1085 }
1086
1087
1088 /* create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created.
1089  * The actual name is returned in 'name', 'folder' contains the complete path, including the new folder name.
1090  */
1091 static int new_folder_path(const char *parent, char *folder, char *name)
1092 {
1093         int i = 1;
1094         int len = 0;
1095
1096         BLI_strncpy(name, "New Folder", FILE_MAXFILE);
1097         BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
1098         /* check whether folder with the name already exists, in this case
1099          * add number to the name. Check length of generated name to avoid
1100          * crazy case of huge number of folders each named 'New Folder (x)' */
1101         while (BLI_exists(folder) && (len < FILE_MAXFILE)) {
1102                 len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
1103                 BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
1104                 i++;
1105         }
1106
1107         return (len < FILE_MAXFILE);
1108 }
1109
1110 int file_directory_new_exec(bContext *C, wmOperator *op)
1111 {
1112         char name[FILE_MAXFILE];
1113         char path[FILE_MAX];
1114         int generate_name = 1;
1115
1116         wmWindowManager *wm = CTX_wm_manager(C);
1117         SpaceFile *sfile = CTX_wm_space_file(C);
1118         
1119         if (!sfile->params) {
1120                 BKE_report(op->reports, RPT_WARNING, "No parent directory given");
1121                 return OPERATOR_CANCELLED;
1122         }
1123         
1124         path[0] = '\0';
1125
1126         if (RNA_struct_find_property(op->ptr, "directory")) {
1127                 RNA_string_get(op->ptr, "directory", path);
1128                 if (path[0] != '\0') generate_name = 0;
1129         }
1130
1131         if (generate_name) {
1132                 /* create a new, non-existing folder name */
1133                 if (!new_folder_path(sfile->params->dir, path, name)) {
1134                         BKE_report(op->reports, RPT_ERROR, "Could not create new folder name");
1135                         return OPERATOR_CANCELLED;
1136                 }
1137         }
1138
1139         /* create the file */
1140         BLI_dir_create_recursive(path);
1141
1142         if (!BLI_exists(path)) {
1143                 BKE_report(op->reports, RPT_ERROR, "Could not create new folder");
1144                 return OPERATOR_CANCELLED;
1145         }
1146
1147         /* now remember file to jump into editing */
1148         BLI_strncpy(sfile->params->renamefile, name, FILE_MAXFILE);
1149
1150         /* set timer to smoothly view newly generated file */
1151         sfile->smoothscroll_timer = WM_event_add_timer(wm, CTX_wm_window(C), TIMER1, 1.0 / 1000.0);  /* max 30 frs/sec */
1152         sfile->scroll_offset = 0;
1153
1154         /* reload dir to make sure we're seeing what's in the directory */
1155         ED_fileselect_clear(wm, sfile);
1156
1157         if (RNA_boolean_get(op->ptr, "open")) {
1158                 BLI_strncpy(sfile->params->dir, path, sizeof(sfile->params->dir));
1159                 file_change_dir(C, 1);
1160         }
1161
1162         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1163
1164         return OPERATOR_FINISHED;
1165 }
1166
1167
1168 void FILE_OT_directory_new(struct wmOperatorType *ot)
1169 {
1170         PropertyRNA *prop;
1171
1172         /* identifiers */
1173         ot->name = "Create New Directory";
1174         ot->description = "Create a new directory";
1175         ot->idname = "FILE_OT_directory_new";
1176         
1177         /* api callbacks */
1178         ot->invoke = WM_operator_confirm;
1179         ot->exec = file_directory_new_exec;
1180         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1181
1182         prop = RNA_def_string_dir_path(ot->srna, "directory", NULL, FILE_MAX, "Directory", "Name of new directory");
1183         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1184         prop = RNA_def_boolean(ot->srna, "open", false, "Open", "Open new directory");
1185         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1186 }
1187
1188
1189 static void file_expand_directory(bContext *C)
1190 {
1191         SpaceFile *sfile = CTX_wm_space_file(C);
1192         
1193         if (sfile->params) {
1194                 /* TODO, what about // when relbase isn't valid? */
1195                 if (G.relbase_valid && BLI_path_is_rel(sfile->params->dir)) {
1196                         BLI_path_abs(sfile->params->dir, G.main->name);
1197                 }
1198                 else if (sfile->params->dir[0] == '~') {
1199                         char tmpstr[sizeof(sfile->params->dir) - 1];
1200                         BLI_strncpy(tmpstr, sfile->params->dir + 1, sizeof(tmpstr));
1201                         BLI_join_dirfile(sfile->params->dir, sizeof(sfile->params->dir), BKE_appdir_folder_default(), tmpstr);
1202                 }
1203
1204                 else if (sfile->params->dir[0] == '\0')
1205 #ifndef WIN32
1206                 {
1207                         sfile->params->dir[0] = '/';
1208                         sfile->params->dir[1] = '\0';
1209                 }
1210 #else
1211                 {
1212                         get_default_root(sfile->params->dir);
1213                 }
1214                 /* change "C:" --> "C:\", [#28102] */
1215                 else if ((isalpha(sfile->params->dir[0]) &&
1216                           (sfile->params->dir[1] == ':')) &&
1217                          (sfile->params->dir[2] == '\0'))
1218                 {
1219                         sfile->params->dir[2] = '\\';
1220                         sfile->params->dir[3] = '\0';
1221                 }
1222                 else if (BLI_path_is_unc(sfile->params->dir)) {
1223                         BLI_cleanup_unc(sfile->params->dir, FILE_MAX_LIBEXTRA);
1224                 }
1225 #endif
1226         }
1227 }
1228
1229 #if defined(WIN32)
1230 static bool can_create_dir(const char *dir)
1231 {
1232         /* for UNC paths we need to check whether the parent of the new
1233          * directory is a proper directory itself and not a share or the
1234          * UNC root (server name) itself. Calling BLI_is_dir does this
1235          */
1236         if (BLI_path_is_unc(dir)) {
1237                 char parent[PATH_MAX];
1238                 BLI_strncpy(parent, dir, PATH_MAX);
1239                 BLI_parent_dir(parent);
1240                 return BLI_is_dir(parent);
1241         }
1242         return true;
1243 }
1244 #endif
1245
1246 void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UNUSED(arg_but))
1247 {
1248         SpaceFile *sfile = CTX_wm_space_file(C);
1249         
1250         if (sfile->params) {
1251                 file_expand_directory(C);
1252
1253                 /* special case, user may have pasted a filepath into the directory */
1254                 if (BLI_is_file(sfile->params->dir)) {
1255                         char path[sizeof(sfile->params->dir)];
1256                         BLI_strncpy(path, sfile->params->dir, sizeof(path));
1257                         BLI_split_dirfile(path, sfile->params->dir, sfile->params->file, sizeof(sfile->params->dir), sizeof(sfile->params->file));
1258                 }
1259
1260                 BLI_cleanup_dir(G.main->name, sfile->params->dir);
1261
1262                 if (BLI_exists(sfile->params->dir)) {
1263                         /* if directory exists, enter it immediately */
1264                         file_change_dir(C, 1);
1265
1266                         /* don't do for now because it selects entire text instead of
1267                          * placing cursor at the end */
1268                         /* UI_textbutton_activate_but(C, but); */
1269                 }
1270 #if defined(WIN32)
1271                 else if (!can_create_dir(sfile->params->dir)) {
1272                         const char *lastdir = folderlist_peeklastdir(sfile->folders_prev);
1273                         if (lastdir)
1274                                 BLI_strncpy(sfile->params->dir, lastdir, sizeof(sfile->params->dir));
1275                 }
1276 #endif
1277                 else {
1278                         const char *lastdir = folderlist_peeklastdir(sfile->folders_prev);
1279
1280                         /* if not, ask to create it and enter if confirmed */
1281                         wmOperatorType *ot = WM_operatortype_find("FILE_OT_directory_new", false);
1282                         PointerRNA ptr;
1283                         WM_operator_properties_create_ptr(&ptr, ot);
1284                         RNA_string_set(&ptr, "directory", sfile->params->dir);
1285                         RNA_boolean_set(&ptr, "open", true);
1286
1287                         if (lastdir)
1288                                 BLI_strncpy(sfile->params->dir, lastdir, sizeof(sfile->params->dir));
1289
1290
1291                         WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
1292                         WM_operator_properties_free(&ptr);
1293                 }
1294
1295                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1296         }
1297 }
1298
1299 void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg_but)
1300 {
1301         SpaceFile *sfile = CTX_wm_space_file(C);
1302         uiBut *but = arg_but;
1303         char matched_file[FILE_MAX];
1304         char filepath[sizeof(sfile->params->dir)];
1305
1306         if (sfile->params) {
1307                 int matches;
1308                 matched_file[0] = '\0';
1309                 filepath[0] = '\0';
1310
1311                 file_expand_directory(C);
1312
1313                 matches = file_select_match(sfile, sfile->params->file, matched_file);
1314
1315                 if (matches) {
1316                         /* int i, numfiles = filelist_numfiles(sfile->files); */ /* XXX UNUSED */
1317                         sfile->params->file[0] = '\0';
1318                         /* replace the pattern (or filename that the user typed in, with the first selected file of the match */
1319                         BLI_strncpy(sfile->params->file, matched_file, sizeof(sfile->params->file));
1320                         
1321                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
1322                 }
1323
1324                 if (matches == 1) {
1325
1326                         BLI_join_dirfile(filepath, sizeof(sfile->params->dir), sfile->params->dir, sfile->params->file);
1327
1328                         /* if directory, open it and empty filename field */
1329                         if (BLI_is_dir(filepath)) {
1330                                 BLI_cleanup_dir(G.main->name, filepath);
1331                                 BLI_strncpy(sfile->params->dir, filepath, sizeof(sfile->params->dir));
1332                                 sfile->params->file[0] = '\0';
1333                                 file_change_dir(C, 1);
1334                                 UI_textbutton_activate_but(C, but);
1335                                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
1336                         }
1337                         else if (sfile->params->type == FILE_LOADLIB) {
1338                                 char tdir[FILE_MAX], tgroup[FILE_MAX];
1339                                 BLI_add_slash(filepath);
1340                                 if (BLO_is_a_library(filepath, tdir, tgroup)) {
1341                                         BLI_cleanup_dir(G.main->name, filepath);
1342                                         BLI_strncpy(sfile->params->dir, filepath, sizeof(sfile->params->dir));
1343                                         sfile->params->file[0] = '\0';
1344                                         file_change_dir(C, 0);
1345                                         UI_textbutton_activate_but(C, but);
1346                                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1347                                 }
1348                         }
1349                 }
1350         }
1351 }
1352
1353 void FILE_OT_refresh(struct wmOperatorType *ot)
1354 {
1355         /* identifiers */
1356         ot->name = "Refresh Filelist";
1357         ot->description = "Refresh the file list";
1358         ot->idname = "FILE_OT_refresh";
1359         
1360         /* api callbacks */
1361         ot->exec = file_refresh_exec;
1362         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1363 }
1364
1365 static int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused))
1366 {
1367         wmWindowManager *wm = CTX_wm_manager(C);
1368         SpaceFile *sfile = CTX_wm_space_file(C);
1369         
1370         if (sfile->params) {
1371                 sfile->params->flag ^= FILE_HIDE_DOT;
1372                 ED_fileselect_clear(wm, sfile);
1373                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1374         }
1375         
1376         return OPERATOR_FINISHED;
1377 }
1378
1379
1380 void FILE_OT_hidedot(struct wmOperatorType *ot)
1381 {
1382         /* identifiers */
1383         ot->name = "Toggle Hide Dot Files";
1384         ot->description = "Toggle hide hidden dot files";
1385         ot->idname = "FILE_OT_hidedot";
1386         
1387         /* api callbacks */
1388         ot->exec = file_hidedot_exec;
1389         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1390 }
1391
1392 ARegion *file_buttons_region(ScrArea *sa)
1393 {
1394         ARegion *ar, *arnew;
1395         
1396         for (ar = sa->regionbase.first; ar; ar = ar->next)
1397                 if (ar->regiontype == RGN_TYPE_CHANNELS)
1398                         return ar;
1399
1400         /* add subdiv level; after header */
1401         for (ar = sa->regionbase.first; ar; ar = ar->next)
1402                 if (ar->regiontype == RGN_TYPE_HEADER)
1403                         break;
1404         
1405         /* is error! */
1406         if (ar == NULL) return NULL;
1407         
1408         arnew = MEM_callocN(sizeof(ARegion), "buttons for file panels");
1409         
1410         BLI_insertlinkafter(&sa->regionbase, ar, arnew);
1411         arnew->regiontype = RGN_TYPE_CHANNELS;
1412         arnew->alignment = RGN_ALIGN_LEFT;
1413         
1414         arnew->flag = RGN_FLAG_HIDDEN;
1415         
1416         return arnew;
1417 }
1418
1419 static int file_bookmark_toggle_exec(bContext *C, wmOperator *UNUSED(unused))
1420 {
1421         ScrArea *sa = CTX_wm_area(C);
1422         ARegion *ar = file_buttons_region(sa);
1423         
1424         if (ar)
1425                 ED_region_toggle_hidden(C, ar);
1426
1427         return OPERATOR_FINISHED;
1428 }
1429
1430 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
1431 {
1432         /* identifiers */
1433         ot->name = "Toggle Bookmarks";
1434         ot->description = "Toggle bookmarks display";
1435         ot->idname = "FILE_OT_bookmark_toggle";
1436         
1437         /* api callbacks */
1438         ot->exec = file_bookmark_toggle_exec;
1439         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1440 }
1441
1442
1443 static int file_filenum_exec(bContext *C, wmOperator *op)
1444 {
1445         SpaceFile *sfile = CTX_wm_space_file(C);
1446         ScrArea *sa = CTX_wm_area(C);
1447         
1448         int inc = RNA_int_get(op->ptr, "increment");
1449         if (sfile->params && (inc != 0)) {
1450                 BLI_newname(sfile->params->file, inc);
1451                 ED_area_tag_redraw(sa);
1452                 file_draw_check_cb(C, NULL, NULL);
1453                 // WM_event_add_notifier(C, NC_WINDOW, NULL);
1454         }
1455         
1456         return OPERATOR_FINISHED;
1457
1458 }
1459
1460 void FILE_OT_filenum(struct wmOperatorType *ot)
1461 {
1462         /* identifiers */
1463         ot->name = "Increment Number in Filename";
1464         ot->description = "Increment number in filename";
1465         ot->idname = "FILE_OT_filenum";
1466         
1467         /* api callbacks */
1468         ot->exec = file_filenum_exec;
1469         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1470
1471         /* props */
1472         RNA_def_int(ot->srna, "increment", 1, -100, 100, "Increment", "", -100, 100);
1473 }
1474
1475 static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
1476 {
1477         ScrArea *sa = CTX_wm_area(C);
1478         SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
1479         
1480         if (sfile->params) {
1481                 int idx = sfile->params->active_file;
1482                 int numfiles = filelist_numfiles(sfile->files);
1483                 if ( (0 <= idx) && (idx < numfiles) ) {
1484                         struct direntry *file = filelist_file(sfile->files, idx);
1485                         filelist_select_file(sfile->files, idx, FILE_SEL_ADD, EDITING_FILE, CHECK_ALL);
1486                         BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE);
1487                         sfile->params->renamefile[0] = '\0';
1488                 }
1489                 ED_area_tag_redraw(sa);
1490         }
1491         
1492         return OPERATOR_FINISHED;
1493
1494 }
1495
1496 static int file_rename_poll(bContext *C)
1497 {
1498         int poll = ED_operator_file_active(C);
1499         SpaceFile *sfile = CTX_wm_space_file(C);
1500
1501         if (sfile && sfile->params) {
1502                 int idx = sfile->params->active_file;
1503
1504                 if (idx >= 0) {
1505                         struct direntry *file = filelist_file(sfile->files, idx);
1506                         if (STREQ(file->relname, "..") || STREQ(file->relname, ".")) {
1507                                 poll = 0;
1508                         }
1509                 }
1510
1511                 if (sfile->params->active_file < 0) {
1512                         poll = 0;
1513                 }
1514                 else {
1515                         char dir[FILE_MAX], group[FILE_MAX];
1516                         if (filelist_islibrary(sfile->files, dir, group)) poll = 0;
1517                 }
1518         }
1519         else
1520                 poll = 0;
1521         return poll;
1522 }
1523
1524 void FILE_OT_rename(struct wmOperatorType *ot)
1525 {
1526         /* identifiers */
1527         ot->name = "Rename File or Directory";
1528         ot->description = "Rename file or file directory";
1529         ot->idname = "FILE_OT_rename";
1530         
1531         /* api callbacks */
1532         ot->exec = file_rename_exec;
1533         ot->poll = file_rename_poll; 
1534
1535 }
1536
1537 static int file_delete_poll(bContext *C)
1538 {
1539         int poll = ED_operator_file_active(C);
1540         SpaceFile *sfile = CTX_wm_space_file(C);
1541
1542         if (sfile && sfile->params) {
1543                 char dir[FILE_MAX], group[FILE_MAX];
1544                 int numfiles = filelist_numfiles(sfile->files);
1545                 int i;
1546                 int num_selected = 0;
1547
1548                 if (filelist_islibrary(sfile->files, dir, group)) poll = 0;
1549                 for (i = 0; i < numfiles; i++) {
1550                         if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
1551                                 num_selected++;
1552                         }
1553                 }
1554                 if (num_selected <= 0) {
1555                         poll = 0;
1556                 }
1557         }
1558         else
1559                 poll = 0;
1560                 
1561         return poll;
1562 }
1563
1564 int file_delete_exec(bContext *C, wmOperator *UNUSED(op))
1565 {
1566         char str[FILE_MAX];
1567         wmWindowManager *wm = CTX_wm_manager(C);
1568         SpaceFile *sfile = CTX_wm_space_file(C);
1569         struct direntry *file;  
1570         int numfiles = filelist_numfiles(sfile->files);
1571         int i;
1572
1573         for (i = 0; i < numfiles; i++) {
1574                 if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
1575                         file = filelist_file(sfile->files, i);
1576                         BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relname);
1577                         BLI_delete(str, false, false);
1578                 }
1579         }
1580         
1581         ED_fileselect_clear(wm, sfile);
1582         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1583         
1584         return OPERATOR_FINISHED;
1585
1586 }
1587
1588 void FILE_OT_delete(struct wmOperatorType *ot)
1589 {
1590         /* identifiers */
1591         ot->name = "Delete Selected Files";
1592         ot->description = "Delete selected files";
1593         ot->idname = "FILE_OT_delete";
1594         
1595         /* api callbacks */
1596         ot->invoke = WM_operator_confirm;
1597         ot->exec = file_delete_exec;
1598         ot->poll = file_delete_poll; /* <- important, handler is on window level */
1599 }
1600
1601
1602 void ED_operatormacros_file(void)
1603 {
1604 //      wmOperatorType *ot;
1605 //      wmOperatorTypeMacro *otmacro;
1606         
1607         /* future macros */
1608 }