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