Fix [#21765] File-Dialog: "New Directory" gives no visual feedback
[blender.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 NOTACTIVE                       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 & ACTIVE)) {
90                         file->flags &= ~ACTIVE;
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)
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 = NOTACTIVE;
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         /* select all valid files between first and last indicated */
145         if ( (first_file >= 0) && (first_file < numfiles) && (last_file >= 0) && (last_file < numfiles) ) {
146                 for (act_file = first_file; act_file <= last_file; act_file++) {
147                         struct direntry* file = filelist_file(sfile->files, act_file);
148                         
149                         if (toggle_one) {
150                                 if (file->flags & ACTIVE) {
151                                         file->flags &= ~ACTIVE;
152                                         selecting=0;
153                                 } else
154                                         file->flags |= ACTIVE;
155                         } else if (selecting) 
156                                 file->flags |= ACTIVE;
157                         else
158                                 file->flags &= ~ACTIVE;
159                 }
160         }
161
162         /* Don't act on multiple selected files */
163         if (first_file != last_file) selecting= 0;
164
165         /* make the last file active */
166         if (selecting && (last_file >= 0 && last_file < numfiles)) {
167                 struct direntry* file = filelist_file(sfile->files, last_file);
168                 params->active_file = last_file;
169
170                 if(file && S_ISDIR(file->type)) {
171                         /* the path is too long and we are not going up! */
172                         if (strcmp(file->relname, "..") && strlen(params->dir) + strlen(file->relname) >= FILE_MAX ) 
173                         {
174                                 // XXX error("Path too long, cannot enter this directory");
175                         } else {
176                                 if (strcmp(file->relname, "..")==0) {    
177                                         /* avoids /../../ */     
178                                         BLI_parent_dir(params->dir);     
179                                 } else {
180                                         BLI_cleanup_dir(G.sce, params->dir);
181                                         strcat(params->dir, file->relname);
182                                         BLI_add_slash(params->dir);
183                                 }
184
185                                 file_change_dir(C, 0);
186                                 retval = FILE_SELECT_DIR;
187                         }
188                 }
189                 else if (file)
190                 {
191                         if (file->relname) {
192                                 BLI_strncpy(params->file, file->relname, FILE_MAXFILE);
193                         }
194                         
195                 }       
196         } 
197         return retval;
198 }
199
200
201
202 static int file_border_select_exec(bContext *C, wmOperator *op)
203 {
204         ARegion *ar= CTX_wm_region(C);
205         short selecting;
206         rcti rect;
207         
208         selecting= (RNA_int_get(op->ptr, "gesture_mode")==GESTURE_MODAL_SELECT);
209         rect.xmin= RNA_int_get(op->ptr, "xmin");
210         rect.ymin= RNA_int_get(op->ptr, "ymin");
211         rect.xmax= RNA_int_get(op->ptr, "xmax");
212         rect.ymax= RNA_int_get(op->ptr, "ymax");
213
214         BLI_isect_rcti(&(ar->v2d.mask), &rect, &rect);
215         
216         if (FILE_SELECT_DIR == file_select(C, &rect, selecting, 0)) {
217                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
218         } else {
219                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
220         }
221         return OPERATOR_FINISHED;
222 }
223
224 void FILE_OT_select_border(wmOperatorType *ot)
225 {
226         /* identifiers */
227         ot->name= "Activate/Select File";
228         ot->description= "Activate/select the file(s) contained in the border";
229         ot->idname= "FILE_OT_select_border";
230         
231         /* api callbacks */
232         ot->invoke= WM_border_select_invoke;
233         ot->exec= file_border_select_exec;
234         ot->modal= WM_border_select_modal;
235         ot->poll= ED_operator_file_active;
236
237         /* rna */
238         WM_operator_properties_gesture_border(ot, 0);
239 }
240
241 static int file_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
242 {
243         ARegion *ar= CTX_wm_region(C);
244         SpaceFile *sfile= CTX_wm_space_file(C);
245         short val;
246         rcti rect;
247         int extend = RNA_boolean_get(op->ptr, "extend");
248
249         if(ar->regiontype != RGN_TYPE_WINDOW)
250                 return OPERATOR_CANCELLED;
251
252         rect.xmin = rect.xmax = event->x - ar->winrct.xmin;
253         rect.ymin = rect.ymax = event->y - ar->winrct.ymin;
254         val = event->val;
255
256         if(!BLI_in_rcti(&ar->v2d.mask, rect.xmin, rect.ymin))
257                 return OPERATOR_CANCELLED;
258
259         /* single select, deselect all selected first */
260         if (!extend) file_deselect_all(sfile);
261
262         if (FILE_SELECT_DIR == file_select(C, &rect, 1, extend ))
263                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
264         else
265                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
266
267         WM_event_add_mousemove(C); /* for directory changes */
268         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
269
270         return OPERATOR_FINISHED;
271 }
272
273 void FILE_OT_select(wmOperatorType *ot)
274 {
275         /* identifiers */
276         ot->name= "Activate/Select File";
277         ot->description= "Activate/select file";
278         ot->idname= "FILE_OT_select";
279         
280         /* api callbacks */
281         ot->invoke= file_select_invoke;
282         ot->poll= ED_operator_file_active;
283
284         /* rna */
285         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend selection instead of deselecting everything first.");
286 }
287
288 static int file_select_all_exec(bContext *C, wmOperator *op)
289 {
290         ScrArea *sa= CTX_wm_area(C);
291         SpaceFile *sfile= CTX_wm_space_file(C);
292         int numfiles = filelist_numfiles(sfile->files);
293         int i;
294         int select = 1;
295
296         /* if any file is selected, deselect all first */
297         for ( i=0; i < numfiles; ++i) {
298                 struct direntry* file = filelist_file(sfile->files, i);
299                 if (file && (file->flags & ACTIVE)) {
300                         file->flags &= ~ACTIVE;
301                         select = 0;
302                         ED_area_tag_redraw(sa);
303                 }
304         }
305         /* select all only if previously no file was selected */
306         if (select) {
307                 for ( i=0; i < numfiles; ++i) {
308                         struct direntry* file = filelist_file(sfile->files, i);
309                         if(file && !S_ISDIR(file->type)) {
310                                 file->flags |= ACTIVE;
311                                 ED_area_tag_redraw(sa);
312                         }
313                 }
314         }
315         return OPERATOR_FINISHED;
316 }
317
318 void FILE_OT_select_all_toggle(wmOperatorType *ot)
319 {
320         /* identifiers */
321         ot->name= "Select/Deselect all files";
322         ot->description= "Select/deselect all files";
323         ot->idname= "FILE_OT_select_all_toggle";
324         
325         /* api callbacks */
326         ot->exec= file_select_all_exec;
327
328         /* rna */
329
330         ot->poll= ED_operator_file_active;
331 }
332
333 /* ---------- BOOKMARKS ----------- */
334
335 static int bookmark_select_exec(bContext *C, wmOperator *op)
336 {
337         SpaceFile *sfile= CTX_wm_space_file(C);
338
339         if(RNA_struct_find_property(op->ptr, "dir")) {
340                 char entry[256];
341                 FileSelectParams* params = sfile->params;
342
343                 RNA_string_get(op->ptr, "dir", entry);
344                 BLI_strncpy(params->dir, entry, sizeof(params->dir));
345                 BLI_cleanup_dir(G.sce, params->dir);
346                 file_change_dir(C, 1);
347
348                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
349         }
350         
351         return OPERATOR_FINISHED;
352 }
353
354 void FILE_OT_select_bookmark(wmOperatorType *ot)
355 {
356         /* identifiers */
357         ot->name= "Select Directory";
358         ot->description= "Select a bookmarked directory";
359         ot->idname= "FILE_OT_select_bookmark";
360         
361         /* api callbacks */
362         ot->exec= bookmark_select_exec;
363         ot->poll= ED_operator_file_active;
364
365         RNA_def_string(ot->srna, "dir", "", 256, "Dir", "");
366 }
367
368 static int bookmark_add_exec(bContext *C, wmOperator *op)
369 {
370         ScrArea *sa= CTX_wm_area(C);
371         SpaceFile *sfile= CTX_wm_space_file(C);
372         struct FSMenu* fsmenu = fsmenu_get();
373         struct FileSelectParams* params= ED_fileselect_get_params(sfile);
374
375         if (params->dir[0] != '\0') {
376                 char name[FILE_MAX];
377         
378                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir, 0, 1);
379                 BLI_make_file_string("/", name, BLI_gethome(), ".Bfs");
380                 fsmenu_write_file(fsmenu, name);
381         }
382
383         ED_area_tag_redraw(sa);
384         return OPERATOR_FINISHED;
385 }
386
387 void FILE_OT_bookmark_add(wmOperatorType *ot)
388 {
389         /* identifiers */
390         ot->name= "Add Bookmark";
391         ot->description= "Add a bookmark for the selected/active directory";
392         ot->idname= "FILE_OT_bookmark_add";
393         
394         /* api callbacks */
395         ot->exec= bookmark_add_exec;
396         ot->poll= ED_operator_file_active;
397 }
398
399 static int bookmark_delete_exec(bContext *C, wmOperator *op)
400 {
401         ScrArea *sa= CTX_wm_area(C);
402         struct FSMenu* fsmenu = fsmenu_get();
403         int nentries = fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS);
404         
405         if(RNA_struct_find_property(op->ptr, "index")) {
406                 int index = RNA_int_get(op->ptr, "index");
407                 if ( (index >-1) && (index < nentries)) {
408                         char name[FILE_MAX];
409                         
410                         fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index);
411                         BLI_make_file_string("/", name, BLI_gethome(), ".Bfs");
412                         fsmenu_write_file(fsmenu, name);
413                         ED_area_tag_redraw(sa);
414                 }
415         }
416
417         return OPERATOR_FINISHED;
418 }
419
420 void FILE_OT_delete_bookmark(wmOperatorType *ot)
421 {
422         /* identifiers */
423         ot->name= "Delete Bookmark";
424         ot->description= "Delete selected bookmark";
425         ot->idname= "FILE_OT_delete_bookmark";
426         
427         /* api callbacks */
428         ot->exec= bookmark_delete_exec;
429         ot->poll= ED_operator_file_active;
430
431         RNA_def_int(ot->srna, "index", -1, -1, 20000, "Index", "", -1, 20000);
432 }
433
434 int file_hilight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
435 {
436         FileSelectParams* params;
437         int numfiles, actfile, origfile;
438         
439         if(sfile==NULL || sfile->files==NULL) return 0;
440
441         numfiles = filelist_numfiles(sfile->files);
442         params = ED_fileselect_get_params(sfile);
443
444         origfile= params->active_file;
445
446         mx -= ar->winrct.xmin;
447         my -= ar->winrct.ymin;
448
449         if(BLI_in_rcti(&ar->v2d.mask, mx, my)) {
450                 actfile = find_file_mouse(sfile, ar, 0, mx , my);
451
452                 if((actfile >= 0) && (actfile < numfiles))
453                         params->active_file=actfile;
454                 else
455                         params->active_file= -1;
456         }
457         else
458                 params->active_file= -1;
459
460         return (params->active_file != origfile);
461 }
462
463 static int file_highlight_invoke(bContext *C, wmOperator *op, wmEvent *event)
464 {
465         ARegion *ar= CTX_wm_region(C);
466         SpaceFile *sfile= CTX_wm_space_file(C);
467
468         if(!file_hilight_set(sfile, ar, event->x, event->y))
469                 return OPERATOR_CANCELLED;
470
471         ED_area_tag_redraw(CTX_wm_area(C));
472         
473         return OPERATOR_FINISHED;
474 }
475
476 void FILE_OT_highlight(struct wmOperatorType *ot)
477 {
478         /* identifiers */
479         ot->name= "Highlight File";
480         ot->description= "Highlight selected file(s)";
481         ot->idname= "FILE_OT_highlight";
482         
483         /* api callbacks */
484         ot->invoke= file_highlight_invoke;
485         ot->poll= ED_operator_file_active;
486 }
487
488 int file_cancel_exec(bContext *C, wmOperator *unused)
489 {
490         SpaceFile *sfile= CTX_wm_space_file(C);
491
492         folderlist_free(sfile->folders_prev);
493         folderlist_free(sfile->folders_next);
494
495         WM_event_fileselect_event(C, sfile->op, EVT_FILESELECT_CANCEL);
496         sfile->op = NULL;
497         
498         if (sfile->files) {
499                 ED_fileselect_clear(C, sfile);
500                 MEM_freeN(sfile->files);
501                 sfile->files= NULL;
502         }
503         
504         return OPERATOR_FINISHED;
505 }
506
507 int file_operator_poll(bContext *C)
508 {
509         int poll = ED_operator_file_active(C);
510         SpaceFile *sfile= CTX_wm_space_file(C);
511
512         if (!sfile || !sfile->op) poll= 0;
513
514         return poll;
515 }
516
517 void FILE_OT_cancel(struct wmOperatorType *ot)
518 {
519         /* identifiers */
520         ot->name= "Cancel File Load";
521         ot->description= "Cancel loading of selected file";
522         ot->idname= "FILE_OT_cancel";
523         
524         /* api callbacks */
525         ot->exec= file_cancel_exec;
526         ot->poll= file_operator_poll;
527 }
528
529 /* sends events now, so things get handled on windowqueue level */
530 int file_exec(bContext *C, wmOperator *exec_op)
531 {
532         SpaceFile *sfile= CTX_wm_space_file(C);
533         char name[FILE_MAX];
534         
535         if(sfile->op) {
536                 wmOperator *op= sfile->op;
537         
538                 /* when used as a macro, for doubleclick, 
539                  to prevent closing when doubleclicking on .. item */
540                 if (RNA_boolean_get(exec_op->ptr, "need_active")) {
541                         int i, active=0;
542                         struct direntry *file;
543                         
544                         for (i=0; i<filelist_numfiles(sfile->files); i++) {
545                                 file = filelist_file(sfile->files, i);
546                                 if(file->flags & ACTIVE) {
547                                         active=1;
548                                 }
549                         }
550                         if (active == 0)
551                                 return OPERATOR_CANCELLED;
552                 }
553                 
554                 sfile->op = NULL;
555                 RNA_string_set(op->ptr, "filename", sfile->params->file);
556                 BLI_strncpy(name, sfile->params->dir, sizeof(name));
557                 RNA_string_set(op->ptr, "directory", name);
558                 strcat(name, sfile->params->file); // XXX unsafe
559
560                 if(RNA_struct_find_property(op->ptr, "relative_path"))
561                         if(RNA_boolean_get(op->ptr, "relative_path"))
562                                 BLI_path_rel(name, G.sce);
563
564                 RNA_string_set(op->ptr, "path", name);
565                 
566                 /* some ops have multiple files to select */
567                 {
568                         PointerRNA itemptr;
569                         int i, numfiles = filelist_numfiles(sfile->files);
570                         struct direntry *file;
571                         if(RNA_struct_find_property(op->ptr, "files")) {
572                                 for (i=0; i<numfiles; i++) {
573                                         file = filelist_file(sfile->files, i);
574                                         if(file->flags & ACTIVE) {
575                                                 if ((file->type & S_IFDIR)==0) {
576                                                         RNA_collection_add(op->ptr, "files", &itemptr);
577                                                         RNA_string_set(&itemptr, "name", file->relname);
578                                                 }
579                                         }
580                                 }
581                         }
582                         
583                         if(RNA_struct_find_property(op->ptr, "dirs")) {
584                                 for (i=0; i<numfiles; i++) {
585                                         file = filelist_file(sfile->files, i);
586                                         if(file->flags & ACTIVE) {
587                                                 if ((file->type & S_IFDIR)) {
588                                                         RNA_collection_add(op->ptr, "dirs", &itemptr);
589                                                         RNA_string_set(&itemptr, "name", file->relname);
590                                                 }
591                                         }
592                                 }
593                         }
594                 }
595                 
596                 folderlist_free(sfile->folders_prev);
597                 folderlist_free(sfile->folders_next);
598
599                 fsmenu_insert_entry(fsmenu_get(), FS_CATEGORY_RECENT, sfile->params->dir,0, 1);
600                 BLI_make_file_string(G.sce, name, BLI_gethome(), ".Bfs");
601                 fsmenu_write_file(fsmenu_get(), name);
602                 WM_event_fileselect_event(C, op, EVT_FILESELECT_EXEC);
603
604                 ED_fileselect_clear(C, sfile);
605                 MEM_freeN(sfile->files);
606                 sfile->files= NULL;
607         }
608                                 
609         return OPERATOR_FINISHED;
610 }
611
612 void FILE_OT_execute(struct wmOperatorType *ot)
613 {
614         /* identifiers */
615         ot->name= "Execute File Window";
616         ot->description= "Execute selected file";
617         ot->idname= "FILE_OT_execute";
618         
619         /* api callbacks */
620         ot->exec= file_exec;
621         ot->poll= file_operator_poll; 
622         
623         RNA_def_boolean(ot->srna, "need_active", 0, "Need Active", "Only execute if there's an active selected file in the file list.");
624 }
625
626
627 int file_parent_exec(bContext *C, wmOperator *unused)
628 {
629         SpaceFile *sfile= CTX_wm_space_file(C);
630         
631         if(sfile->params) {
632                 if (BLI_has_parent(sfile->params->dir)) {
633                         BLI_parent_dir(sfile->params->dir);
634                         BLI_cleanup_dir(G.sce, sfile->params->dir);
635                         file_change_dir(C, 0);
636                         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
637                 }
638         }               
639         
640         return OPERATOR_FINISHED;
641
642 }
643
644
645 void FILE_OT_parent(struct wmOperatorType *ot)
646 {
647         /* identifiers */
648         ot->name= "Parent File";
649         ot->description= "Move to parent directory";
650         ot->idname= "FILE_OT_parent";
651         
652         /* api callbacks */
653         ot->exec= file_parent_exec;
654         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
655 }
656
657
658 int file_refresh_exec(bContext *C, wmOperator *unused)
659 {
660         file_change_dir(C, 1);
661
662         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
663
664         return OPERATOR_FINISHED;
665
666 }
667
668 void FILE_OT_previous(struct wmOperatorType *ot)
669 {
670         /* identifiers */
671         ot->name= "Previous Folder";
672         ot->description= "Move to previous folder";
673         ot->idname= "FILE_OT_previous";
674         
675         /* api callbacks */
676         ot->exec= file_previous_exec;
677         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
678 }
679
680 int file_previous_exec(bContext *C, wmOperator *unused)
681 {
682         SpaceFile *sfile= CTX_wm_space_file(C);
683
684         if(sfile->params) {
685                 if (!sfile->folders_next)
686                         sfile->folders_next = folderlist_new();
687
688                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
689                 folderlist_popdir(sfile->folders_prev, sfile->params->dir);
690                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
691
692                 file_change_dir(C, 1);
693         }
694         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
695
696         return OPERATOR_FINISHED;
697 }
698
699 void FILE_OT_next(struct wmOperatorType *ot)
700 {
701         /* identifiers */
702         ot->name= "Next Folder";
703         ot->description= "Move to next folder";
704         ot->idname= "FILE_OT_next";
705         
706         /* api callbacks */
707         ot->exec= file_next_exec;
708         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
709 }
710
711 int file_next_exec(bContext *C, wmOperator *unused)
712 {
713         SpaceFile *sfile= CTX_wm_space_file(C);
714         if(sfile->params) {
715                 if (!sfile->folders_next)
716                         sfile->folders_next = folderlist_new();
717
718                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
719                 folderlist_popdir(sfile->folders_next, sfile->params->dir);
720
721                 // update folder_prev so we can check for it in folderlist_clear_next()
722                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
723
724                 file_change_dir(C, 1);
725         }               
726         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
727
728         return OPERATOR_FINISHED;
729 }
730
731 /* create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created.
732    The actual name is returned in 'name', 'folder' contains the complete path, including the new folder name.
733 */
734 static int new_folder_path(const char* parent, char *folder, char *name)
735 {
736         int i = 1;
737         int len = 0;
738
739         BLI_strncpy(name, "New Folder", FILE_MAXFILE);
740         BLI_join_dirfile(folder, parent, name);
741         /* check whether folder with the name already exists, in this case
742            add number to the name. Check length of generated name to avoid
743            crazy case of huge number of folders each named 'New Folder (x)' */
744         while (BLI_exists(folder) && (len<FILE_MAXFILE)) {
745                 len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
746                 BLI_join_dirfile(folder, parent, name);
747                 i++;
748         }
749
750         return (len<FILE_MAXFILE);
751 }
752
753 int file_directory_new_exec(bContext *C, wmOperator *op)
754 {
755         char name[FILE_MAXFILE];
756         char path[FILE_MAX];
757         SpaceFile *sfile= CTX_wm_space_file(C);
758         
759         if(!sfile->params) {
760                 BKE_report(op->reports,RPT_WARNING, "No parent directory given.");
761                 return OPERATOR_CANCELLED;
762         }
763         
764         /* create a new, non-existing folder name */
765         if (!new_folder_path(sfile->params->dir, path, name)) {
766                 BKE_report(op->reports,RPT_ERROR, "Couldn't create new folder name.");
767                 return OPERATOR_CANCELLED;
768         }
769                 
770         /* rename the file */
771         BLI_recurdir_fileops(path);
772
773         if (!BLI_exists(path)) {
774                 BKE_report(op->reports,RPT_ERROR, "Couldn't create new folder.");
775                 return OPERATOR_CANCELLED;
776         } 
777
778         /* now remember file to jump into editing */
779         BLI_strncpy(sfile->params->renamefile, name, FILE_MAXFILE);
780         ED_fileselect_clear(C, sfile);
781         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
782
783         return OPERATOR_FINISHED;
784 }
785
786
787 void FILE_OT_directory_new(struct wmOperatorType *ot)
788 {
789         /* identifiers */
790         ot->name= "Create New Directory";
791         ot->description= "Create a new directory";
792         ot->idname= "FILE_OT_directory_new";
793         
794         /* api callbacks */
795         ot->invoke= WM_operator_confirm;
796         ot->exec= file_directory_new_exec;
797         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
798 }
799
800 int file_directory_exec(bContext *C, wmOperator *unused)
801 {
802         char tmpstr[FILE_MAX];
803
804         SpaceFile *sfile= CTX_wm_space_file(C);
805         
806         if(sfile->params) {
807                 if ( sfile->params->dir[0] == '~' ) {
808                         if (sfile->params->dir[1] == '\0') {
809                                 BLI_strncpy(sfile->params->dir, BLI_gethome(), sizeof(sfile->params->dir) );
810                         } else {
811                                 /* replace ~ with home */
812                                 char homestr[FILE_MAX];
813                                 char *d = &sfile->params->dir[1];
814
815                                 while ( (*d == '\\') || (*d == '/') )
816                                         d++;
817                                 BLI_strncpy(homestr,  BLI_gethome(), FILE_MAX);
818                                 BLI_join_dirfile(tmpstr, homestr, d);
819                                 BLI_strncpy(sfile->params->dir, tmpstr, sizeof(sfile->params->dir));
820                         }
821                 }
822 #ifdef WIN32
823                 if (sfile->params->dir[0] == '\0')
824                         get_default_root(sfile->params->dir);
825 #endif
826                 BLI_cleanup_dir(G.sce, sfile->params->dir);
827                 BLI_add_slash(sfile->params->dir);
828                 file_change_dir(C, 1);
829
830                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
831         }               
832         
833
834         return OPERATOR_FINISHED;
835 }
836
837 int file_filename_exec(bContext *C, wmOperator *unused)
838 {
839         SpaceFile *sfile= CTX_wm_space_file(C);
840         
841         if(sfile->params) {
842                 if (file_select_match(sfile, sfile->params->file))
843                 {
844                         sfile->params->file[0] = '\0';
845                         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
846                 }
847         }               
848
849         return OPERATOR_FINISHED;
850 }
851
852
853 void FILE_OT_refresh(struct wmOperatorType *ot)
854 {
855         /* identifiers */
856         ot->name= "Refresh Filelist";
857         ot->description= "Refresh the file list";
858         ot->idname= "FILE_OT_refresh";
859         
860         /* api callbacks */
861         ot->exec= file_refresh_exec;
862         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
863 }
864
865 int file_hidedot_exec(bContext *C, wmOperator *unused)
866 {
867         SpaceFile *sfile= CTX_wm_space_file(C);
868         
869         if(sfile->params) {
870                 sfile->params->flag ^= FILE_HIDE_DOT;
871                 ED_fileselect_clear(C, sfile);
872                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
873         }
874         
875         return OPERATOR_FINISHED;
876 }
877
878
879 void FILE_OT_hidedot(struct wmOperatorType *ot)
880 {
881         /* identifiers */
882         ot->name= "Toggle Hide Dot Files";
883         ot->description= "Toggle hide hidden dot files";
884         ot->idname= "FILE_OT_hidedot";
885         
886         /* api callbacks */
887         ot->exec= file_hidedot_exec;
888         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
889 }
890
891 struct ARegion *file_buttons_region(struct ScrArea *sa)
892 {
893         ARegion *ar, *arnew;
894         
895         for(ar= sa->regionbase.first; ar; ar= ar->next)
896                 if(ar->regiontype==RGN_TYPE_CHANNELS)
897                         return ar;
898
899         /* add subdiv level; after header */
900         for(ar= sa->regionbase.first; ar; ar= ar->next)
901                 if(ar->regiontype==RGN_TYPE_HEADER)
902                         break;
903         
904         /* is error! */
905         if(ar==NULL) return NULL;
906         
907         arnew= MEM_callocN(sizeof(ARegion), "buttons for file panels");
908         
909         BLI_insertlinkafter(&sa->regionbase, ar, arnew);
910         arnew->regiontype= RGN_TYPE_CHANNELS;
911         arnew->alignment= RGN_ALIGN_LEFT;
912         
913         arnew->flag = RGN_FLAG_HIDDEN;
914         
915         return arnew;
916 }
917
918 int file_bookmark_toggle_exec(bContext *C, wmOperator *unused)
919 {
920         ScrArea *sa= CTX_wm_area(C);
921         ARegion *ar= file_buttons_region(sa);
922         
923         if(ar)
924                 ED_region_toggle_hidden(C, ar);
925
926         return OPERATOR_FINISHED;
927 }
928
929 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
930 {
931         /* identifiers */
932         ot->name= "Toggle Bookmarks";
933         ot->description= "Toggle bookmarks display";
934         ot->idname= "FILE_OT_bookmark_toggle";
935         
936         /* api callbacks */
937         ot->exec= file_bookmark_toggle_exec;
938         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
939 }
940
941
942 int file_filenum_exec(bContext *C, wmOperator *op)
943 {
944         SpaceFile *sfile= CTX_wm_space_file(C);
945         ScrArea *sa= CTX_wm_area(C);
946         
947         int inc = RNA_int_get(op->ptr, "increment");
948         if(sfile->params && (inc != 0)) {
949                 BLI_newname(sfile->params->file, inc);
950                 ED_area_tag_redraw(sa);
951                 // WM_event_add_notifier(C, NC_WINDOW, NULL);
952         }
953         
954         return OPERATOR_FINISHED;
955
956 }
957
958 void FILE_OT_filenum(struct wmOperatorType *ot)
959 {
960         /* identifiers */
961         ot->name= "Increment Number in Filename";
962         ot->description= "Increment number in filename";
963         ot->idname= "FILE_OT_filenum";
964         
965         /* api callbacks */
966         ot->exec= file_filenum_exec;
967         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
968
969         /* props */
970         RNA_def_int(ot->srna, "increment", 1, 0, 100, "Increment", "", 0,100);
971 }
972
973 int file_rename_exec(bContext *C, wmOperator *op)
974 {
975         ScrArea *sa= CTX_wm_area(C);
976         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
977         
978         if(sfile->params) {
979                 int idx = sfile->params->active_file;
980                 int numfiles = filelist_numfiles(sfile->files);
981                 if ( (0<=idx) && (idx<numfiles) ) {
982                         struct direntry *file= filelist_file(sfile->files, idx);
983                         file->flags |= EDITING;
984                 }
985                 ED_area_tag_redraw(sa);
986         }
987         
988         return OPERATOR_FINISHED;
989
990 }
991
992 int file_rename_poll(bContext *C)
993 {
994         int poll = ED_operator_file_active(C);
995         SpaceFile *sfile= CTX_wm_space_file(C);
996
997         if (sfile && sfile->params) {
998                 if (sfile->params->active_file < 0) { 
999                         poll= 0;
1000                 } else {
1001                         char dir[FILE_MAX], group[FILE_MAX];    
1002                         if (filelist_islibrary(sfile->files, dir, group)) poll= 0;
1003                 }
1004         }
1005         else
1006                 poll= 0;
1007         return poll;
1008 }
1009
1010 void FILE_OT_rename(struct wmOperatorType *ot)
1011 {
1012         /* identifiers */
1013         ot->name= "Rename File or Directory";
1014         ot->description= "Rename file or file directory";
1015         ot->idname= "FILE_OT_rename";
1016         
1017         /* api callbacks */
1018         ot->exec= file_rename_exec;
1019         ot->poll= file_rename_poll; 
1020
1021 }
1022
1023 int file_delete_poll(bContext *C)
1024 {
1025         int poll = ED_operator_file_active(C);
1026         SpaceFile *sfile= CTX_wm_space_file(C);
1027         struct direntry* file;
1028
1029         if (sfile && sfile->params) {
1030                 if (sfile->params->active_file < 0) { 
1031                         poll= 0;
1032                 } else {
1033                         char dir[FILE_MAX], group[FILE_MAX];    
1034                         if (filelist_islibrary(sfile->files, dir, group)) poll= 0;
1035                         file = filelist_file(sfile->files, sfile->params->active_file);
1036                         if (file && S_ISDIR(file->type)) poll= 0;
1037                 }
1038         }
1039         else
1040                 poll= 0;
1041                 
1042         return poll;
1043 }
1044
1045 int file_delete_exec(bContext *C, wmOperator *op)
1046 {
1047         char str[FILE_MAX];
1048         SpaceFile *sfile= CTX_wm_space_file(C);
1049         struct direntry* file;
1050         
1051         
1052         file = filelist_file(sfile->files, sfile->params->active_file);
1053         BLI_make_file_string(G.sce, str, sfile->params->dir, file->relname);
1054         BLI_delete(str, 0, 0);  
1055         ED_fileselect_clear(C, sfile);
1056         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
1057         
1058         return OPERATOR_FINISHED;
1059
1060 }
1061
1062 void FILE_OT_delete(struct wmOperatorType *ot)
1063 {
1064         /* identifiers */
1065         ot->name= "Delete File";
1066         ot->description= "Delete selected file";
1067         ot->idname= "FILE_OT_delete";
1068         
1069         /* api callbacks */
1070         ot->invoke= WM_operator_confirm;
1071         ot->exec= file_delete_exec;
1072         ot->poll= file_delete_poll; /* <- important, handler is on window level */
1073 }
1074
1075
1076 void ED_operatormacros_file(void)
1077 {
1078         wmOperatorType *ot;
1079         wmOperatorTypeMacro *otmacro;
1080         
1081         ot= WM_operatortype_append_macro("FILE_OT_select_execute", "Select and Execute", OPTYPE_UNDO|OPTYPE_REGISTER);
1082         WM_operatortype_macro_define(ot, "FILE_OT_select");
1083         otmacro= WM_operatortype_macro_define(ot, "FILE_OT_execute");
1084         RNA_boolean_set(otmacro->ptr, "need_active", 1);
1085
1086 }