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