Fix de/select all operator naming for consistency
[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_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
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_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
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 filepath[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
571                 BLI_join_dirfile(filepath, sfile->params->dir, sfile->params->file);
572                 if(RNA_struct_find_property(op->ptr, "relative_path")) {
573                         if(RNA_boolean_get(op->ptr, "relative_path")) {
574                                 BLI_path_rel(filepath, G.sce);
575                         }
576                 }
577
578                 if(RNA_struct_find_property(op->ptr, "filename")) {
579                         RNA_string_set(op->ptr, "filename", sfile->params->file);
580                 }
581                 if(RNA_struct_find_property(op->ptr, "directory")) {
582                         RNA_string_set(op->ptr, "directory", sfile->params->dir);
583                 }
584                 if(RNA_struct_find_property(op->ptr, "filepath")) {
585                         RNA_string_set(op->ptr, "filepath", filepath);
586                 }
587                 
588                 /* some ops have multiple files to select */
589                 {
590                         PointerRNA itemptr;
591                         int i, numfiles = filelist_numfiles(sfile->files);
592                         struct direntry *file;
593                         if(RNA_struct_find_property(op->ptr, "files")) {
594                                 for (i=0; i<numfiles; i++) {
595                                         file = filelist_file(sfile->files, i);
596                                         if(file->flags & ACTIVEFILE) {
597                                                 if ((file->type & S_IFDIR)==0) {
598                                                         RNA_collection_add(op->ptr, "files", &itemptr);
599                                                         RNA_string_set(&itemptr, "name", file->relname);
600                                                 }
601                                         }
602                                 }
603                         }
604                         
605                         if(RNA_struct_find_property(op->ptr, "dirs")) {
606                                 for (i=0; i<numfiles; i++) {
607                                         file = filelist_file(sfile->files, i);
608                                         if(file->flags & ACTIVEFILE) {
609                                                 if ((file->type & S_IFDIR)) {
610                                                         RNA_collection_add(op->ptr, "dirs", &itemptr);
611                                                         RNA_string_set(&itemptr, "name", file->relname);
612                                                 }
613                                         }
614                                 }
615                         }
616                 }
617                 
618                 folderlist_free(sfile->folders_prev);
619                 folderlist_free(sfile->folders_next);
620
621                 fsmenu_insert_entry(fsmenu_get(), FS_CATEGORY_RECENT, sfile->params->dir,0, 1);
622                 BLI_make_file_string(G.sce, filepath, BLI_get_folder_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
623                 fsmenu_write_file(fsmenu_get(), filepath);
624                 WM_event_fileselect_event(C, op, EVT_FILESELECT_EXEC);
625
626                 ED_fileselect_clear(C, sfile);
627                 MEM_freeN(sfile->files);
628                 sfile->files= NULL;
629         }
630                                 
631         return OPERATOR_FINISHED;
632 }
633
634 void FILE_OT_execute(struct wmOperatorType *ot)
635 {
636         /* identifiers */
637         ot->name= "Execute File Window";
638         ot->description= "Execute selected file";
639         ot->idname= "FILE_OT_execute";
640         
641         /* api callbacks */
642         ot->exec= file_exec;
643         ot->poll= file_operator_poll; 
644         
645         RNA_def_boolean(ot->srna, "need_active", 0, "Need Active", "Only execute if there's an active selected file in the file list.");
646 }
647
648
649 int file_parent_exec(bContext *C, wmOperator *unused)
650 {
651         SpaceFile *sfile= CTX_wm_space_file(C);
652         
653         if(sfile->params) {
654                 if (BLI_has_parent(sfile->params->dir)) {
655                         BLI_parent_dir(sfile->params->dir);
656                         BLI_cleanup_dir(G.sce, sfile->params->dir);
657                         file_change_dir(C, 0);
658                         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
659                 }
660         }               
661         
662         return OPERATOR_FINISHED;
663
664 }
665
666
667 void FILE_OT_parent(struct wmOperatorType *ot)
668 {
669         /* identifiers */
670         ot->name= "Parent File";
671         ot->description= "Move to parent directory";
672         ot->idname= "FILE_OT_parent";
673         
674         /* api callbacks */
675         ot->exec= file_parent_exec;
676         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
677 }
678
679
680 int file_refresh_exec(bContext *C, wmOperator *unused)
681 {
682         SpaceFile *sfile= CTX_wm_space_file(C);
683
684         ED_fileselect_clear(C, sfile);
685
686         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
687
688         return OPERATOR_FINISHED;
689
690 }
691
692 void FILE_OT_previous(struct wmOperatorType *ot)
693 {
694         /* identifiers */
695         ot->name= "Previous Folder";
696         ot->description= "Move to previous folder";
697         ot->idname= "FILE_OT_previous";
698         
699         /* api callbacks */
700         ot->exec= file_previous_exec;
701         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
702 }
703
704 int file_previous_exec(bContext *C, wmOperator *unused)
705 {
706         SpaceFile *sfile= CTX_wm_space_file(C);
707
708         if(sfile->params) {
709                 if (!sfile->folders_next)
710                         sfile->folders_next = folderlist_new();
711
712                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
713                 folderlist_popdir(sfile->folders_prev, sfile->params->dir);
714                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
715
716                 file_change_dir(C, 1);
717         }
718         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
719
720         return OPERATOR_FINISHED;
721 }
722
723 void FILE_OT_next(struct wmOperatorType *ot)
724 {
725         /* identifiers */
726         ot->name= "Next Folder";
727         ot->description= "Move to next folder";
728         ot->idname= "FILE_OT_next";
729         
730         /* api callbacks */
731         ot->exec= file_next_exec;
732         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
733 }
734
735 int file_next_exec(bContext *C, wmOperator *unused)
736 {
737         SpaceFile *sfile= CTX_wm_space_file(C);
738         if(sfile->params) {
739                 if (!sfile->folders_next)
740                         sfile->folders_next = folderlist_new();
741
742                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
743                 folderlist_popdir(sfile->folders_next, sfile->params->dir);
744
745                 // update folder_prev so we can check for it in folderlist_clear_next()
746                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
747
748                 file_change_dir(C, 1);
749         }               
750         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
751
752         return OPERATOR_FINISHED;
753 }
754
755
756 /* only meant for timer usage */
757 static int file_smoothscroll_invoke(bContext *C, wmOperator *op, wmEvent *event)
758 {
759         ScrArea *sa = CTX_wm_area(C);
760         SpaceFile *sfile= CTX_wm_space_file(C);
761         ARegion *ar, *oldar= CTX_wm_region(C);
762         int numfiles, offset;
763         int edit_idx = 0;
764         int numfiles_layout;
765         int i;
766
767         /* escape if not our timer */
768         if(sfile->smoothscroll_timer==NULL || sfile->smoothscroll_timer!=event->customdata)
769                 return OPERATOR_PASS_THROUGH;
770         
771         numfiles = filelist_numfiles(sfile->files);
772
773         /* check if we are editing a name */
774         for (i=0; i < numfiles; ++i)
775         {
776                 struct direntry *file = filelist_file(sfile->files, i); 
777                 if (file->flags & EDITING) {
778                         edit_idx=i;
779                         break;
780                 }
781         }
782
783         /* if we are not editing, we are done */
784         if (0==edit_idx) {
785                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
786                 sfile->smoothscroll_timer=NULL;
787                 return OPERATOR_PASS_THROUGH;
788         }
789
790         /* we need the correct area for scrolling */
791         ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
792         if (!ar || ar->regiontype != RGN_TYPE_WINDOW) {
793                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
794                 sfile->smoothscroll_timer=NULL;
795                 return OPERATOR_PASS_THROUGH;
796         }
797
798         offset = ED_fileselect_layout_offset(sfile->layout, 0, ar->v2d.cur.xmin, -ar->v2d.cur.ymax);
799         if (offset<0) offset=0;
800
801         /* scroll offset is the first file in the row/column we are editing in */
802         if (sfile->scroll_offset == 0) {
803                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
804                         sfile->scroll_offset = (edit_idx/sfile->layout->rows)*sfile->layout->rows;
805                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->rows;
806                 } else {
807                         sfile->scroll_offset = (edit_idx/sfile->layout->columns)*sfile->layout->columns;
808                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->columns;
809                 }
810         }
811         
812         numfiles_layout = ED_fileselect_layout_numfiles(sfile->layout, ar);
813         
814         /* check if we have reached our final scroll position */
815         if ( (sfile->scroll_offset >= offset) && (sfile->scroll_offset < offset + numfiles_layout) ) {
816                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
817                 sfile->smoothscroll_timer=NULL;
818                 return OPERATOR_FINISHED;
819         }
820
821         /* temporarily set context to the main window region, 
822          * so the scroll operators work */
823         CTX_wm_region_set(C, ar);
824         
825         /* scroll one step in the desired direction */
826         if (sfile->scroll_offset < offset) {
827                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
828                         WM_operator_name_call(C, "VIEW2D_OT_scroll_left", 0, NULL);
829                 } else {
830                         WM_operator_name_call(C, "VIEW2D_OT_scroll_up", 0, NULL);
831                 }
832                 
833         } else {
834                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
835                         WM_operator_name_call(C, "VIEW2D_OT_scroll_right", 0, NULL);
836                 } else {
837                         WM_operator_name_call(C, "VIEW2D_OT_scroll_down", 0, NULL);
838                 }
839         }
840         
841         ED_region_tag_redraw(CTX_wm_region(C));
842         
843         /* and restore context */
844         CTX_wm_region_set(C, oldar);
845         
846         return OPERATOR_FINISHED;
847 }
848
849
850 void FILE_OT_smoothscroll(wmOperatorType *ot)
851 {
852         
853         /* identifiers */
854         ot->name= "Smooth Scroll";
855         ot->idname= "FILE_OT_smoothscroll";
856         ot->description="Smooth scroll to make editable file visible.";
857         
858         /* api callbacks */
859         ot->invoke= file_smoothscroll_invoke;
860         
861         ot->poll= ED_operator_file_active;
862 }
863
864
865 /* create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created.
866    The actual name is returned in 'name', 'folder' contains the complete path, including the new folder name.
867 */
868 static int new_folder_path(const char* parent, char *folder, char *name)
869 {
870         int i = 1;
871         int len = 0;
872
873         BLI_strncpy(name, "New Folder", FILE_MAXFILE);
874         BLI_join_dirfile(folder, parent, name);
875         /* check whether folder with the name already exists, in this case
876            add number to the name. Check length of generated name to avoid
877            crazy case of huge number of folders each named 'New Folder (x)' */
878         while (BLI_exists(folder) && (len<FILE_MAXFILE)) {
879                 len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
880                 BLI_join_dirfile(folder, parent, name);
881                 i++;
882         }
883
884         return (len<FILE_MAXFILE);
885 }
886
887 int file_directory_new_exec(bContext *C, wmOperator *op)
888 {
889         char name[FILE_MAXFILE];
890         char path[FILE_MAX];
891         SpaceFile *sfile= CTX_wm_space_file(C);
892         
893         if(!sfile->params) {
894                 BKE_report(op->reports,RPT_WARNING, "No parent directory given.");
895                 return OPERATOR_CANCELLED;
896         }
897         
898         /* create a new, non-existing folder name */
899         if (!new_folder_path(sfile->params->dir, path, name)) {
900                 BKE_report(op->reports,RPT_ERROR, "Couldn't create new folder name.");
901                 return OPERATOR_CANCELLED;
902         }
903                 
904         /* rename the file */
905         BLI_recurdir_fileops(path);
906
907         if (!BLI_exists(path)) {
908                 BKE_report(op->reports,RPT_ERROR, "Couldn't create new folder.");
909                 return OPERATOR_CANCELLED;
910         } 
911
912         /* now remember file to jump into editing */
913         BLI_strncpy(sfile->params->renamefile, name, FILE_MAXFILE);
914
915         /* set timer to smoothly view newly generated file */
916         sfile->smoothscroll_timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER1, 1.0/1000.0);        /* max 30 frs/sec */
917         sfile->scroll_offset=0;
918
919         /* reload dir to make sure we're seeing what's in the directory */
920         ED_fileselect_clear(C, sfile);
921         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
922
923         return OPERATOR_FINISHED;
924 }
925
926
927 void FILE_OT_directory_new(struct wmOperatorType *ot)
928 {
929         /* identifiers */
930         ot->name= "Create New Directory";
931         ot->description= "Create a new directory";
932         ot->idname= "FILE_OT_directory_new";
933         
934         /* api callbacks */
935         ot->invoke= WM_operator_confirm;
936         ot->exec= file_directory_new_exec;
937         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
938 }
939
940 int file_directory_exec(bContext *C, wmOperator *unused)
941 {
942         SpaceFile *sfile= CTX_wm_space_file(C);
943         
944         if(sfile->params) {
945                 if ( sfile->params->dir[0] == '~' ) {
946                         char tmpstr[sizeof(sfile->params->dir)-1];
947                         strncpy(tmpstr, sfile->params->dir+1, sizeof(tmpstr));
948                         BLI_join_dirfile(sfile->params->dir, BLI_getDefaultDocumentFolder(), tmpstr);
949                 }
950
951 #ifdef WIN32
952                 if (sfile->params->dir[0] == '\0')
953                         get_default_root(sfile->params->dir);
954 #endif
955                 BLI_cleanup_dir(G.sce, sfile->params->dir);
956                 BLI_add_slash(sfile->params->dir);
957                 file_change_dir(C, 1);
958
959                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
960         }               
961         
962
963         return OPERATOR_FINISHED;
964 }
965
966 int file_filename_exec(bContext *C, wmOperator *unused)
967 {
968         SpaceFile *sfile= CTX_wm_space_file(C);
969         
970         if(sfile->params) {
971                 if (file_select_match(sfile, sfile->params->file))
972                 {
973                         sfile->params->file[0] = '\0';
974                         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_PARAMS, NULL);
975                 }
976         }               
977
978         return OPERATOR_FINISHED;
979 }
980
981
982 void FILE_OT_refresh(struct wmOperatorType *ot)
983 {
984         /* identifiers */
985         ot->name= "Refresh Filelist";
986         ot->description= "Refresh the file list";
987         ot->idname= "FILE_OT_refresh";
988         
989         /* api callbacks */
990         ot->exec= file_refresh_exec;
991         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
992 }
993
994 int file_hidedot_exec(bContext *C, wmOperator *unused)
995 {
996         SpaceFile *sfile= CTX_wm_space_file(C);
997         
998         if(sfile->params) {
999                 sfile->params->flag ^= FILE_HIDE_DOT;
1000                 ED_fileselect_clear(C, sfile);
1001                 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
1002         }
1003         
1004         return OPERATOR_FINISHED;
1005 }
1006
1007
1008 void FILE_OT_hidedot(struct wmOperatorType *ot)
1009 {
1010         /* identifiers */
1011         ot->name= "Toggle Hide Dot Files";
1012         ot->description= "Toggle hide hidden dot files";
1013         ot->idname= "FILE_OT_hidedot";
1014         
1015         /* api callbacks */
1016         ot->exec= file_hidedot_exec;
1017         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1018 }
1019
1020 struct ARegion *file_buttons_region(struct ScrArea *sa)
1021 {
1022         ARegion *ar, *arnew;
1023         
1024         for(ar= sa->regionbase.first; ar; ar= ar->next)
1025                 if(ar->regiontype==RGN_TYPE_CHANNELS)
1026                         return ar;
1027
1028         /* add subdiv level; after header */
1029         for(ar= sa->regionbase.first; ar; ar= ar->next)
1030                 if(ar->regiontype==RGN_TYPE_HEADER)
1031                         break;
1032         
1033         /* is error! */
1034         if(ar==NULL) return NULL;
1035         
1036         arnew= MEM_callocN(sizeof(ARegion), "buttons for file panels");
1037         
1038         BLI_insertlinkafter(&sa->regionbase, ar, arnew);
1039         arnew->regiontype= RGN_TYPE_CHANNELS;
1040         arnew->alignment= RGN_ALIGN_LEFT;
1041         
1042         arnew->flag = RGN_FLAG_HIDDEN;
1043         
1044         return arnew;
1045 }
1046
1047 int file_bookmark_toggle_exec(bContext *C, wmOperator *unused)
1048 {
1049         ScrArea *sa= CTX_wm_area(C);
1050         ARegion *ar= file_buttons_region(sa);
1051         
1052         if(ar)
1053                 ED_region_toggle_hidden(C, ar);
1054
1055         return OPERATOR_FINISHED;
1056 }
1057
1058 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
1059 {
1060         /* identifiers */
1061         ot->name= "Toggle Bookmarks";
1062         ot->description= "Toggle bookmarks display";
1063         ot->idname= "FILE_OT_bookmark_toggle";
1064         
1065         /* api callbacks */
1066         ot->exec= file_bookmark_toggle_exec;
1067         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1068 }
1069
1070
1071 int file_filenum_exec(bContext *C, wmOperator *op)
1072 {
1073         SpaceFile *sfile= CTX_wm_space_file(C);
1074         ScrArea *sa= CTX_wm_area(C);
1075         
1076         int inc = RNA_int_get(op->ptr, "increment");
1077         if(sfile->params && (inc != 0)) {
1078                 BLI_newname(sfile->params->file, inc);
1079                 ED_area_tag_redraw(sa);
1080                 // WM_event_add_notifier(C, NC_WINDOW, NULL);
1081         }
1082         
1083         return OPERATOR_FINISHED;
1084
1085 }
1086
1087 void FILE_OT_filenum(struct wmOperatorType *ot)
1088 {
1089         /* identifiers */
1090         ot->name= "Increment Number in Filename";
1091         ot->description= "Increment number in filename";
1092         ot->idname= "FILE_OT_filenum";
1093         
1094         /* api callbacks */
1095         ot->exec= file_filenum_exec;
1096         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
1097
1098         /* props */
1099         RNA_def_int(ot->srna, "increment", 1, 0, 100, "Increment", "", 0,100);
1100 }
1101
1102 int file_rename_exec(bContext *C, wmOperator *op)
1103 {
1104         ScrArea *sa= CTX_wm_area(C);
1105         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
1106         
1107         if(sfile->params) {
1108                 int idx = sfile->params->active_file;
1109                 int numfiles = filelist_numfiles(sfile->files);
1110                 if ( (0<=idx) && (idx<numfiles) ) {
1111                         struct direntry *file= filelist_file(sfile->files, idx);
1112                         file->flags |= EDITING;
1113                 }
1114                 ED_area_tag_redraw(sa);
1115         }
1116         
1117         return OPERATOR_FINISHED;
1118
1119 }
1120
1121 int file_rename_poll(bContext *C)
1122 {
1123         int poll = ED_operator_file_active(C);
1124         SpaceFile *sfile= CTX_wm_space_file(C);
1125
1126         if (sfile && sfile->params) {
1127                 if (sfile->params->active_file < 0) { 
1128                         poll= 0;
1129                 } else {
1130                         char dir[FILE_MAX], group[FILE_MAX];    
1131                         if (filelist_islibrary(sfile->files, dir, group)) poll= 0;
1132                 }
1133         }
1134         else
1135                 poll= 0;
1136         return poll;
1137 }
1138
1139 void FILE_OT_rename(struct wmOperatorType *ot)
1140 {
1141         /* identifiers */
1142         ot->name= "Rename File or Directory";
1143         ot->description= "Rename file or file directory";
1144         ot->idname= "FILE_OT_rename";
1145         
1146         /* api callbacks */
1147         ot->exec= file_rename_exec;
1148         ot->poll= file_rename_poll; 
1149
1150 }
1151
1152 int file_delete_poll(bContext *C)
1153 {
1154         int poll = ED_operator_file_active(C);
1155         SpaceFile *sfile= CTX_wm_space_file(C);
1156         struct direntry* file;
1157
1158         if (sfile && sfile->params) {
1159                 if (sfile->params->active_file < 0) { 
1160                         poll= 0;
1161                 } else {
1162                         char dir[FILE_MAX], group[FILE_MAX];    
1163                         if (filelist_islibrary(sfile->files, dir, group)) poll= 0;
1164                         file = filelist_file(sfile->files, sfile->params->active_file);
1165                         if (file && S_ISDIR(file->type)) poll= 0;
1166                 }
1167         }
1168         else
1169                 poll= 0;
1170                 
1171         return poll;
1172 }
1173
1174 int file_delete_exec(bContext *C, wmOperator *op)
1175 {
1176         char str[FILE_MAX];
1177         SpaceFile *sfile= CTX_wm_space_file(C);
1178         struct direntry* file;
1179         
1180         
1181         file = filelist_file(sfile->files, sfile->params->active_file);
1182         BLI_make_file_string(G.sce, str, sfile->params->dir, file->relname);
1183         BLI_delete(str, 0, 0);  
1184         ED_fileselect_clear(C, sfile);
1185         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
1186         
1187         return OPERATOR_FINISHED;
1188
1189 }
1190
1191 void FILE_OT_delete(struct wmOperatorType *ot)
1192 {
1193         /* identifiers */
1194         ot->name= "Delete File";
1195         ot->description= "Delete selected file";
1196         ot->idname= "FILE_OT_delete";
1197         
1198         /* api callbacks */
1199         ot->invoke= WM_operator_confirm;
1200         ot->exec= file_delete_exec;
1201         ot->poll= file_delete_poll; /* <- important, handler is on window level */
1202 }
1203
1204
1205 void ED_operatormacros_file(void)
1206 {
1207         wmOperatorType *ot;
1208         wmOperatorTypeMacro *otmacro;
1209         
1210         ot= WM_operatortype_append_macro("FILE_OT_select_execute", "Select and Execute", OPTYPE_UNDO|OPTYPE_REGISTER);
1211         WM_operatortype_macro_define(ot, "FILE_OT_select");
1212         otmacro= WM_operatortype_macro_define(ot, "FILE_OT_execute");
1213         RNA_boolean_set(otmacro->ptr, "need_active", 1);
1214
1215 }