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