code cleanup:
[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         wmWindowManager *wm = CTX_wm_manager(C);
616         SpaceFile *sfile = CTX_wm_space_file(C);
617         wmOperator *op = sfile->op;
618         
619         sfile->op = NULL;
620
621         WM_event_fileselect_event(wm, op, EVT_FILESELECT_CANCEL);
622         
623         return OPERATOR_FINISHED;
624 }
625
626 static int file_operator_poll(bContext *C)
627 {
628         int poll = ED_operator_file_active(C);
629         SpaceFile *sfile = CTX_wm_space_file(C);
630
631         if (!sfile || !sfile->op) poll = 0;
632
633         return poll;
634 }
635
636 void FILE_OT_cancel(struct wmOperatorType *ot)
637 {
638         /* identifiers */
639         ot->name = "Cancel File Load";
640         ot->description = "Cancel loading of selected file";
641         ot->idname = "FILE_OT_cancel";
642         
643         /* api callbacks */
644         ot->exec = file_cancel_exec;
645         ot->poll = file_operator_poll;
646 }
647
648
649 void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
650 {
651         BLI_join_dirfile(filepath, FILE_MAX, sfile->params->dir, sfile->params->file); /* XXX, not real length */
652         if (RNA_struct_find_property(op->ptr, "relative_path")) {
653                 if (RNA_boolean_get(op->ptr, "relative_path")) {
654                         BLI_path_rel(filepath, G.main->name);
655                 }
656         }
657
658         if (RNA_struct_find_property(op->ptr, "filename")) {
659                 RNA_string_set(op->ptr, "filename", sfile->params->file);
660         }
661         if (RNA_struct_find_property(op->ptr, "directory")) {
662                 RNA_string_set(op->ptr, "directory", sfile->params->dir);
663         }
664         if (RNA_struct_find_property(op->ptr, "filepath")) {
665                 RNA_string_set(op->ptr, "filepath", filepath);
666         }
667         
668         /* some ops have multiple files to select */
669         /* this is called on operators check() so clear collections first since
670          * they may be already set. */
671         {
672                 PointerRNA itemptr;
673                 PropertyRNA *prop_files = RNA_struct_find_property(op->ptr, "files");
674                 PropertyRNA *prop_dirs = RNA_struct_find_property(op->ptr, "dirs");
675                 int i, numfiles = filelist_numfiles(sfile->files);
676
677                 if (prop_files) {
678                         int num_files = 0;
679                         RNA_property_collection_clear(op->ptr, prop_files);
680                         for (i = 0; i < numfiles; i++) {
681                                 if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
682                                         struct direntry *file = filelist_file(sfile->files, i);
683                                         RNA_property_collection_add(op->ptr, prop_files, &itemptr);
684                                         RNA_string_set(&itemptr, "name", file->relname);
685                                         num_files++;
686                                 }
687                         }
688                         /* make sure the file specified in the filename button is added even if no files selected */
689                         if (0 == num_files) {
690                                 RNA_property_collection_add(op->ptr, prop_files, &itemptr);
691                                 RNA_string_set(&itemptr, "name", sfile->params->file);
692                         }
693                 }
694
695                 if (prop_dirs) {
696                         int num_dirs = 0;
697                         RNA_property_collection_clear(op->ptr, prop_dirs);
698                         for (i = 0; i < numfiles; i++) {
699                                 if (filelist_is_selected(sfile->files, i, CHECK_DIRS)) {
700                                         struct direntry *file = filelist_file(sfile->files, i);
701                                         RNA_property_collection_add(op->ptr, prop_dirs, &itemptr);
702                                         RNA_string_set(&itemptr, "name", file->relname);
703                                         num_dirs++;
704                                 }
705                         }
706                         
707                         /* make sure the directory specified in the button is added even if no directory selected */
708                         if (0 == num_dirs) {
709                                 RNA_property_collection_add(op->ptr, prop_dirs, &itemptr);
710                                 RNA_string_set(&itemptr, "name", sfile->params->dir);
711                         }
712                 }
713
714
715         }
716 }
717
718 void file_operator_to_sfile(SpaceFile *sfile, wmOperator *op)
719 {
720         PropertyRNA *prop;
721
722         /* If neither of the above are set, split the filepath back */
723         if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
724                 char filepath[FILE_MAX];
725                 RNA_property_string_get(op->ptr, prop, filepath);
726                 BLI_split_dirfile(filepath, sfile->params->dir, sfile->params->file, sizeof(sfile->params->dir), sizeof(sfile->params->file));
727         }
728         else {
729                 if ((prop = RNA_struct_find_property(op->ptr, "filename"))) {
730                         RNA_property_string_get(op->ptr, prop, sfile->params->file);
731                 }
732                 if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
733                         RNA_property_string_get(op->ptr, prop, sfile->params->dir);
734                 }
735         }
736         
737         /* we could check for relative_path property which is used when converting
738          * in the other direction but doesnt hurt to do this every time */
739         BLI_path_abs(sfile->params->dir, G.main->name);
740
741         /* XXX, files and dirs updates missing, not really so important though */
742 }
743
744 void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2))
745 {
746         SpaceFile *sfile = CTX_wm_space_file(C);
747         wmOperator *op = sfile->op;
748         if (op) { /* fail on reload */
749                 if (op->type->check) {
750                         char filepath[FILE_MAX];
751                         file_sfile_to_operator(op, sfile, filepath);
752                         
753                         /* redraw */
754                         if (op->type->check(C, op)) {
755                                 file_operator_to_sfile(sfile, op);
756         
757                                 /* redraw, else the changed settings wont get updated */
758                                 ED_area_tag_redraw(CTX_wm_area(C));
759                         }
760                 }
761         }
762 }
763
764 int file_draw_check_exists(SpaceFile *sfile)
765 {
766         if (sfile->op) { /* fails on reload */
767                 if (RNA_struct_find_property(sfile->op->ptr, "check_existing")) {
768                         if (RNA_boolean_get(sfile->op->ptr, "check_existing")) {
769                                 char filepath[FILE_MAX];
770                                 BLI_join_dirfile(filepath, sizeof(filepath), sfile->params->dir, sfile->params->file);
771                                 if (BLI_is_file(filepath)) {
772                                         return TRUE;
773                                 }
774                         }
775                 }
776         }
777
778         return FALSE;
779 }
780
781 /* sends events now, so things get handled on windowqueue level */
782 int file_exec(bContext *C, wmOperator *exec_op)
783 {
784         wmWindowManager *wm = CTX_wm_manager(C);
785         SpaceFile *sfile = CTX_wm_space_file(C);
786         char filepath[FILE_MAX];
787         
788         if (sfile->op) {
789                 wmOperator *op = sfile->op;
790         
791                 /* when used as a macro, for doubleclick, 
792                  * to prevent closing when doubleclicking on .. item */
793                 if (RNA_boolean_get(exec_op->ptr, "need_active")) {
794                         int i, active = 0;
795                         
796                         for (i = 0; i < filelist_numfiles(sfile->files); i++) {
797                                 if (filelist_is_selected(sfile->files, i, CHECK_ALL)) {
798                                         active = 1;
799                                         break;
800                                 }
801                         }
802                         if (active == 0)
803                                 return OPERATOR_CANCELLED;
804                 }
805                 
806                 sfile->op = NULL;
807
808                 file_sfile_to_operator(op, sfile, filepath);
809
810                 if (BLI_exists(sfile->params->dir)) {
811                         fsmenu_insert_entry(fsmenu_get(), FS_CATEGORY_RECENT, sfile->params->dir, FS_INSERT_SAVE | FS_INSERT_FIRST);
812                 }
813
814                 BLI_make_file_string(G.main->name, filepath, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
815                 fsmenu_write_file(fsmenu_get(), filepath);
816                 WM_event_fileselect_event(wm, op, EVT_FILESELECT_EXEC);
817
818         }
819                                 
820         return OPERATOR_FINISHED;
821 }
822
823 void FILE_OT_execute(struct wmOperatorType *ot)
824 {
825         PropertyRNA *prop;
826
827         /* identifiers */
828         ot->name = "Execute File Window";
829         ot->description = "Execute selected file";
830         ot->idname = "FILE_OT_execute";
831         
832         /* api callbacks */
833         ot->exec = file_exec;
834         ot->poll = file_operator_poll; 
835
836         /* properties */
837         prop = RNA_def_boolean(ot->srna, "need_active", 0, "Need Active",
838                                "Only execute if there's an active selected file in the file list");
839         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
840 }
841
842
843 int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
844 {
845         SpaceFile *sfile = CTX_wm_space_file(C);
846         
847         if (sfile->params) {
848                 if (BLI_has_parent(sfile->params->dir)) {
849                         BLI_parent_dir(sfile->params->dir);
850                         BLI_cleanup_dir(G.main->name, sfile->params->dir);
851                         file_change_dir(C, 0);
852                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
853                 }
854         }
855         
856         return OPERATOR_FINISHED;
857
858 }
859
860
861 void FILE_OT_parent(struct wmOperatorType *ot)
862 {
863         /* identifiers */
864         ot->name = "Parent File";
865         ot->description = "Move to parent directory";
866         ot->idname = "FILE_OT_parent";
867         
868         /* api callbacks */
869         ot->exec = file_parent_exec;
870         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
871 }
872
873
874 static int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
875 {
876         wmWindowManager *wm = CTX_wm_manager(C);
877         SpaceFile *sfile = CTX_wm_space_file(C);
878         struct FSMenu *fsmenu = fsmenu_get();
879
880         ED_fileselect_clear(wm, sfile);
881
882         /* refresh system directory menu */
883         fsmenu_refresh_system_category(fsmenu);
884
885         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
886
887         return OPERATOR_FINISHED;
888
889 }
890
891 void FILE_OT_previous(struct wmOperatorType *ot)
892 {
893         /* identifiers */
894         ot->name = "Previous Folder";
895         ot->description = "Move to previous folder";
896         ot->idname = "FILE_OT_previous";
897         
898         /* api callbacks */
899         ot->exec = file_previous_exec;
900         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
901 }
902
903 int file_previous_exec(bContext *C, wmOperator *UNUSED(unused))
904 {
905         SpaceFile *sfile = CTX_wm_space_file(C);
906
907         if (sfile->params) {
908                 if (!sfile->folders_next)
909                         sfile->folders_next = folderlist_new();
910
911                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
912                 folderlist_popdir(sfile->folders_prev, sfile->params->dir);
913                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
914
915                 file_change_dir(C, 1);
916         }
917         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
918
919         return OPERATOR_FINISHED;
920 }
921
922 void FILE_OT_next(struct wmOperatorType *ot)
923 {
924         /* identifiers */
925         ot->name = "Next Folder";
926         ot->description = "Move to next folder";
927         ot->idname = "FILE_OT_next";
928         
929         /* api callbacks */
930         ot->exec = file_next_exec;
931         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
932 }
933
934 int file_next_exec(bContext *C, wmOperator *UNUSED(unused))
935 {
936         SpaceFile *sfile = CTX_wm_space_file(C);
937         if (sfile->params) {
938                 if (!sfile->folders_next)
939                         sfile->folders_next = folderlist_new();
940
941                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
942                 folderlist_popdir(sfile->folders_next, sfile->params->dir);
943
944                 // update folders_prev so we can check for it in folderlist_clear_next()
945                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
946
947                 file_change_dir(C, 1);
948         }
949         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
950
951         return OPERATOR_FINISHED;
952 }
953
954
955 /* only meant for timer usage */
956 static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
957 {
958         ScrArea *sa = CTX_wm_area(C);
959         SpaceFile *sfile = CTX_wm_space_file(C);
960         ARegion *ar, *oldar = CTX_wm_region(C);
961         int offset;
962         int numfiles, numfiles_layout;
963         int edit_idx = 0;
964         int i;
965
966         /* escape if not our timer */
967         if (sfile->smoothscroll_timer == NULL || sfile->smoothscroll_timer != event->customdata)
968                 return OPERATOR_PASS_THROUGH;
969         
970         numfiles = filelist_numfiles(sfile->files);
971
972         /* check if we are editing a name */
973         for (i = 0; i < numfiles; ++i) {
974                 if (filelist_is_selected(sfile->files, i, CHECK_ALL) ) {
975                         edit_idx = i;
976                         break;
977                 }
978         }
979
980         /* if we are not editing, we are done */
981         if (0 == edit_idx) {
982                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
983                 sfile->smoothscroll_timer = NULL;
984                 return OPERATOR_PASS_THROUGH;
985         }
986
987         /* we need the correct area for scrolling */
988         ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
989         if (!ar || ar->regiontype != RGN_TYPE_WINDOW) {
990                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
991                 sfile->smoothscroll_timer = NULL;
992                 return OPERATOR_PASS_THROUGH;
993         }
994
995         offset = ED_fileselect_layout_offset(sfile->layout, (int)ar->v2d.cur.xmin, (int)-ar->v2d.cur.ymax);
996         if (offset < 0) offset = 0;
997
998         /* scroll offset is the first file in the row/column we are editing in */
999         if (sfile->scroll_offset == 0) {
1000                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
1001                         sfile->scroll_offset = (edit_idx / sfile->layout->rows) * sfile->layout->rows;
1002                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->rows;
1003                 }
1004                 else {
1005                         sfile->scroll_offset = (edit_idx / sfile->layout->columns) * sfile->layout->columns;
1006                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->columns;
1007                 }
1008         }
1009         
1010         numfiles_layout = ED_fileselect_layout_numfiles(sfile->layout, ar);
1011         
1012         /* check if we have reached our final scroll position */
1013         if ( (sfile->scroll_offset >= offset) && (sfile->scroll_offset < offset + numfiles_layout) ) {
1014                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
1015                 sfile->smoothscroll_timer = NULL;
1016                 return OPERATOR_FINISHED;
1017         }
1018
1019         /* temporarily set context to the main window region, 
1020          * so the scroll operators work */
1021         CTX_wm_region_set(C, ar);
1022         
1023         /* scroll one step in the desired direction */
1024         if (sfile->scroll_offset < offset) {
1025                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
1026                         WM_operator_name_call(C, "VIEW2D_OT_scroll_left", 0, NULL);
1027                 }
1028                 else {
1029                         WM_operator_name_call(C, "VIEW2D_OT_scroll_up", 0, NULL);
1030                 }
1031                 
1032         }
1033         else {
1034                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
1035                         WM_operator_name_call(C, "VIEW2D_OT_scroll_right", 0, NULL);
1036                 }
1037                 else {
1038                         WM_operator_name_call(C, "VIEW2D_OT_scroll_down", 0, NULL);
1039                 }
1040         }
1041         
1042         ED_region_tag_redraw(CTX_wm_region(C));
1043         
1044         /* and restore context */
1045         CTX_wm_region_set(C, oldar);
1046         
1047         return OPERATOR_FINISHED;
1048 }
1049
1050
1051 void FILE_OT_smoothscroll(wmOperatorType *ot)
1052 {
1053         
1054         /* identifiers */
1055         ot->name = "Smooth Scroll";
1056         ot->idname = "FILE_OT_smoothscroll";
1057         ot->description = "Smooth scroll to make editable file visible";
1058         
1059         /* api callbacks */
1060         ot->invoke = file_smoothscroll_invoke;
1061         
1062         ot->poll = ED_operator_file_active;
1063 }
1064
1065
1066 /* create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created.
1067  * The actual name is returned in 'name', 'folder' contains the complete path, including the new folder name.
1068  */
1069 static int new_folder_path(const char *parent, char *folder, char *name)
1070 {
1071         int i = 1;
1072         int len = 0;
1073
1074         BLI_strncpy(name, "New Folder", FILE_MAXFILE);
1075         BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
1076         /* check whether folder with the name already exists, in this case
1077          * add number to the name. Check length of generated name to avoid
1078          * crazy case of huge number of folders each named 'New Folder (x)' */
1079         while (BLI_exists(folder) && (len < FILE_MAXFILE)) {
1080                 len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
1081                 BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
1082                 i++;
1083         }
1084
1085         return (len < FILE_MAXFILE);
1086 }
1087
1088 int file_directory_new_exec(bContext *C, wmOperator *op)
1089 {
1090         char name[FILE_MAXFILE];
1091         char path[FILE_MAX];
1092         int generate_name = 1;
1093
1094         wmWindowManager *wm = CTX_wm_manager(C);
1095         SpaceFile *sfile = CTX_wm_space_file(C);
1096         
1097         if (!sfile->params) {
1098                 BKE_report(op->reports, RPT_WARNING, "No parent directory given");
1099                 return OPERATOR_CANCELLED;
1100         }
1101         
1102         path[0] = '\0';
1103
1104         if (RNA_struct_find_property(op->ptr, "directory")) {
1105                 RNA_string_get(op->ptr, "directory", path);
1106                 if (path[0] != '\0') generate_name = 0;
1107         }
1108
1109         if (generate_name) {
1110                 /* create a new, non-existing folder name */
1111                 if (!new_folder_path(sfile->params->dir, path, name)) {
1112                         BKE_report(op->reports, RPT_ERROR, "Could not create new folder name");
1113                         return OPERATOR_CANCELLED;
1114                 }
1115         }
1116
1117         /* create the file */
1118         BLI_dir_create_recursive(path);
1119
1120         if (!BLI_exists(path)) {
1121                 BKE_report(op->reports, RPT_ERROR, "Could not create new folder");
1122                 return OPERATOR_CANCELLED;
1123         }
1124
1125         /* now remember file to jump into editing */
1126         BLI_strncpy(sfile->params->renamefile, name, FILE_MAXFILE);
1127
1128         /* set timer to smoothly view newly generated file */
1129         sfile->smoothscroll_timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER1, 1.0 / 1000.0);    /* max 30 frs/sec */
1130         sfile->scroll_offset = 0;
1131
1132         /* reload dir to make sure we're seeing what's in the directory */
1133         ED_fileselect_clear(wm, sfile);
1134         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1135
1136         return OPERATOR_FINISHED;
1137 }
1138
1139
1140 void FILE_OT_directory_new(struct wmOperatorType *ot)
1141 {
1142         PropertyRNA *prop;
1143
1144         /* identifiers */
1145         ot->name = "Create New Directory";
1146         ot->description = "Create a new directory";
1147         ot->idname = "FILE_OT_directory_new";
1148         
1149         /* api callbacks */
1150         ot->invoke = WM_operator_confirm;
1151         ot->exec = file_directory_new_exec;
1152         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1153
1154         prop = RNA_def_string_dir_path(ot->srna, "directory", "", FILE_MAX, "Directory", "Name of new directory");
1155         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1156
1157 }
1158
1159
1160 static void file_expand_directory(bContext *C)
1161 {
1162         SpaceFile *sfile = CTX_wm_space_file(C);
1163         
1164         if (sfile->params) {
1165                 /* TODO, what about // when relbase isn't valid? */
1166                 if (G.relbase_valid && BLI_path_is_rel(sfile->params->dir)) {
1167                         BLI_path_abs(sfile->params->dir, G.main->name);
1168                 }
1169                 else if (sfile->params->dir[0] == '~') {
1170                         char tmpstr[sizeof(sfile->params->dir) - 1];
1171                         BLI_strncpy(tmpstr, sfile->params->dir + 1, sizeof(tmpstr));
1172                         BLI_join_dirfile(sfile->params->dir, sizeof(sfile->params->dir), BLI_getDefaultDocumentFolder(), tmpstr);
1173                 }
1174
1175                 else if (sfile->params->dir[0] == '\0')
1176 #ifndef WIN32
1177                 {
1178                         sfile->params->dir[0] = '/';
1179                         sfile->params->dir[1] = '\0';
1180                 }
1181 #else
1182                 {
1183                         get_default_root(sfile->params->dir);
1184                 }
1185                 /* change "C:" --> "C:\", [#28102] */
1186                 else if ((isalpha(sfile->params->dir[0]) &&
1187                           (sfile->params->dir[1] == ':')) &&
1188                          (sfile->params->dir[2] == '\0'))
1189                 {
1190                         sfile->params->dir[2] = '\\';
1191                         sfile->params->dir[3] = '\0';
1192                 }
1193 #endif
1194         }
1195 }
1196
1197 static int file_directory_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1198 {
1199         SpaceFile *sfile = CTX_wm_space_file(C);
1200
1201         if (sfile->params) {
1202                 file_expand_directory(C);
1203                 
1204                 if (!BLI_exists(sfile->params->dir)) {
1205                         return WM_operator_confirm_message(C, op, "Create new directory?");
1206                 }
1207
1208                 return file_directory_exec(C, op);
1209         }
1210
1211         return OPERATOR_CANCELLED;
1212 }
1213
1214
1215
1216 int file_directory_exec(bContext *C, wmOperator *UNUSED(unused))
1217 {
1218         SpaceFile *sfile = CTX_wm_space_file(C);
1219         
1220         if (sfile->params) {
1221                 file_expand_directory(C);
1222
1223                 if (!BLI_exists(sfile->params->dir)) {
1224                         BLI_dir_create_recursive(sfile->params->dir);
1225                 }
1226
1227                 /* special case, user may have pasted a filepath into the directory */
1228                 if (BLI_is_file(sfile->params->dir)) {
1229                         char path[sizeof(sfile->params->dir)];
1230                         BLI_strncpy(path, sfile->params->dir, sizeof(path));
1231                         BLI_split_dirfile(path, sfile->params->dir, sfile->params->file, sizeof(sfile->params->dir), sizeof(sfile->params->file));
1232                 }
1233
1234                 BLI_cleanup_dir(G.main->name, sfile->params->dir);
1235                 file_change_dir(C, 1);
1236
1237                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1238         }
1239
1240         return OPERATOR_FINISHED;
1241 }
1242
1243 int file_filename_exec(bContext *C, wmOperator *UNUSED(unused))
1244 {
1245         SpaceFile *sfile = CTX_wm_space_file(C);
1246         char matched_file[FILE_MAX];
1247         if (sfile->params) {
1248                 matched_file[0] = '\0';
1249                 if (file_select_match(sfile, sfile->params->file, matched_file)) {
1250                         /* int i, numfiles = filelist_numfiles(sfile->files); */ /* XXX UNUSED */
1251                         sfile->params->file[0] = '\0';
1252                         /* replace the pattern (or filename that the user typed in, with the first selected file of the match */
1253                         BLI_strncpy(sfile->params->file, matched_file, sizeof(sfile->params->file));
1254                         
1255                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
1256                 }
1257         }
1258
1259         return OPERATOR_FINISHED;
1260 }
1261
1262 /* TODO, directory operator is non-functional while a library is loaded
1263  * until this is properly supported just disable it. */
1264 static int file_directory_poll(bContext *C)
1265 {
1266         /* sfile->files can be NULL on file load */
1267         SpaceFile *sfile = CTX_wm_space_file(C);
1268         return ED_operator_file_active(C) && (sfile->files == NULL || filelist_lib(sfile->files) == NULL);
1269 }
1270
1271 void FILE_OT_directory(struct wmOperatorType *ot)
1272 {
1273         /* identifiers */
1274         ot->name = "Enter Directory Name";
1275         ot->description = "Enter a directory name";
1276         ot->idname = "FILE_OT_directory";
1277         
1278         /* api callbacks */
1279         ot->invoke = file_directory_invoke;
1280         ot->exec = file_directory_exec;
1281         ot->poll = file_directory_poll; /* <- important, handler is on window level */
1282 }
1283
1284 void FILE_OT_refresh(struct wmOperatorType *ot)
1285 {
1286         /* identifiers */
1287         ot->name = "Refresh Filelist";
1288         ot->description = "Refresh the file list";
1289         ot->idname = "FILE_OT_refresh";
1290         
1291         /* api callbacks */
1292         ot->exec = file_refresh_exec;
1293         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1294 }
1295
1296 static int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused))
1297 {
1298         wmWindowManager *wm = CTX_wm_manager(C);
1299         SpaceFile *sfile = CTX_wm_space_file(C);
1300         
1301         if (sfile->params) {
1302                 sfile->params->flag ^= FILE_HIDE_DOT;
1303                 ED_fileselect_clear(wm, sfile);
1304                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1305         }
1306         
1307         return OPERATOR_FINISHED;
1308 }
1309
1310
1311 void FILE_OT_hidedot(struct wmOperatorType *ot)
1312 {
1313         /* identifiers */
1314         ot->name = "Toggle Hide Dot Files";
1315         ot->description = "Toggle hide hidden dot files";
1316         ot->idname = "FILE_OT_hidedot";
1317         
1318         /* api callbacks */
1319         ot->exec = file_hidedot_exec;
1320         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1321 }
1322
1323 ARegion *file_buttons_region(ScrArea *sa)
1324 {
1325         ARegion *ar, *arnew;
1326         
1327         for (ar = sa->regionbase.first; ar; ar = ar->next)
1328                 if (ar->regiontype == RGN_TYPE_CHANNELS)
1329                         return ar;
1330
1331         /* add subdiv level; after header */
1332         for (ar = sa->regionbase.first; ar; ar = ar->next)
1333                 if (ar->regiontype == RGN_TYPE_HEADER)
1334                         break;
1335         
1336         /* is error! */
1337         if (ar == NULL) return NULL;
1338         
1339         arnew = MEM_callocN(sizeof(ARegion), "buttons for file panels");
1340         
1341         BLI_insertlinkafter(&sa->regionbase, ar, arnew);
1342         arnew->regiontype = RGN_TYPE_CHANNELS;
1343         arnew->alignment = RGN_ALIGN_LEFT;
1344         
1345         arnew->flag = RGN_FLAG_HIDDEN;
1346         
1347         return arnew;
1348 }
1349
1350 static int file_bookmark_toggle_exec(bContext *C, wmOperator *UNUSED(unused))
1351 {
1352         ScrArea *sa = CTX_wm_area(C);
1353         ARegion *ar = file_buttons_region(sa);
1354         
1355         if (ar)
1356                 ED_region_toggle_hidden(C, ar);
1357
1358         return OPERATOR_FINISHED;
1359 }
1360
1361 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
1362 {
1363         /* identifiers */
1364         ot->name = "Toggle Bookmarks";
1365         ot->description = "Toggle bookmarks display";
1366         ot->idname = "FILE_OT_bookmark_toggle";
1367         
1368         /* api callbacks */
1369         ot->exec = file_bookmark_toggle_exec;
1370         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1371 }
1372
1373
1374 static int file_filenum_exec(bContext *C, wmOperator *op)
1375 {
1376         SpaceFile *sfile = CTX_wm_space_file(C);
1377         ScrArea *sa = CTX_wm_area(C);
1378         
1379         int inc = RNA_int_get(op->ptr, "increment");
1380         if (sfile->params && (inc != 0)) {
1381                 BLI_newname(sfile->params->file, inc);
1382                 ED_area_tag_redraw(sa);
1383                 file_draw_check_cb(C, NULL, NULL);
1384                 // WM_event_add_notifier(C, NC_WINDOW, NULL);
1385         }
1386         
1387         return OPERATOR_FINISHED;
1388
1389 }
1390
1391 void FILE_OT_filenum(struct wmOperatorType *ot)
1392 {
1393         /* identifiers */
1394         ot->name = "Increment Number in Filename";
1395         ot->description = "Increment number in filename";
1396         ot->idname = "FILE_OT_filenum";
1397         
1398         /* api callbacks */
1399         ot->exec = file_filenum_exec;
1400         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1401
1402         /* props */
1403         RNA_def_int(ot->srna, "increment", 1, -100, 100, "Increment", "", -100, 100);
1404 }
1405
1406 static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
1407 {
1408         ScrArea *sa = CTX_wm_area(C);
1409         SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
1410         
1411         if (sfile->params) {
1412                 int idx = sfile->params->active_file;
1413                 int numfiles = filelist_numfiles(sfile->files);
1414                 if ( (0 <= idx) && (idx < numfiles) ) {
1415                         struct direntry *file = filelist_file(sfile->files, idx);
1416                         filelist_select_file(sfile->files, idx, FILE_SEL_ADD, EDITING_FILE, CHECK_ALL);
1417                         BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE);
1418                         sfile->params->renamefile[0] = '\0';
1419                 }
1420                 ED_area_tag_redraw(sa);
1421         }
1422         
1423         return OPERATOR_FINISHED;
1424
1425 }
1426
1427 static int file_rename_poll(bContext *C)
1428 {
1429         int poll = ED_operator_file_active(C);
1430         SpaceFile *sfile = CTX_wm_space_file(C);
1431
1432         if (sfile && sfile->params) {
1433                 if (sfile->params->active_file < 0) {
1434                         poll = 0;
1435                 }
1436                 else {
1437                         char dir[FILE_MAX], group[FILE_MAX];
1438                         if (filelist_islibrary(sfile->files, dir, group)) poll = 0;
1439                 }
1440         }
1441         else
1442                 poll = 0;
1443         return poll;
1444 }
1445
1446 void FILE_OT_rename(struct wmOperatorType *ot)
1447 {
1448         /* identifiers */
1449         ot->name = "Rename File or Directory";
1450         ot->description = "Rename file or file directory";
1451         ot->idname = "FILE_OT_rename";
1452         
1453         /* api callbacks */
1454         ot->exec = file_rename_exec;
1455         ot->poll = file_rename_poll; 
1456
1457 }
1458
1459 static int file_delete_poll(bContext *C)
1460 {
1461         int poll = ED_operator_file_active(C);
1462         SpaceFile *sfile = CTX_wm_space_file(C);
1463
1464         if (sfile && sfile->params) {
1465                 char dir[FILE_MAX], group[FILE_MAX];
1466                 int numfiles = filelist_numfiles(sfile->files);
1467                 int i;
1468                 int num_selected = 0;
1469
1470                 if (filelist_islibrary(sfile->files, dir, group)) poll = 0;
1471                 for (i = 0; i < numfiles; i++) {
1472                         if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
1473                                 num_selected++;
1474                         }
1475                 }
1476                 if (num_selected <= 0) {
1477                         poll = 0;
1478                 }
1479         }
1480         else
1481                 poll = 0;
1482                 
1483         return poll;
1484 }
1485
1486 int file_delete_exec(bContext *C, wmOperator *UNUSED(op))
1487 {
1488         char str[FILE_MAX];
1489         wmWindowManager *wm = CTX_wm_manager(C);
1490         SpaceFile *sfile = CTX_wm_space_file(C);
1491         struct direntry *file;  
1492         int numfiles = filelist_numfiles(sfile->files);
1493         int i;
1494
1495         for (i = 0; i < numfiles; i++) {
1496                 if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
1497                         file = filelist_file(sfile->files, i);
1498                         BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relname);
1499                         BLI_delete(str, false, false);
1500                 }
1501         }
1502         
1503         ED_fileselect_clear(wm, sfile);
1504         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1505         
1506         return OPERATOR_FINISHED;
1507
1508 }
1509
1510 void FILE_OT_delete(struct wmOperatorType *ot)
1511 {
1512         /* identifiers */
1513         ot->name = "Delete Selected Files";
1514         ot->description = "Delete selected files";
1515         ot->idname = "FILE_OT_delete";
1516         
1517         /* api callbacks */
1518         ot->invoke = WM_operator_confirm;
1519         ot->exec = file_delete_exec;
1520         ot->poll = file_delete_poll; /* <- important, handler is on window level */
1521 }
1522
1523
1524 void ED_operatormacros_file(void)
1525 {
1526 //      wmOperatorType *ot;
1527 //      wmOperatorTypeMacro *otmacro;
1528         
1529         /* future macros */
1530 }