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