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