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