Tweak for elubie, scroll main file selector window immediately when newly added
[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
749 /* only meant for timer usage */
750 static int file_smoothscroll_invoke(bContext *C, wmOperator *op, wmEvent *event)
751 {
752         ScrArea *sa = CTX_wm_area(C);
753         SpaceFile *sfile= CTX_wm_space_file(C);
754         ARegion *ar, *oldar= CTX_wm_region(C);
755         int numfiles, offset;
756         int edit_idx = 0;
757         int numfiles_layout;
758         int i;
759
760         /* escape if not our timer */
761         if(sfile->smoothscroll_timer==NULL || sfile->smoothscroll_timer!=event->customdata)
762                 return OPERATOR_PASS_THROUGH;
763         
764         numfiles = filelist_numfiles(sfile->files);
765
766         /* check if we are editing a name */
767         for (i=0; i < numfiles; ++i)
768         {
769                 struct direntry *file = filelist_file(sfile->files, i); 
770                 if (file->flags & EDITING) {
771                         edit_idx=i;
772                         break;
773                 }
774         }
775
776         /* if we are not editing, we are done */
777         if (0==edit_idx) {
778                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
779                 sfile->smoothscroll_timer=NULL;
780                 return OPERATOR_PASS_THROUGH;
781         }
782
783         /* we need the correct area for scrolling */
784         ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
785         if (!ar || ar->regiontype != RGN_TYPE_WINDOW) {
786                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
787                 sfile->smoothscroll_timer=NULL;
788                 return OPERATOR_PASS_THROUGH;
789         }
790
791         offset = ED_fileselect_layout_offset(sfile->layout, 0, ar->v2d.cur.xmin, -ar->v2d.cur.ymax);
792         if (offset<0) offset=0;
793
794         /* scroll offset is the first file in the row/column we are editing in */
795         if (sfile->scroll_offset == 0) {
796                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
797                         sfile->scroll_offset = (edit_idx/sfile->layout->rows)*sfile->layout->rows;
798                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->rows;
799                 } else {
800                         sfile->scroll_offset = (edit_idx/sfile->layout->columns)*sfile->layout->columns;
801                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->columns;
802                 }
803         }
804         
805         numfiles_layout = ED_fileselect_layout_numfiles(sfile->layout, ar);
806         
807         /* check if we have reached our final scroll position */
808         if ( (sfile->scroll_offset >= offset) && (sfile->scroll_offset < offset + numfiles_layout) ) {
809                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
810                 sfile->smoothscroll_timer=NULL;
811                 return OPERATOR_FINISHED;
812         }
813
814         /* temporarily set context to the main window region, 
815          * so the scroll operators work */
816         CTX_wm_region_set(C, ar);
817         
818         /* scroll one step in the desired direction */
819         if (sfile->scroll_offset < offset) {
820                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
821                         WM_operator_name_call(C, "VIEW2D_OT_scroll_left", 0, NULL);
822                 } else {
823                         WM_operator_name_call(C, "VIEW2D_OT_scroll_up", 0, NULL);
824                 }
825                 
826         } else {
827                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
828                         WM_operator_name_call(C, "VIEW2D_OT_scroll_right", 0, NULL);
829                 } else {
830                         WM_operator_name_call(C, "VIEW2D_OT_scroll_down", 0, NULL);
831                 }
832         }
833         
834         ED_region_tag_redraw(CTX_wm_region(C));
835         
836         /* and restore context */
837         CTX_wm_region_set(C, oldar);
838         
839         return OPERATOR_FINISHED;
840 }
841
842
843 void FILE_OT_smoothscroll(wmOperatorType *ot)
844 {
845         
846         /* identifiers */
847         ot->name= "Smooth Scroll";
848         ot->idname= "FILE_OT_smoothscroll";
849         ot->description="Smooth scroll to make editable file visible.";
850         
851         /* api callbacks */
852         ot->invoke= file_smoothscroll_invoke;
853         
854         ot->poll= ED_operator_file_active;
855 }
856
857
858 /* create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created.
859    The actual name is returned in 'name', 'folder' contains the complete path, including the new folder name.
860 */
861 static int new_folder_path(const char* parent, char *folder, char *name)
862 {
863         int i = 1;
864         int len = 0;
865
866         BLI_strncpy(name, "New Folder", FILE_MAXFILE);
867         BLI_join_dirfile(folder, parent, name);
868         /* check whether folder with the name already exists, in this case
869            add number to the name. Check length of generated name to avoid
870            crazy case of huge number of folders each named 'New Folder (x)' */
871         while (BLI_exists(folder) && (len<FILE_MAXFILE)) {
872                 len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
873                 BLI_join_dirfile(folder, parent, name);
874                 i++;
875         }
876
877         return (len<FILE_MAXFILE);
878 }
879
880 int file_directory_new_exec(bContext *C, wmOperator *op)
881 {
882         char name[FILE_MAXFILE];
883         char path[FILE_MAX];
884         SpaceFile *sfile= CTX_wm_space_file(C);
885         
886         if(!sfile->params) {
887                 BKE_report(op->reports,RPT_WARNING, "No parent directory given.");
888                 return OPERATOR_CANCELLED;
889         }
890         
891         /* create a new, non-existing folder name */
892         if (!new_folder_path(sfile->params->dir, path, name)) {
893                 BKE_report(op->reports,RPT_ERROR, "Couldn't create new folder name.");
894                 return OPERATOR_CANCELLED;
895         }
896                 
897         /* rename the file */
898         BLI_recurdir_fileops(path);
899
900         if (!BLI_exists(path)) {
901                 BKE_report(op->reports,RPT_ERROR, "Couldn't create new folder.");
902                 return OPERATOR_CANCELLED;
903         } 
904
905         /* now remember file to jump into editing */
906         BLI_strncpy(sfile->params->renamefile, name, FILE_MAXFILE);
907
908         /* set timer to smoothly view newly generated file */
909         sfile->smoothscroll_timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER1, 1.0/1000.0);        /* max 30 frs/sec */
910         sfile->scroll_offset=0;
911
912         /* reload dir to make sure we're seeing what's in the directory */
913         ED_fileselect_clear(C, sfile);
914         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
915
916         return OPERATOR_FINISHED;
917 }
918
919
920 void FILE_OT_directory_new(struct wmOperatorType *ot)
921 {
922         /* identifiers */
923         ot->name= "Create New Directory";
924         ot->description= "Create a new directory";
925         ot->idname= "FILE_OT_directory_new";
926         
927         /* api callbacks */
928         ot->invoke= WM_operator_confirm;
929         ot->exec= file_directory_new_exec;
930         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
931 }
932
933 int file_directory_exec(bContext *C, wmOperator *unused)
934 {
935         SpaceFile *sfile= CTX_wm_space_file(C);
936         
937         if(sfile->params) {
938                 if ( sfile->params->dir[0] == '~' ) {
939                         char tmpstr[sizeof(sfile->params->dir)-1];
940                         strncpy(tmpstr, sfile->params->dir+1, sizeof(tmpstr));
941                         BLI_join_dirfile(sfile->params->dir, BLI_gethome(), tmpstr);
942                 }
943
944 #ifdef WIN32
945                 if (sfile->params->dir[0] == '\0')
946                         get_default_root(sfile->params->dir);
947 #endif
948                 BLI_cleanup_dir(G.sce, sfile->params->dir);
949                 BLI_add_slash(sfile->params->dir);
950                 file_change_dir(C, 1);
951
952                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
953         }               
954         
955
956         return OPERATOR_FINISHED;
957 }
958
959 int file_filename_exec(bContext *C, wmOperator *unused)
960 {
961         SpaceFile *sfile= CTX_wm_space_file(C);
962         
963         if(sfile->params) {
964                 if (file_select_match(sfile, sfile->params->file))
965                 {
966                         sfile->params->file[0] = '\0';
967                         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
968                 }
969         }               
970
971         return OPERATOR_FINISHED;
972 }
973
974
975 void FILE_OT_refresh(struct wmOperatorType *ot)
976 {
977         /* identifiers */
978         ot->name= "Refresh Filelist";
979         ot->description= "Refresh the file list";
980         ot->idname= "FILE_OT_refresh";
981         
982         /* api callbacks */
983         ot->exec= file_refresh_exec;
984         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
985 }
986
987 int file_hidedot_exec(bContext *C, wmOperator *unused)
988 {
989         SpaceFile *sfile= CTX_wm_space_file(C);
990         
991         if(sfile->params) {
992                 sfile->params->flag ^= FILE_HIDE_DOT;
993                 ED_fileselect_clear(C, sfile);
994                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
995         }
996         
997         return OPERATOR_FINISHED;
998 }
999
1000
1001 void FILE_OT_hidedot(struct wmOperatorType *ot)
1002 {
1003         /* identifiers */
1004         ot->name= "Toggle Hide Dot Files";
1005         ot->description= "Toggle hide hidden dot files";
1006         ot->idname= "FILE_OT_hidedot";
1007         
1008         /* api callbacks */
1009         ot->exec= file_hidedot_exec;
1010         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1011 }
1012
1013 struct ARegion *file_buttons_region(struct ScrArea *sa)
1014 {
1015         ARegion *ar, *arnew;
1016         
1017         for(ar= sa->regionbase.first; ar; ar= ar->next)
1018                 if(ar->regiontype==RGN_TYPE_CHANNELS)
1019                         return ar;
1020
1021         /* add subdiv level; after header */
1022         for(ar= sa->regionbase.first; ar; ar= ar->next)
1023                 if(ar->regiontype==RGN_TYPE_HEADER)
1024                         break;
1025         
1026         /* is error! */
1027         if(ar==NULL) return NULL;
1028         
1029         arnew= MEM_callocN(sizeof(ARegion), "buttons for file panels");
1030         
1031         BLI_insertlinkafter(&sa->regionbase, ar, arnew);
1032         arnew->regiontype= RGN_TYPE_CHANNELS;
1033         arnew->alignment= RGN_ALIGN_LEFT;
1034         
1035         arnew->flag = RGN_FLAG_HIDDEN;
1036         
1037         return arnew;
1038 }
1039
1040 int file_bookmark_toggle_exec(bContext *C, wmOperator *unused)
1041 {
1042         ScrArea *sa= CTX_wm_area(C);
1043         ARegion *ar= file_buttons_region(sa);
1044         
1045         if(ar)
1046                 ED_region_toggle_hidden(C, ar);
1047
1048         return OPERATOR_FINISHED;
1049 }
1050
1051 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
1052 {
1053         /* identifiers */
1054         ot->name= "Toggle Bookmarks";
1055         ot->description= "Toggle bookmarks display";
1056         ot->idname= "FILE_OT_bookmark_toggle";
1057         
1058         /* api callbacks */
1059         ot->exec= file_bookmark_toggle_exec;
1060         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1061 }
1062
1063
1064 int file_filenum_exec(bContext *C, wmOperator *op)
1065 {
1066         SpaceFile *sfile= CTX_wm_space_file(C);
1067         ScrArea *sa= CTX_wm_area(C);
1068         
1069         int inc = RNA_int_get(op->ptr, "increment");
1070         if(sfile->params && (inc != 0)) {
1071                 BLI_newname(sfile->params->file, inc);
1072                 ED_area_tag_redraw(sa);
1073                 // WM_event_add_notifier(C, NC_WINDOW, NULL);
1074         }
1075         
1076         return OPERATOR_FINISHED;
1077
1078 }
1079
1080 void FILE_OT_filenum(struct wmOperatorType *ot)
1081 {
1082         /* identifiers */
1083         ot->name= "Increment Number in Filename";
1084         ot->description= "Increment number in filename";
1085         ot->idname= "FILE_OT_filenum";
1086         
1087         /* api callbacks */
1088         ot->exec= file_filenum_exec;
1089         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1090
1091         /* props */
1092         RNA_def_int(ot->srna, "increment", 1, 0, 100, "Increment", "", 0,100);
1093 }
1094
1095 int file_rename_exec(bContext *C, wmOperator *op)
1096 {
1097         ScrArea *sa= CTX_wm_area(C);
1098         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
1099         
1100         if(sfile->params) {
1101                 int idx = sfile->params->active_file;
1102                 int numfiles = filelist_numfiles(sfile->files);
1103                 if ( (0<=idx) && (idx<numfiles) ) {
1104                         struct direntry *file= filelist_file(sfile->files, idx);
1105                         file->flags |= EDITING;
1106                 }
1107                 ED_area_tag_redraw(sa);
1108         }
1109         
1110         return OPERATOR_FINISHED;
1111
1112 }
1113
1114 int file_rename_poll(bContext *C)
1115 {
1116         int poll = ED_operator_file_active(C);
1117         SpaceFile *sfile= CTX_wm_space_file(C);
1118
1119         if (sfile && sfile->params) {
1120                 if (sfile->params->active_file < 0) { 
1121                         poll= 0;
1122                 } else {
1123                         char dir[FILE_MAX], group[FILE_MAX];    
1124                         if (filelist_islibrary(sfile->files, dir, group)) poll= 0;
1125                 }
1126         }
1127         else
1128                 poll= 0;
1129         return poll;
1130 }
1131
1132 void FILE_OT_rename(struct wmOperatorType *ot)
1133 {
1134         /* identifiers */
1135         ot->name= "Rename File or Directory";
1136         ot->description= "Rename file or file directory";
1137         ot->idname= "FILE_OT_rename";
1138         
1139         /* api callbacks */
1140         ot->exec= file_rename_exec;
1141         ot->poll= file_rename_poll; 
1142
1143 }
1144
1145 int file_delete_poll(bContext *C)
1146 {
1147         int poll = ED_operator_file_active(C);
1148         SpaceFile *sfile= CTX_wm_space_file(C);
1149         struct direntry* file;
1150
1151         if (sfile && sfile->params) {
1152                 if (sfile->params->active_file < 0) { 
1153                         poll= 0;
1154                 } else {
1155                         char dir[FILE_MAX], group[FILE_MAX];    
1156                         if (filelist_islibrary(sfile->files, dir, group)) poll= 0;
1157                         file = filelist_file(sfile->files, sfile->params->active_file);
1158                         if (file && S_ISDIR(file->type)) poll= 0;
1159                 }
1160         }
1161         else
1162                 poll= 0;
1163                 
1164         return poll;
1165 }
1166
1167 int file_delete_exec(bContext *C, wmOperator *op)
1168 {
1169         char str[FILE_MAX];
1170         SpaceFile *sfile= CTX_wm_space_file(C);
1171         struct direntry* file;
1172         
1173         
1174         file = filelist_file(sfile->files, sfile->params->active_file);
1175         BLI_make_file_string(G.sce, str, sfile->params->dir, file->relname);
1176         BLI_delete(str, 0, 0);  
1177         ED_fileselect_clear(C, sfile);
1178         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
1179         
1180         return OPERATOR_FINISHED;
1181
1182 }
1183
1184 void FILE_OT_delete(struct wmOperatorType *ot)
1185 {
1186         /* identifiers */
1187         ot->name= "Delete File";
1188         ot->description= "Delete selected file";
1189         ot->idname= "FILE_OT_delete";
1190         
1191         /* api callbacks */
1192         ot->invoke= WM_operator_confirm;
1193         ot->exec= file_delete_exec;
1194         ot->poll= file_delete_poll; /* <- important, handler is on window level */
1195 }
1196
1197
1198 void ED_operatormacros_file(void)
1199 {
1200         wmOperatorType *ot;
1201         wmOperatorTypeMacro *otmacro;
1202         
1203         ot= WM_operatortype_append_macro("FILE_OT_select_execute", "Select and Execute", OPTYPE_UNDO|OPTYPE_REGISTER);
1204         WM_operatortype_macro_define(ot, "FILE_OT_select");
1205         otmacro= WM_operatortype_macro_define(ot, "FILE_OT_execute");
1206         RNA_boolean_set(otmacro->ptr, "need_active", 1);
1207
1208 }