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