remove G.sce, use G.main->name instead.
[blender-staging.git] / source / blender / editors / space_file / file_ops.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Andrea Weikert (c) 2008 Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include "BKE_context.h"
30 #include "BKE_screen.h"
31 #include "BKE_global.h"
32 #include "BKE_report.h"
33 #include "BKE_main.h"
34
35 #include "BLI_blenlib.h"
36 #include "BLI_storage_types.h"
37 #ifdef WIN32
38 #include "BLI_winstuff.h"
39 #endif
40
41 #include "ED_screen.h"
42 #include "ED_fileselect.h"
43
44 #include "MEM_guardedalloc.h"
45
46 #include "RNA_access.h"
47 #include "RNA_define.h"
48
49 #include "UI_view2d.h"
50
51 #include "WM_api.h"
52 #include "WM_types.h"
53
54 #include "file_intern.h"
55 #include "filelist.h"
56 #include "fsmenu.h"
57
58 #include <stdlib.h>
59 #include <string.h>
60 #include <stdio.h>
61
62 /* for events */
63 #define NOTACTIVEFILE                   0
64 #define ACTIVATE                        1
65 #define INACTIVATE                      2
66
67 /* ---------- FILE SELECTION ------------ */
68
69 static int find_file_mouse(SpaceFile *sfile, struct ARegion* ar, int clamp_bounds, int x, int y)
70 {
71         float fx,fy;
72         int active_file = -1;
73         View2D* v2d = &ar->v2d;
74
75         UI_view2d_region_to_view(v2d, x, y, &fx, &fy);
76
77         active_file = ED_fileselect_layout_offset(sfile->layout, clamp_bounds, v2d->tot.xmin + fx, v2d->tot.ymax - fy);
78         
79         return active_file;
80 }
81
82
83 static void file_deselect_all(SpaceFile* sfile)
84 {
85         int numfiles = filelist_numfiles(sfile->files);
86         int i;
87
88         for ( i=0; i < numfiles; ++i) {
89                 struct direntry* file = filelist_file(sfile->files, i);
90                 if (file && (file->flags & ACTIVEFILE)) {
91                         file->flags &= ~ACTIVEFILE;
92                 }
93         }
94 }
95
96 typedef enum FileSelect { FILE_SELECT_DIR = 1, 
97   FILE_SELECT_FILE = 2 } FileSelect;
98
99
100 static void clamp_to_filelist(int numfiles, int *first_file, int *last_file)
101 {
102         /* border select before the first file */
103         if ( (*first_file < 0) && (*last_file >=0 ) ) {
104                 *first_file = 0;
105         }
106         /* don't select if everything is outside filelist */
107         if ( (*first_file >= numfiles) && ((*last_file < 0) || (*last_file >= numfiles)) ) {
108                 *first_file = -1;
109                 *last_file = -1;
110         }
111         
112         /* fix if last file invalid */
113         if ( (*first_file > 0) && (*last_file < 0) )
114                 *last_file = numfiles-1;
115
116         /* clamp */
117         if ( (*first_file >= numfiles) ) {
118                 *first_file = numfiles-1;
119         }
120         if ( (*last_file >= numfiles) ) {
121                 *last_file = numfiles-1;
122         }
123 }
124
125 static FileSelect file_select(bContext* C, const rcti* rect, short selecting, short toggle_one, short fill)
126 {
127         ARegion *ar= CTX_wm_region(C);
128         SpaceFile *sfile= CTX_wm_space_file(C);
129         int first_file = -1;
130         int last_file = -1;
131         int act_file;
132         FileSelect retval = FILE_SELECT_FILE;
133
134         FileSelectParams *params = ED_fileselect_get_params(sfile);
135         // FileLayout *layout = ED_fileselect_get_layout(sfile, ar);
136
137         int numfiles = filelist_numfiles(sfile->files);
138         
139         params->selstate = NOTACTIVEFILE;
140         first_file = find_file_mouse(sfile, ar, 1, rect->xmin, rect->ymax);
141         last_file = find_file_mouse(sfile, ar, 1, rect->xmax, rect->ymin);
142         
143         clamp_to_filelist(numfiles, &first_file, &last_file);
144
145         if (fill && (last_file >= 0) && (last_file < numfiles) ) {
146                 int f= last_file;
147                 while (f >= 0) {
148                         struct direntry* file = filelist_file(sfile->files, f);
149                         if (file->flags & ACTIVEFILE)
150                                 break;
151                         f--;
152                 }
153                 if (f >= 0) {
154                         first_file = f+1;
155                 }
156         }
157
158         /* select all valid files between first and last indicated */
159         if ( (first_file >= 0) && (first_file < numfiles) && (last_file >= 0) && (last_file < numfiles) ) {
160                 for (act_file = first_file; act_file <= last_file; act_file++) {
161                         struct direntry* file = filelist_file(sfile->files, act_file);
162                         
163                         if (toggle_one) {
164                                 if (file->flags & ACTIVEFILE) {
165                                         file->flags &= ~ACTIVEFILE;
166                                         selecting=0;
167                                 } else
168                                         file->flags |= ACTIVEFILE;
169                         } else if (selecting) 
170                                 file->flags |= ACTIVEFILE;
171                         else
172                                 file->flags &= ~ACTIVEFILE;
173                 }
174         }
175
176         /* Don't act on multiple selected files */
177         if (first_file != last_file) selecting= 0;
178
179         /* make the last file active */
180         if (selecting && (last_file >= 0 && last_file < numfiles)) {
181                 struct direntry* file = filelist_file(sfile->files, last_file);
182                 params->active_file = last_file;
183
184                 if(file && S_ISDIR(file->type)) {
185                         /* the path is too long and we are not going up! */
186                         if (strcmp(file->relname, "..") && strlen(params->dir) + strlen(file->relname) >= FILE_MAX ) 
187                         {
188                                 // XXX error("Path too long, cannot enter this directory");
189                         } else {
190                                 if (strcmp(file->relname, "..")==0) {    
191                                         /* avoids /../../ */     
192                                         BLI_parent_dir(params->dir);     
193                                 } else {
194                                         BLI_cleanup_dir(G.main->name, params->dir);
195                                         strcat(params->dir, file->relname);
196                                         BLI_add_slash(params->dir);
197                                 }
198
199                                 file_change_dir(C, 0);
200                                 retval = FILE_SELECT_DIR;
201                         }
202                 }
203                 else if (file)
204                 {
205                         if (file->relname) {
206                                 BLI_strncpy(params->file, file->relname, FILE_MAXFILE);
207                         }
208                         
209                 }       
210         }
211         
212         /* update operator for name change event */
213         file_draw_check_cb(C, NULL, NULL);
214         
215         return retval;
216 }
217
218
219
220 static int file_border_select_exec(bContext *C, wmOperator *op)
221 {
222         ARegion *ar= CTX_wm_region(C);
223         short selecting;
224         rcti rect;
225         
226         selecting= (RNA_int_get(op->ptr, "gesture_mode")==GESTURE_MODAL_SELECT);
227         rect.xmin= RNA_int_get(op->ptr, "xmin");
228         rect.ymin= RNA_int_get(op->ptr, "ymin");
229         rect.xmax= RNA_int_get(op->ptr, "xmax");
230         rect.ymax= RNA_int_get(op->ptr, "ymax");
231
232         BLI_isect_rcti(&(ar->v2d.mask), &rect, &rect);
233         
234         if (FILE_SELECT_DIR == file_select(C, &rect, selecting, 0, 0)) {
235                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
236         } else {
237                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
238         }
239         return OPERATOR_FINISHED;
240 }
241
242 void FILE_OT_select_border(wmOperatorType *ot)
243 {
244         /* identifiers */
245         ot->name= "Activate/Select File";
246         ot->description= "Activate/select the file(s) contained in the border";
247         ot->idname= "FILE_OT_select_border";
248         
249         /* api callbacks */
250         ot->invoke= WM_border_select_invoke;
251         ot->exec= file_border_select_exec;
252         ot->modal= WM_border_select_modal;
253         ot->poll= ED_operator_file_active;
254
255         /* rna */
256         WM_operator_properties_gesture_border(ot, 0);
257 }
258
259 static int file_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
260 {
261         ARegion *ar= CTX_wm_region(C);
262         SpaceFile *sfile= CTX_wm_space_file(C);
263         short val;
264         rcti rect;
265         int extend = RNA_boolean_get(op->ptr, "extend");
266         int fill = RNA_boolean_get(op->ptr, "fill");
267
268         if(ar->regiontype != RGN_TYPE_WINDOW)
269                 return OPERATOR_CANCELLED;
270
271         rect.xmin = rect.xmax = event->x - ar->winrct.xmin;
272         rect.ymin = rect.ymax = event->y - ar->winrct.ymin;
273         val = event->val;
274
275         if(!BLI_in_rcti(&ar->v2d.mask, rect.xmin, rect.ymin))
276                 return OPERATOR_CANCELLED;
277
278         /* single select, deselect all selected first */
279         if (!extend) file_deselect_all(sfile);
280
281         if (FILE_SELECT_DIR == file_select(C, &rect, 1, extend, fill ))
282                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
283         else
284                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
285
286         WM_event_add_mousemove(C); /* for directory changes */
287         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
288
289         return OPERATOR_FINISHED;
290 }
291
292 void FILE_OT_select(wmOperatorType *ot)
293 {
294         /* identifiers */
295         ot->name= "Activate/Select File";
296         ot->description= "Activate/select file";
297         ot->idname= "FILE_OT_select";
298         
299         /* api callbacks */
300         ot->invoke= file_select_invoke;
301         ot->poll= ED_operator_file_active;
302
303         /* rna */
304         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend selection instead of deselecting everything first.");
305         RNA_def_boolean(ot->srna, "fill", 0, "Fill", "Select everything beginning with the last selection.");
306 }
307
308 static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op))
309 {
310         ScrArea *sa= CTX_wm_area(C);
311         SpaceFile *sfile= CTX_wm_space_file(C);
312         int numfiles = filelist_numfiles(sfile->files);
313         int i;
314         int select = 1;
315
316         /* if any file is selected, deselect all first */
317         for ( i=0; i < numfiles; ++i) {
318                 struct direntry* file = filelist_file(sfile->files, i);
319                 if (file && (file->flags & ACTIVEFILE)) {
320                         file->flags &= ~ACTIVEFILE;
321                         select = 0;
322                         ED_area_tag_redraw(sa);
323                 }
324         }
325         /* select all only if previously no file was selected */
326         if (select) {
327                 for ( i=0; i < numfiles; ++i) {
328                         struct direntry* file = filelist_file(sfile->files, i);
329                         if(file && !S_ISDIR(file->type)) {
330                                 file->flags |= ACTIVEFILE;
331                                 ED_area_tag_redraw(sa);
332                         }
333                 }
334         }
335         return OPERATOR_FINISHED;
336 }
337
338 void FILE_OT_select_all_toggle(wmOperatorType *ot)
339 {
340         /* identifiers */
341         ot->name= "Select/Deselect All Files";
342         ot->description= "Select/deselect all files";
343         ot->idname= "FILE_OT_select_all_toggle";
344         
345         /* api callbacks */
346         ot->exec= file_select_all_exec;
347
348         /* rna */
349
350         ot->poll= ED_operator_file_active;
351 }
352
353 /* ---------- BOOKMARKS ----------- */
354
355 static int bookmark_select_exec(bContext *C, wmOperator *op)
356 {
357         SpaceFile *sfile= CTX_wm_space_file(C);
358
359         if(RNA_struct_find_property(op->ptr, "dir")) {
360                 char entry[256];
361                 FileSelectParams* params = sfile->params;
362
363                 RNA_string_get(op->ptr, "dir", entry);
364                 BLI_strncpy(params->dir, entry, sizeof(params->dir));
365                 BLI_cleanup_dir(G.main->name, params->dir);
366                 file_change_dir(C, 1);
367
368                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
369         }
370         
371         return OPERATOR_FINISHED;
372 }
373
374 void FILE_OT_select_bookmark(wmOperatorType *ot)
375 {
376         /* identifiers */
377         ot->name= "Select Directory";
378         ot->description= "Select a bookmarked directory";
379         ot->idname= "FILE_OT_select_bookmark";
380         
381         /* api callbacks */
382         ot->exec= bookmark_select_exec;
383         ot->poll= ED_operator_file_active;
384
385         RNA_def_string(ot->srna, "dir", "", 256, "Dir", "");
386 }
387
388 static int bookmark_add_exec(bContext *C, wmOperator *UNUSED(op))
389 {
390         ScrArea *sa= CTX_wm_area(C);
391         SpaceFile *sfile= CTX_wm_space_file(C);
392         struct FSMenu* fsmenu = fsmenu_get();
393         struct FileSelectParams* params= ED_fileselect_get_params(sfile);
394
395         if (params->dir[0] != '\0') {
396                 char name[FILE_MAX];
397         
398                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir, 0, 1);
399                 BLI_make_file_string("/", name, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
400                 fsmenu_write_file(fsmenu, name);
401         }
402
403         ED_area_tag_redraw(sa);
404         return OPERATOR_FINISHED;
405 }
406
407 void FILE_OT_bookmark_add(wmOperatorType *ot)
408 {
409         /* identifiers */
410         ot->name= "Add Bookmark";
411         ot->description= "Add a bookmark for the selected/active directory";
412         ot->idname= "FILE_OT_bookmark_add";
413         
414         /* api callbacks */
415         ot->exec= bookmark_add_exec;
416         ot->poll= ED_operator_file_active;
417 }
418
419 static int bookmark_delete_exec(bContext *C, wmOperator *op)
420 {
421         ScrArea *sa= CTX_wm_area(C);
422         struct FSMenu* fsmenu = fsmenu_get();
423         int nentries = fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS);
424         
425         if(RNA_struct_find_property(op->ptr, "index")) {
426                 int index = RNA_int_get(op->ptr, "index");
427                 if ( (index >-1) && (index < nentries)) {
428                         char name[FILE_MAX];
429                         
430                         fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index);
431                         BLI_make_file_string("/", name, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
432                         fsmenu_write_file(fsmenu, name);
433                         ED_area_tag_redraw(sa);
434                 }
435         }
436
437         return OPERATOR_FINISHED;
438 }
439
440 void FILE_OT_delete_bookmark(wmOperatorType *ot)
441 {
442         /* identifiers */
443         ot->name= "Delete Bookmark";
444         ot->description= "Delete selected bookmark";
445         ot->idname= "FILE_OT_delete_bookmark";
446         
447         /* api callbacks */
448         ot->exec= bookmark_delete_exec;
449         ot->poll= ED_operator_file_active;
450
451         RNA_def_int(ot->srna, "index", -1, -1, 20000, "Index", "", -1, 20000);
452 }
453
454 int file_hilight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
455 {
456         FileSelectParams* params;
457         int numfiles, actfile, origfile;
458         
459         if(sfile==NULL || sfile->files==NULL) return 0;
460
461         numfiles = filelist_numfiles(sfile->files);
462         params = ED_fileselect_get_params(sfile);
463
464         origfile= params->active_file;
465
466         mx -= ar->winrct.xmin;
467         my -= ar->winrct.ymin;
468
469         if(BLI_in_rcti(&ar->v2d.mask, mx, my)) {
470                 actfile = find_file_mouse(sfile, ar, 0, mx , my);
471
472                 if((actfile >= 0) && (actfile < numfiles))
473                         params->active_file=actfile;
474                 else
475                         params->active_file= -1;
476         }
477         else
478                 params->active_file= -1;
479
480         return (params->active_file != origfile);
481 }
482
483 static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
484 {
485         ARegion *ar= CTX_wm_region(C);
486         SpaceFile *sfile= CTX_wm_space_file(C);
487
488         if(!file_hilight_set(sfile, ar, event->x, event->y))
489                 return OPERATOR_CANCELLED;
490
491         ED_area_tag_redraw(CTX_wm_area(C));
492         
493         return OPERATOR_FINISHED;
494 }
495
496 void FILE_OT_highlight(struct wmOperatorType *ot)
497 {
498         /* identifiers */
499         ot->name= "Highlight File";
500         ot->description= "Highlight selected file(s)";
501         ot->idname= "FILE_OT_highlight";
502         
503         /* api callbacks */
504         ot->invoke= file_highlight_invoke;
505         ot->poll= ED_operator_file_active;
506 }
507
508 int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused))
509 {
510         SpaceFile *sfile= CTX_wm_space_file(C);
511         wmOperator *op = sfile->op;
512         
513         sfile->op = NULL;
514
515         WM_event_fileselect_event(C, op, EVT_FILESELECT_CANCEL);
516         
517         return OPERATOR_FINISHED;
518 }
519
520 int file_operator_poll(bContext *C)
521 {
522         int poll = ED_operator_file_active(C);
523         SpaceFile *sfile= CTX_wm_space_file(C);
524
525         if (!sfile || !sfile->op) poll= 0;
526
527         return poll;
528 }
529
530 void FILE_OT_cancel(struct wmOperatorType *ot)
531 {
532         /* identifiers */
533         ot->name= "Cancel File Load";
534         ot->description= "Cancel loading of selected file";
535         ot->idname= "FILE_OT_cancel";
536         
537         /* api callbacks */
538         ot->exec= file_cancel_exec;
539         ot->poll= file_operator_poll;
540 }
541
542
543 void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
544 {
545         BLI_join_dirfile(filepath, sfile->params->dir, sfile->params->file);
546         if(RNA_struct_find_property(op->ptr, "relative_path")) {
547                 if(RNA_boolean_get(op->ptr, "relative_path")) {
548                         BLI_path_rel(filepath, G.main->name);
549                 }
550         }
551
552         if(RNA_struct_find_property(op->ptr, "filename")) {
553                 RNA_string_set(op->ptr, "filename", sfile->params->file);
554         }
555         if(RNA_struct_find_property(op->ptr, "directory")) {
556                 RNA_string_set(op->ptr, "directory", sfile->params->dir);
557         }
558         if(RNA_struct_find_property(op->ptr, "filepath")) {
559                 RNA_string_set(op->ptr, "filepath", filepath);
560         }
561         
562         /* some ops have multiple files to select */
563         {
564                 PointerRNA itemptr;
565                 int i, numfiles = filelist_numfiles(sfile->files);
566                 struct direntry *file;
567                 if(RNA_struct_find_property(op->ptr, "files")) {
568                         for (i=0; i<numfiles; i++) {
569                                 file = filelist_file(sfile->files, i);
570                                 if(file->flags & ACTIVEFILE) {
571                                         if ((file->type & S_IFDIR)==0) {
572                                                 RNA_collection_add(op->ptr, "files", &itemptr);
573                                                 RNA_string_set(&itemptr, "name", file->relname);
574                                         }
575                                 }
576                         }
577                 }
578                 
579                 if(RNA_struct_find_property(op->ptr, "dirs")) {
580                         for (i=0; i<numfiles; i++) {
581                                 file = filelist_file(sfile->files, i);
582                                 if(file->flags & ACTIVEFILE) {
583                                         if ((file->type & S_IFDIR)) {
584                                                 RNA_collection_add(op->ptr, "dirs", &itemptr);
585                                                 RNA_string_set(&itemptr, "name", file->relname);
586                                         }
587                                 }
588                         }
589                 }
590         }
591 }
592
593 void file_operator_to_sfile(SpaceFile *sfile, wmOperator *op)
594 {
595         int change= FALSE;
596         if(RNA_struct_find_property(op->ptr, "filename")) {
597                 RNA_string_get(op->ptr, "filename", sfile->params->file);
598                 change= TRUE;
599         }
600         if(RNA_struct_find_property(op->ptr, "directory")) {
601                 RNA_string_get(op->ptr, "directory", sfile->params->dir);
602                 change= TRUE;
603         }
604         
605         /* If neither of the above are set, split the filepath back */
606         if(RNA_struct_find_property(op->ptr, "filepath")) {
607                 if(change==FALSE) {
608                         char filepath[FILE_MAX];
609                         RNA_string_get(op->ptr, "filepath", filepath);
610                         BLI_split_dirfile(filepath, sfile->params->dir, sfile->params->file);
611                 }
612         }
613         
614         /* XXX, files and dirs updates missing, not really so important though */
615 }
616
617 void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2))
618 {
619         SpaceFile *sfile= CTX_wm_space_file(C);
620         wmOperator *op= sfile->op;
621         if(op) { /* fail on reload */
622                 if(op->type->check) {
623                         char filepath[FILE_MAX];
624                         file_sfile_to_operator(op, sfile, filepath);
625                         
626                         /* redraw */
627                         if(op->type->check(C, op)) {
628                                 file_operator_to_sfile(sfile, op);
629         
630                                 /* redraw, else the changed settings wont get updated */
631                                 ED_area_tag_redraw(CTX_wm_area(C));
632                         }
633                 }
634         }
635 }
636
637 int file_draw_check_exists(SpaceFile *sfile)
638 {
639         if(sfile->op) { /* fails on reload */
640                 if(RNA_struct_find_property(sfile->op->ptr, "check_existing")) {
641                         if(RNA_boolean_get(sfile->op->ptr, "check_existing")) {
642                                 char filepath[FILE_MAX];
643                                 BLI_join_dirfile(filepath, sfile->params->dir, sfile->params->file);
644                                 if(BLI_exists(filepath) && !BLI_is_dir(filepath)) {
645                                         return TRUE;
646                                 }
647                         }
648                 }
649         }
650
651         return FALSE;
652 }
653
654 /* sends events now, so things get handled on windowqueue level */
655 int file_exec(bContext *C, wmOperator *exec_op)
656 {
657         SpaceFile *sfile= CTX_wm_space_file(C);
658         char filepath[FILE_MAX];
659         
660         if(sfile->op) {
661                 wmOperator *op= sfile->op;
662         
663                 /* when used as a macro, for doubleclick, 
664                  to prevent closing when doubleclicking on .. item */
665                 if (RNA_boolean_get(exec_op->ptr, "need_active")) {
666                         int i, active=0;
667                         struct direntry *file;
668                         
669                         for (i=0; i<filelist_numfiles(sfile->files); i++) {
670                                 file = filelist_file(sfile->files, i);
671                                 if(file->flags & ACTIVEFILE) {
672                                         active=1;
673                                 }
674                         }
675                         if (active == 0)
676                                 return OPERATOR_CANCELLED;
677                 }
678                 
679                 sfile->op = NULL;
680
681                 file_sfile_to_operator(op, sfile, filepath);
682
683                 fsmenu_insert_entry(fsmenu_get(), FS_CATEGORY_RECENT, sfile->params->dir,0, 1);
684                 BLI_make_file_string(G.main->name, filepath, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
685                 fsmenu_write_file(fsmenu_get(), filepath);
686                 WM_event_fileselect_event(C, op, EVT_FILESELECT_EXEC);
687
688         }
689                                 
690         return OPERATOR_FINISHED;
691 }
692
693 void FILE_OT_execute(struct wmOperatorType *ot)
694 {
695         /* identifiers */
696         ot->name= "Execute File Window";
697         ot->description= "Execute selected file";
698         ot->idname= "FILE_OT_execute";
699         
700         /* api callbacks */
701         ot->exec= file_exec;
702         ot->poll= file_operator_poll; 
703         
704         RNA_def_boolean(ot->srna, "need_active", 0, "Need Active", "Only execute if there's an active selected file in the file list.");
705 }
706
707
708 int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
709 {
710         SpaceFile *sfile= CTX_wm_space_file(C);
711         
712         if(sfile->params) {
713                 if (BLI_has_parent(sfile->params->dir)) {
714                         BLI_parent_dir(sfile->params->dir);
715                         BLI_cleanup_dir(G.main->name, sfile->params->dir);
716                         file_change_dir(C, 0);
717                         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
718                 }
719         }               
720         
721         return OPERATOR_FINISHED;
722
723 }
724
725
726 void FILE_OT_parent(struct wmOperatorType *ot)
727 {
728         /* identifiers */
729         ot->name= "Parent File";
730         ot->description= "Move to parent directory";
731         ot->idname= "FILE_OT_parent";
732         
733         /* api callbacks */
734         ot->exec= file_parent_exec;
735         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
736 }
737
738
739 int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
740 {
741         SpaceFile *sfile= CTX_wm_space_file(C);
742
743         ED_fileselect_clear(C, sfile);
744
745         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
746
747         return OPERATOR_FINISHED;
748
749 }
750
751 void FILE_OT_previous(struct wmOperatorType *ot)
752 {
753         /* identifiers */
754         ot->name= "Previous Folder";
755         ot->description= "Move to previous folder";
756         ot->idname= "FILE_OT_previous";
757         
758         /* api callbacks */
759         ot->exec= file_previous_exec;
760         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
761 }
762
763 int file_previous_exec(bContext *C, wmOperator *UNUSED(unused))
764 {
765         SpaceFile *sfile= CTX_wm_space_file(C);
766
767         if(sfile->params) {
768                 if (!sfile->folders_next)
769                         sfile->folders_next = folderlist_new();
770
771                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
772                 folderlist_popdir(sfile->folders_prev, sfile->params->dir);
773                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
774
775                 file_change_dir(C, 1);
776         }
777         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
778
779         return OPERATOR_FINISHED;
780 }
781
782 void FILE_OT_next(struct wmOperatorType *ot)
783 {
784         /* identifiers */
785         ot->name= "Next Folder";
786         ot->description= "Move to next folder";
787         ot->idname= "FILE_OT_next";
788         
789         /* api callbacks */
790         ot->exec= file_next_exec;
791         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
792 }
793
794 int file_next_exec(bContext *C, wmOperator *UNUSED(unused))
795 {
796         SpaceFile *sfile= CTX_wm_space_file(C);
797         if(sfile->params) {
798                 if (!sfile->folders_next)
799                         sfile->folders_next = folderlist_new();
800
801                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
802                 folderlist_popdir(sfile->folders_next, sfile->params->dir);
803
804                 // update folder_prev so we can check for it in folderlist_clear_next()
805                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
806
807                 file_change_dir(C, 1);
808         }               
809         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
810
811         return OPERATOR_FINISHED;
812 }
813
814
815 /* only meant for timer usage */
816 static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
817 {
818         ScrArea *sa = CTX_wm_area(C);
819         SpaceFile *sfile= CTX_wm_space_file(C);
820         ARegion *ar, *oldar= CTX_wm_region(C);
821         int numfiles, offset;
822         int edit_idx = 0;
823         int numfiles_layout;
824         int i;
825
826         /* escape if not our timer */
827         if(sfile->smoothscroll_timer==NULL || sfile->smoothscroll_timer!=event->customdata)
828                 return OPERATOR_PASS_THROUGH;
829         
830         numfiles = filelist_numfiles(sfile->files);
831
832         /* check if we are editing a name */
833         for (i=0; i < numfiles; ++i)
834         {
835                 struct direntry *file = filelist_file(sfile->files, i); 
836                 if (file->flags & EDITING) {
837                         edit_idx=i;
838                         break;
839                 }
840         }
841
842         /* if we are not editing, we are done */
843         if (0==edit_idx) {
844                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
845                 sfile->smoothscroll_timer=NULL;
846                 return OPERATOR_PASS_THROUGH;
847         }
848
849         /* we need the correct area for scrolling */
850         ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
851         if (!ar || ar->regiontype != RGN_TYPE_WINDOW) {
852                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
853                 sfile->smoothscroll_timer=NULL;
854                 return OPERATOR_PASS_THROUGH;
855         }
856
857         offset = ED_fileselect_layout_offset(sfile->layout, 0, ar->v2d.cur.xmin, -ar->v2d.cur.ymax);
858         if (offset<0) offset=0;
859
860         /* scroll offset is the first file in the row/column we are editing in */
861         if (sfile->scroll_offset == 0) {
862                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
863                         sfile->scroll_offset = (edit_idx/sfile->layout->rows)*sfile->layout->rows;
864                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->rows;
865                 } else {
866                         sfile->scroll_offset = (edit_idx/sfile->layout->columns)*sfile->layout->columns;
867                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->columns;
868                 }
869         }
870         
871         numfiles_layout = ED_fileselect_layout_numfiles(sfile->layout, ar);
872         
873         /* check if we have reached our final scroll position */
874         if ( (sfile->scroll_offset >= offset) && (sfile->scroll_offset < offset + numfiles_layout) ) {
875                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
876                 sfile->smoothscroll_timer=NULL;
877                 return OPERATOR_FINISHED;
878         }
879
880         /* temporarily set context to the main window region, 
881          * so the scroll operators work */
882         CTX_wm_region_set(C, ar);
883         
884         /* scroll one step in the desired direction */
885         if (sfile->scroll_offset < offset) {
886                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
887                         WM_operator_name_call(C, "VIEW2D_OT_scroll_left", 0, NULL);
888                 } else {
889                         WM_operator_name_call(C, "VIEW2D_OT_scroll_up", 0, NULL);
890                 }
891                 
892         } else {
893                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
894                         WM_operator_name_call(C, "VIEW2D_OT_scroll_right", 0, NULL);
895                 } else {
896                         WM_operator_name_call(C, "VIEW2D_OT_scroll_down", 0, NULL);
897                 }
898         }
899         
900         ED_region_tag_redraw(CTX_wm_region(C));
901         
902         /* and restore context */
903         CTX_wm_region_set(C, oldar);
904         
905         return OPERATOR_FINISHED;
906 }
907
908
909 void FILE_OT_smoothscroll(wmOperatorType *ot)
910 {
911         
912         /* identifiers */
913         ot->name= "Smooth Scroll";
914         ot->idname= "FILE_OT_smoothscroll";
915         ot->description="Smooth scroll to make editable file visible.";
916         
917         /* api callbacks */
918         ot->invoke= file_smoothscroll_invoke;
919         
920         ot->poll= ED_operator_file_active;
921 }
922
923
924 /* create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created.
925    The actual name is returned in 'name', 'folder' contains the complete path, including the new folder name.
926 */
927 static int new_folder_path(const char* parent, char *folder, char *name)
928 {
929         int i = 1;
930         int len = 0;
931
932         BLI_strncpy(name, "New Folder", FILE_MAXFILE);
933         BLI_join_dirfile(folder, parent, name);
934         /* check whether folder with the name already exists, in this case
935            add number to the name. Check length of generated name to avoid
936            crazy case of huge number of folders each named 'New Folder (x)' */
937         while (BLI_exists(folder) && (len<FILE_MAXFILE)) {
938                 len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
939                 BLI_join_dirfile(folder, parent, name);
940                 i++;
941         }
942
943         return (len<FILE_MAXFILE);
944 }
945
946 int file_directory_new_exec(bContext *C, wmOperator *op)
947 {
948         char name[FILE_MAXFILE];
949         char path[FILE_MAX];
950         SpaceFile *sfile= CTX_wm_space_file(C);
951         
952         if(!sfile->params) {
953                 BKE_report(op->reports,RPT_WARNING, "No parent directory given.");
954                 return OPERATOR_CANCELLED;
955         }
956         
957         /* create a new, non-existing folder name */
958         if (!new_folder_path(sfile->params->dir, path, name)) {
959                 BKE_report(op->reports,RPT_ERROR, "Couldn't create new folder name.");
960                 return OPERATOR_CANCELLED;
961         }
962                 
963         /* rename the file */
964         BLI_recurdir_fileops(path);
965
966         if (!BLI_exists(path)) {
967                 BKE_report(op->reports,RPT_ERROR, "Couldn't create new folder.");
968                 return OPERATOR_CANCELLED;
969         } 
970
971         /* now remember file to jump into editing */
972         BLI_strncpy(sfile->params->renamefile, name, FILE_MAXFILE);
973
974         /* set timer to smoothly view newly generated file */
975         sfile->smoothscroll_timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER1, 1.0/1000.0);        /* max 30 frs/sec */
976         sfile->scroll_offset=0;
977
978         /* reload dir to make sure we're seeing what's in the directory */
979         ED_fileselect_clear(C, sfile);
980         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
981
982         return OPERATOR_FINISHED;
983 }
984
985
986 void FILE_OT_directory_new(struct wmOperatorType *ot)
987 {
988         /* identifiers */
989         ot->name= "Create New Directory";
990         ot->description= "Create a new directory";
991         ot->idname= "FILE_OT_directory_new";
992         
993         /* api callbacks */
994         ot->invoke= WM_operator_confirm;
995         ot->exec= file_directory_new_exec;
996         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
997 }
998
999 int file_directory_exec(bContext *C, wmOperator *UNUSED(unused))
1000 {
1001         SpaceFile *sfile= CTX_wm_space_file(C);
1002         
1003         if(sfile->params) {
1004                 if ( sfile->params->dir[0] == '~' ) {
1005                         char tmpstr[sizeof(sfile->params->dir)-1];
1006                         strncpy(tmpstr, sfile->params->dir+1, sizeof(tmpstr));
1007                         BLI_join_dirfile(sfile->params->dir, BLI_getDefaultDocumentFolder(), tmpstr);
1008                 }
1009
1010 #ifdef WIN32
1011                 if (sfile->params->dir[0] == '\0')
1012                         get_default_root(sfile->params->dir);
1013 #endif
1014                 BLI_cleanup_dir(G.main->name, sfile->params->dir);
1015                 BLI_add_slash(sfile->params->dir);
1016                 file_change_dir(C, 1);
1017
1018                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
1019         }               
1020         
1021
1022         return OPERATOR_FINISHED;
1023 }
1024
1025 int file_filename_exec(bContext *C, wmOperator *UNUSED(unused))
1026 {
1027         SpaceFile *sfile= CTX_wm_space_file(C);
1028         
1029         if(sfile->params) {
1030                 if (file_select_match(sfile, sfile->params->file))
1031                 {
1032                         sfile->params->file[0] = '\0';
1033                         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
1034                 }
1035         }               
1036
1037         return OPERATOR_FINISHED;
1038 }
1039
1040
1041 void FILE_OT_refresh(struct wmOperatorType *ot)
1042 {
1043         /* identifiers */
1044         ot->name= "Refresh Filelist";
1045         ot->description= "Refresh the file list";
1046         ot->idname= "FILE_OT_refresh";
1047         
1048         /* api callbacks */
1049         ot->exec= file_refresh_exec;
1050         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1051 }
1052
1053 int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused))
1054 {
1055         SpaceFile *sfile= CTX_wm_space_file(C);
1056         
1057         if(sfile->params) {
1058                 sfile->params->flag ^= FILE_HIDE_DOT;
1059                 ED_fileselect_clear(C, sfile);
1060                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
1061         }
1062         
1063         return OPERATOR_FINISHED;
1064 }
1065
1066
1067 void FILE_OT_hidedot(struct wmOperatorType *ot)
1068 {
1069         /* identifiers */
1070         ot->name= "Toggle Hide Dot Files";
1071         ot->description= "Toggle hide hidden dot files";
1072         ot->idname= "FILE_OT_hidedot";
1073         
1074         /* api callbacks */
1075         ot->exec= file_hidedot_exec;
1076         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1077 }
1078
1079 struct ARegion *file_buttons_region(struct ScrArea *sa)
1080 {
1081         ARegion *ar, *arnew;
1082         
1083         for(ar= sa->regionbase.first; ar; ar= ar->next)
1084                 if(ar->regiontype==RGN_TYPE_CHANNELS)
1085                         return ar;
1086
1087         /* add subdiv level; after header */
1088         for(ar= sa->regionbase.first; ar; ar= ar->next)
1089                 if(ar->regiontype==RGN_TYPE_HEADER)
1090                         break;
1091         
1092         /* is error! */
1093         if(ar==NULL) return NULL;
1094         
1095         arnew= MEM_callocN(sizeof(ARegion), "buttons for file panels");
1096         
1097         BLI_insertlinkafter(&sa->regionbase, ar, arnew);
1098         arnew->regiontype= RGN_TYPE_CHANNELS;
1099         arnew->alignment= RGN_ALIGN_LEFT;
1100         
1101         arnew->flag = RGN_FLAG_HIDDEN;
1102         
1103         return arnew;
1104 }
1105
1106 int file_bookmark_toggle_exec(bContext *C, wmOperator *UNUSED(unused))
1107 {
1108         ScrArea *sa= CTX_wm_area(C);
1109         ARegion *ar= file_buttons_region(sa);
1110         
1111         if(ar)
1112                 ED_region_toggle_hidden(C, ar);
1113
1114         return OPERATOR_FINISHED;
1115 }
1116
1117 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
1118 {
1119         /* identifiers */
1120         ot->name= "Toggle Bookmarks";
1121         ot->description= "Toggle bookmarks display";
1122         ot->idname= "FILE_OT_bookmark_toggle";
1123         
1124         /* api callbacks */
1125         ot->exec= file_bookmark_toggle_exec;
1126         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1127 }
1128
1129
1130 int file_filenum_exec(bContext *C, wmOperator *op)
1131 {
1132         SpaceFile *sfile= CTX_wm_space_file(C);
1133         ScrArea *sa= CTX_wm_area(C);
1134         
1135         int inc = RNA_int_get(op->ptr, "increment");
1136         if(sfile->params && (inc != 0)) {
1137                 BLI_newname(sfile->params->file, inc);
1138                 ED_area_tag_redraw(sa);
1139                 file_draw_check_cb(C, NULL, NULL);
1140                 // WM_event_add_notifier(C, NC_WINDOW, NULL);
1141         }
1142         
1143         return OPERATOR_FINISHED;
1144
1145 }
1146
1147 void FILE_OT_filenum(struct wmOperatorType *ot)
1148 {
1149         /* identifiers */
1150         ot->name= "Increment Number in Filename";
1151         ot->description= "Increment number in filename";
1152         ot->idname= "FILE_OT_filenum";
1153         
1154         /* api callbacks */
1155         ot->exec= file_filenum_exec;
1156         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1157
1158         /* props */
1159         RNA_def_int(ot->srna, "increment", 1, 0, 100, "Increment", "", 0,100);
1160 }
1161
1162 int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
1163 {
1164         ScrArea *sa= CTX_wm_area(C);
1165         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
1166         
1167         if(sfile->params) {
1168                 int idx = sfile->params->active_file;
1169                 int numfiles = filelist_numfiles(sfile->files);
1170                 if ( (0<=idx) && (idx<numfiles) ) {
1171                         struct direntry *file= filelist_file(sfile->files, idx);
1172                         file->flags |= EDITING;
1173                         BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE);
1174                         sfile->params->renamefile[0]= '\0';
1175                 }
1176                 ED_area_tag_redraw(sa);
1177         }
1178         
1179         return OPERATOR_FINISHED;
1180
1181 }
1182
1183 int file_rename_poll(bContext *C)
1184 {
1185         int poll = ED_operator_file_active(C);
1186         SpaceFile *sfile= CTX_wm_space_file(C);
1187
1188         if (sfile && sfile->params) {
1189                 if (sfile->params->active_file < 0) { 
1190                         poll= 0;
1191                 } else {
1192                         char dir[FILE_MAX], group[FILE_MAX];    
1193                         if (filelist_islibrary(sfile->files, dir, group)) poll= 0;
1194                 }
1195         }
1196         else
1197                 poll= 0;
1198         return poll;
1199 }
1200
1201 void FILE_OT_rename(struct wmOperatorType *ot)
1202 {
1203         /* identifiers */
1204         ot->name= "Rename File or Directory";
1205         ot->description= "Rename file or file directory";
1206         ot->idname= "FILE_OT_rename";
1207         
1208         /* api callbacks */
1209         ot->exec= file_rename_exec;
1210         ot->poll= file_rename_poll; 
1211
1212 }
1213
1214 int file_delete_poll(bContext *C)
1215 {
1216         int poll = ED_operator_file_active(C);
1217         SpaceFile *sfile= CTX_wm_space_file(C);
1218         struct direntry* file;
1219
1220         if (sfile && sfile->params) {
1221                 if (sfile->params->active_file < 0) { 
1222                         poll= 0;
1223                 } else {
1224                         char dir[FILE_MAX], group[FILE_MAX];    
1225                         if (filelist_islibrary(sfile->files, dir, group)) poll= 0;
1226                         file = filelist_file(sfile->files, sfile->params->active_file);
1227                         if (file && S_ISDIR(file->type)) poll= 0;
1228                 }
1229         }
1230         else
1231                 poll= 0;
1232                 
1233         return poll;
1234 }
1235
1236 int file_delete_exec(bContext *C, wmOperator *UNUSED(op))
1237 {
1238         char str[FILE_MAX];
1239         SpaceFile *sfile= CTX_wm_space_file(C);
1240         struct direntry* file;
1241         
1242         
1243         file = filelist_file(sfile->files, sfile->params->active_file);
1244         BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relname);
1245         BLI_delete(str, 0, 0);  
1246         ED_fileselect_clear(C, sfile);
1247         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
1248         
1249         return OPERATOR_FINISHED;
1250
1251 }
1252
1253 void FILE_OT_delete(struct wmOperatorType *ot)
1254 {
1255         /* identifiers */
1256         ot->name= "Delete File";
1257         ot->description= "Delete selected file";
1258         ot->idname= "FILE_OT_delete";
1259         
1260         /* api callbacks */
1261         ot->invoke= WM_operator_confirm;
1262         ot->exec= file_delete_exec;
1263         ot->poll= file_delete_poll; /* <- important, handler is on window level */
1264 }
1265
1266
1267 void ED_operatormacros_file(void)
1268 {
1269         wmOperatorType *ot;
1270         wmOperatorTypeMacro *otmacro;
1271         
1272         ot= WM_operatortype_append_macro("FILE_OT_select_execute", "Select and Execute", OPTYPE_UNDO|OPTYPE_REGISTER);
1273         WM_operatortype_macro_define(ot, "FILE_OT_select");
1274         otmacro= WM_operatortype_macro_define(ot, "FILE_OT_execute");
1275         RNA_boolean_set(otmacro->ptr, "need_active", 1);
1276
1277 }