FileBrowser: Editable Bookmarks.
[blender-staging.git] / source / blender / editors / space_file / file_ops.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Andrea Weikert (c) 2008 Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_file/file_ops.c
28  *  \ingroup spfile
29  */
30
31 #include "BLI_blenlib.h"
32 #include "BLI_utildefines.h"
33 #include "BLI_fileops_types.h"
34 #include "BLI_linklist.h"
35
36 #include "BLO_readfile.h"
37
38 #include "BKE_appdir.h"
39 #include "BKE_context.h"
40 #include "BKE_screen.h"
41 #include "BKE_global.h"
42 #include "BKE_report.h"
43 #include "BKE_main.h"
44
45 #ifdef WIN32
46 #  include "BLI_winstuff.h"
47 #endif
48
49 #include "ED_screen.h"
50 #include "ED_fileselect.h"
51
52 #include "UI_interface.h"
53
54 #include "MEM_guardedalloc.h"
55
56 #include "RNA_access.h"
57 #include "RNA_define.h"
58
59 #include "UI_view2d.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64 #include "file_intern.h"
65 #include "filelist.h"
66 #include "fsmenu.h"
67
68 #include <stdlib.h>
69 #include <string.h>
70 #include <stdio.h>
71 #include <ctype.h>
72
73 /* ---------- FILE SELECTION ------------ */
74 static FileSelection find_file_mouse_rect(SpaceFile *sfile, ARegion *ar, const rcti *rect_region)
75 {
76         FileSelection sel;
77         
78         View2D *v2d = &ar->v2d;
79         rcti rect_view;
80         rctf rect_view_fl;
81         rctf rect_region_fl;
82
83         BLI_rctf_rcti_copy(&rect_region_fl, rect_region);
84
85         UI_view2d_region_to_view_rctf(v2d, &rect_region_fl, &rect_view_fl);
86
87         BLI_rcti_init(&rect_view,
88                       (int)(v2d->tot.xmin + rect_view_fl.xmin),
89                       (int)(v2d->tot.xmin + rect_view_fl.xmax),
90                       (int)(v2d->tot.ymax - rect_view_fl.ymin),
91                       (int)(v2d->tot.ymax - rect_view_fl.ymax));
92
93         sel  = ED_fileselect_layout_offset_rect(sfile->layout, &rect_view);
94         
95         return sel;
96 }
97
98 static void file_deselect_all(SpaceFile *sfile, unsigned int flag)
99 {
100         FileSelection sel;
101         sel.first = 0;
102         sel.last = filelist_numfiles(sfile->files) - 1;
103         
104         filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, flag, CHECK_ALL);
105 }
106
107 typedef enum FileSelect { 
108         FILE_SELECT_NOTHING = 0,
109         FILE_SELECT_DIR = 1, 
110         FILE_SELECT_FILE = 2 
111 } FileSelect;
112
113 static void clamp_to_filelist(int numfiles, FileSelection *sel)
114 {
115         /* border select before the first file */
116         if ( (sel->first < 0) && (sel->last >= 0) ) {
117                 sel->first = 0;
118         }
119         /* don't select if everything is outside filelist */
120         if ( (sel->first >= numfiles) && ((sel->last < 0) || (sel->last >= numfiles)) ) {
121                 sel->first = -1;
122                 sel->last = -1;
123         }
124         
125         /* fix if last file invalid */
126         if ( (sel->first > 0) && (sel->last < 0) )
127                 sel->last = numfiles - 1;
128
129         /* clamp */
130         if ( (sel->first >= numfiles) ) {
131                 sel->first = numfiles - 1;
132         }
133         if ( (sel->last >= numfiles) ) {
134                 sel->last = numfiles - 1;
135         }
136 }
137
138 static FileSelection file_selection_get(bContext *C, const rcti *rect, bool fill)
139 {
140         ARegion *ar = CTX_wm_region(C);
141         SpaceFile *sfile = CTX_wm_space_file(C);
142         int numfiles = filelist_numfiles(sfile->files);
143         FileSelection sel;
144
145         sel = find_file_mouse_rect(sfile, ar, rect);
146         if (!((sel.first == -1) && (sel.last == -1)) ) {
147                 clamp_to_filelist(numfiles, &sel);
148         }
149
150
151         /* if desired, fill the selection up from the last selected file to the current one */
152         if (fill && (sel.last >= 0) && (sel.last < numfiles) ) {
153                 int f = sel.last;
154                 while (f >= 0) {
155                         if (filelist_is_selected(sfile->files, f, CHECK_ALL) )
156                                 break;
157                         f--;
158                 }
159                 if (f >= 0) {
160                         sel.first = f + 1;
161                 }
162         }
163         return sel;
164 }
165
166 static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
167 {
168         FileSelect retval = FILE_SELECT_NOTHING;
169         SpaceFile *sfile = CTX_wm_space_file(C);
170         FileSelectParams *params = ED_fileselect_get_params(sfile);
171         int numfiles = filelist_numfiles(sfile->files);
172         struct direntry *file;
173
174         /* make the selected file active */
175         if ((selected_idx >= 0) &&
176             (selected_idx < numfiles) &&
177             (file = filelist_file(sfile->files, selected_idx)))
178         {
179                 params->active_file = selected_idx;
180
181                 if (S_ISDIR(file->type)) {
182                         if (do_diropen == false) {
183                                 params->file[0] = '\0';
184                                 retval = FILE_SELECT_DIR;
185                         }
186                         /* the path is too long and we are not going up! */
187                         else if (!FILENAME_IS_PARENT(file->relname) && strlen(params->dir) + strlen(file->relname) >= FILE_MAX) {
188                                 // XXX error("Path too long, cannot enter this directory");
189                         }
190                         else {
191                                 if (FILENAME_IS_PARENT(file->relname)) {
192                                         /* avoids /../../ */
193                                         BLI_parent_dir(params->dir);
194                                 }
195                                 else {
196                                         BLI_cleanup_dir(G.main->name, params->dir);
197                                         strcat(params->dir, file->relname);
198                                         BLI_add_slash(params->dir);
199                                 }
200
201                                 ED_file_change_dir(C, false);
202                                 retval = FILE_SELECT_DIR;
203                         }
204                 }
205                 else {
206                         if (file->relname) {
207                                 BLI_strncpy(params->file, file->relname, FILE_MAXFILE);
208                         }
209                         retval = FILE_SELECT_FILE;
210                 }
211         }
212         return retval;
213 }
214
215
216 static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select, bool fill, bool do_diropen)
217 {
218         SpaceFile *sfile = CTX_wm_space_file(C);
219         FileSelect retval = FILE_SELECT_NOTHING;
220         FileSelection sel = file_selection_get(C, rect, fill); /* get the selection */
221         const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_ALL;
222         
223         /* flag the files as selected in the filelist */
224         filelist_select(sfile->files, &sel, select, FILE_SEL_SELECTED, check_type);
225         
226         /* Don't act on multiple selected files */
227         if (sel.first != sel.last) select = 0;
228
229         /* Do we have a valid selection and are we actually selecting */
230         if ((sel.last >= 0) && ((select == FILE_SEL_ADD) || (select == FILE_SEL_TOGGLE))) {
231                 /* Check last selection, if selected, act on the file or dir */
232                 if (filelist_is_selected(sfile->files, sel.last, check_type)) {
233                         retval = file_select_do(C, sel.last, do_diropen);
234                 }
235         }
236
237         /* update operator for name change event */
238         file_draw_check_cb(C, NULL, NULL);
239         
240         return retval;
241 }
242
243 static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
244 {
245         ARegion *ar = CTX_wm_region(C);
246         SpaceFile *sfile = CTX_wm_space_file(C);
247         FileSelectParams *params = ED_fileselect_get_params(sfile);
248         FileSelection sel;
249         rcti rect;
250
251         int result;
252
253         result = WM_border_select_modal(C, op, event);
254
255         if (result == OPERATOR_RUNNING_MODAL) {
256
257                 WM_operator_properties_border_to_rcti(op, &rect);
258
259                 BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect);
260
261                 sel = file_selection_get(C, &rect, 0);
262                 if ( (sel.first != params->sel_first) || (sel.last != params->sel_last) ) {
263                         int idx;
264
265                         file_deselect_all(sfile, FILE_SEL_HIGHLIGHTED);
266                         filelist_select(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_HIGHLIGHTED, CHECK_ALL);
267                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
268
269                         /* dont highlight readonly file (".." or ".") on border select */
270                         for (idx = sel.last; idx >= 0; idx--) {
271                                 struct direntry *file = filelist_file(sfile->files, idx);
272
273                                 if (FILENAME_IS_CURRPAR(file->relname)) {
274                                         file->selflag &= ~FILE_SEL_HIGHLIGHTED;
275                                 }
276
277                                 /* active_file gets highlighted as well - make sure it is no readonly file */
278                                 if (sel.last == idx) {
279                                         params->active_file = idx;
280                                 }
281                         }
282                 }
283                 params->sel_first = sel.first; params->sel_last = sel.last;
284
285         }
286         else {
287                 params->active_file = -1;
288                 params->sel_first = params->sel_last = -1;
289                 file_deselect_all(sfile, FILE_SEL_HIGHLIGHTED);
290                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
291         }
292
293         return result;
294 }
295
296 static int file_border_select_exec(bContext *C, wmOperator *op)
297 {
298         ARegion *ar = CTX_wm_region(C);
299         rcti rect;
300         FileSelect ret;
301         const bool select = (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_SELECT);
302         const bool extend = RNA_boolean_get(op->ptr, "extend");
303
304         WM_operator_properties_border_to_rcti(op, &rect);
305
306         if (!extend) {
307                 SpaceFile *sfile = CTX_wm_space_file(C);
308
309                 file_deselect_all(sfile, FILE_SEL_SELECTED);
310         }
311
312         BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect);
313
314         ret = file_select(C, &rect, select ? FILE_SEL_ADD : FILE_SEL_REMOVE, false, false);
315         if (FILE_SELECT_DIR == ret) {
316                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
317         }
318         else if (FILE_SELECT_FILE == ret) {
319                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
320         }
321         return OPERATOR_FINISHED;
322 }
323
324 void FILE_OT_select_border(wmOperatorType *ot)
325 {
326         /* identifiers */
327         ot->name = "Activate/Select File";
328         ot->description = "Activate/select the file(s) contained in the border";
329         ot->idname = "FILE_OT_select_border";
330         
331         /* api callbacks */
332         ot->invoke = WM_border_select_invoke;
333         ot->exec = file_border_select_exec;
334         ot->modal = file_border_select_modal;
335         ot->poll = ED_operator_file_active;
336         ot->cancel = WM_border_select_cancel;
337
338         /* properties */
339         WM_operator_properties_gesture_border(ot, 1);
340 }
341
342 static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
343 {
344         ARegion *ar = CTX_wm_region(C);
345         SpaceFile *sfile = CTX_wm_space_file(C);
346         FileSelect ret;
347         rcti rect;
348         const bool extend = RNA_boolean_get(op->ptr, "extend");
349         const bool fill = RNA_boolean_get(op->ptr, "fill");
350         const bool do_diropen = RNA_boolean_get(op->ptr, "open");
351
352         if (ar->regiontype != RGN_TYPE_WINDOW)
353                 return OPERATOR_CANCELLED;
354
355         rect.xmin = rect.xmax = event->mval[0];
356         rect.ymin = rect.ymax = event->mval[1];
357
358         if (!BLI_rcti_isect_pt(&ar->v2d.mask, rect.xmin, rect.ymin))
359                 return OPERATOR_CANCELLED;
360
361         if (sfile && sfile->params) {
362                 int idx = sfile->params->active_file;
363
364                 if (idx >= 0) {
365                         struct direntry *file = filelist_file(sfile->files, idx);
366                         if (FILENAME_IS_CURRPAR(file->relname)) {
367                                 /* skip - If a readonly file (".." or ".") is selected, skip deselect all! */
368                         }
369                         else {
370                                 /* single select, deselect all selected first */
371                                 if (!extend) file_deselect_all(sfile, FILE_SEL_SELECTED);
372                         }
373                 }
374         }
375
376         ret = file_select(C, &rect, extend ? FILE_SEL_TOGGLE : FILE_SEL_ADD, fill, do_diropen);
377         if (FILE_SELECT_DIR == ret)
378                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
379         else if (FILE_SELECT_FILE == ret)
380                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
381
382         WM_event_add_mousemove(C); /* for directory changes */
383         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
384
385         return OPERATOR_FINISHED;
386 }
387
388 void FILE_OT_select(wmOperatorType *ot)
389 {
390         PropertyRNA *prop;
391
392         /* identifiers */
393         ot->name = "Activate/Select File";
394         ot->description = "Activate/select file";
395         ot->idname = "FILE_OT_select";
396         
397         /* api callbacks */
398         ot->invoke = file_select_invoke;
399         ot->poll = ED_operator_file_active;
400
401         /* properties */
402         prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting everything first");
403         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
404         prop = RNA_def_boolean(ot->srna, "fill", false, "Fill", "Select everything beginning with the last selection");
405         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
406         prop = RNA_def_boolean(ot->srna, "open", true, "Open", "Open a directory when selecting it");
407         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
408 }
409
410 static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op))
411 {
412         ScrArea *sa = CTX_wm_area(C);
413         SpaceFile *sfile = CTX_wm_space_file(C);
414         FileSelection sel;
415         int numfiles = filelist_numfiles(sfile->files);
416         int i;
417         bool is_selected = false;
418
419         sel.first = 0; 
420         sel.last = numfiles - 1;
421
422         /* Is any file selected ? */
423         for (i = 0; i < numfiles; ++i) {
424                 if (filelist_is_selected(sfile->files, i, CHECK_ALL)) {
425                         is_selected = true;
426                         break;
427                 }
428         }
429         /* select all only if previously no file was selected */
430         if (is_selected) {
431                 filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL);
432         }
433         else {
434                 const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_FILES;
435                 filelist_select(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_SELECTED, check_type);
436         }
437         ED_area_tag_redraw(sa);
438         return OPERATOR_FINISHED;
439 }
440
441 void FILE_OT_select_all_toggle(wmOperatorType *ot)
442 {
443         /* identifiers */
444         ot->name = "(De)select All Files";
445         ot->description = "Select or deselect all files";
446         ot->idname = "FILE_OT_select_all_toggle";
447         
448         /* api callbacks */
449         ot->exec = file_select_all_exec;
450         ot->poll = ED_operator_file_active;
451
452         /* properties */
453 }
454
455 /* ---------- BOOKMARKS ----------- */
456
457 /* Note we could get rid of this one, but it's used by some addon so... Does not hurt keeping it around for now. */
458 static int bookmark_select_exec(bContext *C, wmOperator *op)
459 {
460         SpaceFile *sfile = CTX_wm_space_file(C);
461
462         if (RNA_struct_find_property(op->ptr, "dir")) {
463                 char entry[256];
464                 FileSelectParams *params = sfile->params;
465
466                 RNA_string_get(op->ptr, "dir", entry);
467                 BLI_strncpy(params->dir, entry, sizeof(params->dir));
468                 BLI_cleanup_dir(G.main->name, params->dir);
469                 ED_file_change_dir(C, true);
470
471                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
472         }
473         
474         return OPERATOR_FINISHED;
475 }
476
477 void FILE_OT_select_bookmark(wmOperatorType *ot)
478 {
479         PropertyRNA *prop;
480
481         /* identifiers */
482         ot->name = "Select Directory";
483         ot->description = "Select a bookmarked directory";
484         ot->idname = "FILE_OT_select_bookmark";
485         
486         /* api callbacks */
487         ot->exec = bookmark_select_exec;
488         ot->poll = ED_operator_file_active;
489
490         /* properties */
491         prop = RNA_def_string(ot->srna, "dir", NULL, FILE_MAXDIR, "Dir", "");
492         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
493 }
494
495 static int bookmark_add_exec(bContext *C, wmOperator *UNUSED(op))
496 {
497         ScrArea *sa = CTX_wm_area(C);
498         SpaceFile *sfile = CTX_wm_space_file(C);
499         struct FSMenu *fsmenu = ED_fsmenu_get();
500         struct FileSelectParams *params = ED_fileselect_get_params(sfile);
501
502         if (params->dir[0] != '\0') {
503                 char name[FILE_MAX];
504         
505                 fsmenu_insert_entry(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir, NULL, FS_INSERT_SAVE);
506                 BLI_make_file_string("/", name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
507                 fsmenu_write_file(fsmenu, name);
508         }
509
510         ED_area_tag_refresh(sa);
511         ED_area_tag_redraw(sa);
512         return OPERATOR_FINISHED;
513 }
514
515 void FILE_OT_bookmark_add(wmOperatorType *ot)
516 {
517         /* identifiers */
518         ot->name = "Add Bookmark";
519         ot->description = "Add a bookmark for the selected/active directory";
520         ot->idname = "FILE_OT_bookmark_add";
521         
522         /* api callbacks */
523         ot->exec = bookmark_add_exec;
524         ot->poll = ED_operator_file_active;
525 }
526
527 static int bookmark_delete_exec(bContext *C, wmOperator *op)
528 {
529         ScrArea *sa = CTX_wm_area(C);
530         SpaceFile *sfile = CTX_wm_space_file(C);
531         struct FSMenu *fsmenu = ED_fsmenu_get();
532         int nentries = ED_fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS);
533
534         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "index");
535
536         if (prop) {
537                 int index;
538                 if (RNA_property_is_set(op->ptr, prop)) {
539                         index = RNA_property_int_get(op->ptr, prop);
540                 }
541                 else {  /* if index unset, use active bookmark... */
542                         index = sfile->bookmarknr;
543                 }
544                 if ((index > -1) && (index < nentries)) {
545                         char name[FILE_MAX];
546                         
547                         fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index);
548                         BLI_make_file_string("/", name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
549                         fsmenu_write_file(fsmenu, name);
550                         ED_area_tag_refresh(sa);
551                         ED_area_tag_redraw(sa);
552                 }
553         }
554
555         return OPERATOR_FINISHED;
556 }
557
558 void FILE_OT_bookmark_delete(wmOperatorType *ot)
559 {
560         PropertyRNA *prop;
561
562         /* identifiers */
563         ot->name = "Delete Bookmark";
564         ot->description = "Delete selected bookmark";
565         ot->idname = "FILE_OT_bookmark_delete";
566         
567         /* api callbacks */
568         ot->exec = bookmark_delete_exec;
569         ot->poll = ED_operator_file_active;
570
571         /* properties */
572         prop = RNA_def_int(ot->srna, "index", -1, -1, 20000, "Index", "", -1, 20000);
573         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
574 }
575
576 enum {
577         FILE_BOOKMARK_MOVE_TOP = -2,
578         FILE_BOOKMARK_MOVE_UP = -1,
579         FILE_BOOKMARK_MOVE_DOWN = 1,
580         FILE_BOOKMARK_MOVE_BOTTOM = 2,
581 };
582
583 static int bookmark_move_exec(bContext *C, wmOperator *op)
584 {
585         ScrArea *sa = CTX_wm_area(C);
586         SpaceFile *sfile = CTX_wm_space_file(C);
587         struct FSMenu *fsmenu = ED_fsmenu_get();
588         struct FSMenuEntry *fsmentry = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS);
589         const struct FSMenuEntry *fsmentry_org = fsmentry;
590
591         char fname[FILE_MAX];
592
593         const int direction = RNA_enum_get(op->ptr, "direction");
594         const int totitems = ED_fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS);
595         const int act_index = sfile->bookmarknr;
596         int new_index;
597
598         switch (direction) {
599                 case FILE_BOOKMARK_MOVE_TOP:
600                         new_index = 0;
601                         break;
602                 case FILE_BOOKMARK_MOVE_BOTTOM:
603                         new_index = totitems - 1;
604                         break;
605                 case FILE_BOOKMARK_MOVE_UP:
606                 case FILE_BOOKMARK_MOVE_DOWN:
607                 default:
608                         new_index = (totitems + act_index + direction) % totitems;
609                         break;
610         }
611
612         if (new_index == act_index) {
613                 return OPERATOR_CANCELLED;
614         }
615
616         BLI_linklist_move_item((LinkNode **)&fsmentry, act_index, new_index);
617         if (fsmentry != fsmentry_org) {
618                 ED_fsmenu_set_category(fsmenu, FS_CATEGORY_BOOKMARKS, fsmentry);
619         }
620
621         /* Need to update active bookmark number. */
622         sfile->bookmarknr = new_index;
623
624         BLI_make_file_string("/", fname, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
625         fsmenu_write_file(fsmenu, fname);
626
627         ED_area_tag_redraw(sa);
628         return OPERATOR_FINISHED;
629 }
630
631 void FILE_OT_bookmark_move(wmOperatorType *ot)
632 {
633         static EnumPropertyItem slot_move[] = {
634             {FILE_BOOKMARK_MOVE_TOP, "TOP", 0, "Top", "Top of the list"},
635                 {FILE_BOOKMARK_MOVE_UP, "UP", 0, "Up", ""},
636                 {FILE_BOOKMARK_MOVE_DOWN, "DOWN", 0, "Down", ""},
637                 {FILE_BOOKMARK_MOVE_BOTTOM, "BOTTOM", 0, "Bottom", "Bottom of the list"},
638                 { 0, NULL, 0, NULL, NULL }
639         };
640
641         /* identifiers */
642         ot->name = "Move Bookmark";
643         ot->idname = "FILE_OT_bookmark_move";
644         ot->description = "Move the active bookmark up/down in the list";
645
646         /* api callbacks */
647         ot->poll = ED_operator_file_active;
648         ot->exec = bookmark_move_exec;
649
650         /* flags */
651         ot->flag = OPTYPE_REGISTER;  /* No undo! */
652
653         RNA_def_enum(ot->srna, "direction", slot_move, 0, "Direction", "Direction to move, UP or DOWN");
654 }
655
656 static int reset_recent_exec(bContext *C, wmOperator *UNUSED(op))
657 {
658         ScrArea *sa = CTX_wm_area(C);
659         char name[FILE_MAX];
660         struct FSMenu *fsmenu = ED_fsmenu_get();
661         
662         while (ED_fsmenu_get_entry(fsmenu, FS_CATEGORY_RECENT, 0) != NULL) {
663                 fsmenu_remove_entry(fsmenu, FS_CATEGORY_RECENT, 0);
664         }
665         BLI_make_file_string("/", name, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE);
666         fsmenu_write_file(fsmenu, name);
667         ED_area_tag_redraw(sa);
668
669         return OPERATOR_FINISHED;
670 }
671
672 void FILE_OT_reset_recent(wmOperatorType *ot)
673 {
674         /* identifiers */
675         ot->name = "Reset Recent";
676         ot->description = "Reset Recent files";
677         ot->idname = "FILE_OT_reset_recent";
678         
679         /* api callbacks */
680         ot->exec = reset_recent_exec;
681         ot->poll = ED_operator_file_active;
682
683 }
684
685 int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
686 {
687         View2D *v2d = &ar->v2d;
688         FileSelectParams *params;
689         int numfiles, origfile;
690
691         if (sfile == NULL || sfile->files == NULL) return 0;
692
693         numfiles = filelist_numfiles(sfile->files);
694         params = ED_fileselect_get_params(sfile);
695
696         origfile = params->active_file;
697
698         mx -= ar->winrct.xmin;
699         my -= ar->winrct.ymin;
700
701         if (BLI_rcti_isect_pt(&ar->v2d.mask, mx, my)) {
702                 float fx, fy;
703                 int active_file;
704
705                 UI_view2d_region_to_view(v2d, mx, my, &fx, &fy);
706
707                 active_file = ED_fileselect_layout_offset(sfile->layout, (int)(v2d->tot.xmin + fx), (int)(v2d->tot.ymax - fy));
708
709                 if ((active_file >= 0) && (active_file < numfiles))
710                         params->active_file = active_file;
711                 else
712                         params->active_file = -1;
713         }
714         else
715                 params->active_file = -1;
716
717         return (params->active_file != origfile);
718 }
719
720 static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
721 {
722         ARegion *ar = CTX_wm_region(C);
723         SpaceFile *sfile = CTX_wm_space_file(C);
724
725         if (!file_highlight_set(sfile, ar, event->x, event->y))
726                 return OPERATOR_CANCELLED;
727
728         ED_area_tag_redraw(CTX_wm_area(C));
729         
730         return OPERATOR_FINISHED;
731 }
732
733 void FILE_OT_highlight(struct wmOperatorType *ot)
734 {
735         /* identifiers */
736         ot->name = "Highlight File";
737         ot->description = "Highlight selected file(s)";
738         ot->idname = "FILE_OT_highlight";
739         
740         /* api callbacks */
741         ot->invoke = file_highlight_invoke;
742         ot->poll = ED_operator_file_active;
743 }
744
745 int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused))
746 {
747         wmWindowManager *wm = CTX_wm_manager(C);
748         SpaceFile *sfile = CTX_wm_space_file(C);
749         wmOperator *op = sfile->op;
750         
751         sfile->op = NULL;
752
753         WM_event_fileselect_event(wm, op, EVT_FILESELECT_CANCEL);
754         
755         return OPERATOR_FINISHED;
756 }
757
758 static int file_operator_poll(bContext *C)
759 {
760         int poll = ED_operator_file_active(C);
761         SpaceFile *sfile = CTX_wm_space_file(C);
762
763         if (!sfile || !sfile->op) poll = 0;
764
765         return poll;
766 }
767
768 void FILE_OT_cancel(struct wmOperatorType *ot)
769 {
770         /* identifiers */
771         ot->name = "Cancel File Load";
772         ot->description = "Cancel loading of selected file";
773         ot->idname = "FILE_OT_cancel";
774         
775         /* api callbacks */
776         ot->exec = file_cancel_exec;
777         ot->poll = file_operator_poll;
778 }
779
780
781 void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
782 {
783         BLI_join_dirfile(filepath, FILE_MAX, sfile->params->dir, sfile->params->file); /* XXX, not real length */
784         if (RNA_struct_find_property(op->ptr, "relative_path")) {
785                 if (RNA_boolean_get(op->ptr, "relative_path")) {
786                         BLI_path_rel(filepath, G.main->name);
787                 }
788         }
789
790         if (RNA_struct_find_property(op->ptr, "filename")) {
791                 RNA_string_set(op->ptr, "filename", sfile->params->file);
792         }
793         if (RNA_struct_find_property(op->ptr, "directory")) {
794                 RNA_string_set(op->ptr, "directory", sfile->params->dir);
795         }
796         if (RNA_struct_find_property(op->ptr, "filepath")) {
797                 RNA_string_set(op->ptr, "filepath", filepath);
798         }
799         
800         /* some ops have multiple files to select */
801         /* this is called on operators check() so clear collections first since
802          * they may be already set. */
803         {
804                 PointerRNA itemptr;
805                 PropertyRNA *prop_files = RNA_struct_find_property(op->ptr, "files");
806                 PropertyRNA *prop_dirs = RNA_struct_find_property(op->ptr, "dirs");
807                 int i, numfiles = filelist_numfiles(sfile->files);
808
809                 if (prop_files) {
810                         int num_files = 0;
811                         RNA_property_collection_clear(op->ptr, prop_files);
812                         for (i = 0; i < numfiles; i++) {
813                                 if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
814                                         struct direntry *file = filelist_file(sfile->files, i);
815                                         RNA_property_collection_add(op->ptr, prop_files, &itemptr);
816                                         RNA_string_set(&itemptr, "name", file->relname);
817                                         num_files++;
818                                 }
819                         }
820                         /* make sure the file specified in the filename button is added even if no files selected */
821                         if (0 == num_files) {
822                                 RNA_property_collection_add(op->ptr, prop_files, &itemptr);
823                                 RNA_string_set(&itemptr, "name", sfile->params->file);
824                         }
825                 }
826
827                 if (prop_dirs) {
828                         int num_dirs = 0;
829                         RNA_property_collection_clear(op->ptr, prop_dirs);
830                         for (i = 0; i < numfiles; i++) {
831                                 if (filelist_is_selected(sfile->files, i, CHECK_DIRS)) {
832                                         struct direntry *file = filelist_file(sfile->files, i);
833                                         RNA_property_collection_add(op->ptr, prop_dirs, &itemptr);
834                                         RNA_string_set(&itemptr, "name", file->relname);
835                                         num_dirs++;
836                                 }
837                         }
838                         
839                         /* make sure the directory specified in the button is added even if no directory selected */
840                         if (0 == num_dirs) {
841                                 RNA_property_collection_add(op->ptr, prop_dirs, &itemptr);
842                                 RNA_string_set(&itemptr, "name", sfile->params->dir);
843                         }
844                 }
845
846
847         }
848 }
849
850 void file_operator_to_sfile(SpaceFile *sfile, wmOperator *op)
851 {
852         PropertyRNA *prop;
853
854         /* If neither of the above are set, split the filepath back */
855         if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
856                 char filepath[FILE_MAX];
857                 RNA_property_string_get(op->ptr, prop, filepath);
858                 BLI_split_dirfile(filepath, sfile->params->dir, sfile->params->file, sizeof(sfile->params->dir), sizeof(sfile->params->file));
859         }
860         else {
861                 if ((prop = RNA_struct_find_property(op->ptr, "filename"))) {
862                         RNA_property_string_get(op->ptr, prop, sfile->params->file);
863                 }
864                 if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
865                         RNA_property_string_get(op->ptr, prop, sfile->params->dir);
866                 }
867         }
868         
869         /* we could check for relative_path property which is used when converting
870          * in the other direction but doesnt hurt to do this every time */
871         BLI_path_abs(sfile->params->dir, G.main->name);
872
873         /* XXX, files and dirs updates missing, not really so important though */
874 }
875
876 void file_draw_check_cb(bContext *C, void *UNUSED(arg1), void *UNUSED(arg2))
877 {
878         SpaceFile *sfile = CTX_wm_space_file(C);
879         wmOperator *op = sfile->op;
880         if (op) { /* fail on reload */
881                 if (op->type->check) {
882                         char filepath[FILE_MAX];
883                         file_sfile_to_operator(op, sfile, filepath);
884                         
885                         /* redraw */
886                         if (op->type->check(C, op)) {
887                                 file_operator_to_sfile(sfile, op);
888         
889                                 /* redraw, else the changed settings wont get updated */
890                                 ED_area_tag_redraw(CTX_wm_area(C));
891                         }
892                 }
893         }
894 }
895
896 bool file_draw_check_exists(SpaceFile *sfile)
897 {
898         if (sfile->op) { /* fails on reload */
899                 if (RNA_struct_find_property(sfile->op->ptr, "check_existing")) {
900                         if (RNA_boolean_get(sfile->op->ptr, "check_existing")) {
901                                 char filepath[FILE_MAX];
902                                 BLI_join_dirfile(filepath, sizeof(filepath), sfile->params->dir, sfile->params->file);
903                                 if (BLI_is_file(filepath)) {
904                                         return true;
905                                 }
906                         }
907                 }
908         }
909
910         return false;
911 }
912
913 /* sends events now, so things get handled on windowqueue level */
914 int file_exec(bContext *C, wmOperator *exec_op)
915 {
916         wmWindowManager *wm = CTX_wm_manager(C);
917         SpaceFile *sfile = CTX_wm_space_file(C);
918         char filepath[FILE_MAX];
919         
920         if (sfile->op) {
921                 wmOperator *op = sfile->op;
922         
923                 /* when used as a macro, for doubleclick, 
924                  * to prevent closing when doubleclicking on .. item */
925                 if (RNA_boolean_get(exec_op->ptr, "need_active")) {
926                         int i, active = 0;
927                         
928                         for (i = 0; i < filelist_numfiles(sfile->files); i++) {
929                                 if (filelist_is_selected(sfile->files, i, CHECK_ALL)) {
930                                         active = 1;
931                                         break;
932                                 }
933                         }
934                         if (active == 0)
935                                 return OPERATOR_CANCELLED;
936                 }
937                 
938                 sfile->op = NULL;
939
940                 file_sfile_to_operator(op, sfile, filepath);
941
942                 if (BLI_exists(sfile->params->dir)) {
943                         fsmenu_insert_entry(ED_fsmenu_get(), FS_CATEGORY_RECENT, sfile->params->dir, NULL,
944                                             FS_INSERT_SAVE | FS_INSERT_FIRST);
945                 }
946
947                 BLI_make_file_string(G.main->name, filepath, BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
948                                      BLENDER_BOOKMARK_FILE);
949                 fsmenu_write_file(ED_fsmenu_get(), filepath);
950                 WM_event_fileselect_event(wm, op, EVT_FILESELECT_EXEC);
951
952         }
953                                 
954         return OPERATOR_FINISHED;
955 }
956
957 void FILE_OT_execute(struct wmOperatorType *ot)
958 {
959         PropertyRNA *prop;
960
961         /* identifiers */
962         ot->name = "Execute File Window";
963         ot->description = "Execute selected file";
964         ot->idname = "FILE_OT_execute";
965         
966         /* api callbacks */
967         ot->exec = file_exec;
968         ot->poll = file_operator_poll; 
969
970         /* properties */
971         prop = RNA_def_boolean(ot->srna, "need_active", 0, "Need Active",
972                                "Only execute if there's an active selected file in the file list");
973         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
974 }
975
976
977 int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
978 {
979         SpaceFile *sfile = CTX_wm_space_file(C);
980         
981         if (sfile->params) {
982                 if (BLI_parent_dir(sfile->params->dir)) {
983                         BLI_cleanup_dir(G.main->name, sfile->params->dir);
984                         /* if not browsing in .blend file, we still want to check whether the path is a directory */
985                         if (sfile->params->type == FILE_LOADLIB) {
986                                 char tdir[FILE_MAX], tgroup[FILE_MAX];
987                                 if (BLO_is_a_library(sfile->params->dir, tdir, tgroup)) {
988                                         ED_file_change_dir(C, false);
989                                 }
990                                 else {
991                                         ED_file_change_dir(C, true);
992                                 }
993                         }
994                         else {
995                                 ED_file_change_dir(C, true);
996                         }
997                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
998                 }
999         }
1000         
1001         return OPERATOR_FINISHED;
1002
1003 }
1004
1005
1006 void FILE_OT_parent(struct wmOperatorType *ot)
1007 {
1008         /* identifiers */
1009         ot->name = "Parent File";
1010         ot->description = "Move to parent directory";
1011         ot->idname = "FILE_OT_parent";
1012         
1013         /* api callbacks */
1014         ot->exec = file_parent_exec;
1015         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1016 }
1017
1018
1019 static int file_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
1020 {
1021         wmWindowManager *wm = CTX_wm_manager(C);
1022         SpaceFile *sfile = CTX_wm_space_file(C);
1023         struct FSMenu *fsmenu = ED_fsmenu_get();
1024
1025         ED_fileselect_clear(wm, sfile);
1026
1027         /* refresh system directory menu */
1028         fsmenu_refresh_system_category(fsmenu);
1029
1030         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1031
1032         return OPERATOR_FINISHED;
1033
1034 }
1035
1036 void FILE_OT_previous(struct wmOperatorType *ot)
1037 {
1038         /* identifiers */
1039         ot->name = "Previous Folder";
1040         ot->description = "Move to previous folder";
1041         ot->idname = "FILE_OT_previous";
1042         
1043         /* api callbacks */
1044         ot->exec = file_previous_exec;
1045         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1046 }
1047
1048 int file_previous_exec(bContext *C, wmOperator *UNUSED(unused))
1049 {
1050         SpaceFile *sfile = CTX_wm_space_file(C);
1051
1052         if (sfile->params) {
1053                 if (!sfile->folders_next)
1054                         sfile->folders_next = folderlist_new();
1055
1056                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
1057                 folderlist_popdir(sfile->folders_prev, sfile->params->dir);
1058                 folderlist_pushdir(sfile->folders_next, sfile->params->dir);
1059
1060                 ED_file_change_dir(C, true);
1061         }
1062         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1063
1064         return OPERATOR_FINISHED;
1065 }
1066
1067 void FILE_OT_next(struct wmOperatorType *ot)
1068 {
1069         /* identifiers */
1070         ot->name = "Next Folder";
1071         ot->description = "Move to next folder";
1072         ot->idname = "FILE_OT_next";
1073         
1074         /* api callbacks */
1075         ot->exec = file_next_exec;
1076         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1077 }
1078
1079 int file_next_exec(bContext *C, wmOperator *UNUSED(unused))
1080 {
1081         SpaceFile *sfile = CTX_wm_space_file(C);
1082         if (sfile->params) {
1083                 if (!sfile->folders_next)
1084                         sfile->folders_next = folderlist_new();
1085
1086                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
1087                 folderlist_popdir(sfile->folders_next, sfile->params->dir);
1088
1089                 // update folders_prev so we can check for it in folderlist_clear_next()
1090                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
1091
1092                 ED_file_change_dir(C, true);
1093         }
1094         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1095
1096         return OPERATOR_FINISHED;
1097 }
1098
1099
1100 /* only meant for timer usage */
1101 static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1102 {
1103         ScrArea *sa = CTX_wm_area(C);
1104         SpaceFile *sfile = CTX_wm_space_file(C);
1105         ARegion *ar, *oldar = CTX_wm_region(C);
1106         int offset;
1107         int numfiles, numfiles_layout;
1108         int edit_idx = 0;
1109         int i;
1110
1111         /* escape if not our timer */
1112         if (sfile->smoothscroll_timer == NULL || sfile->smoothscroll_timer != event->customdata)
1113                 return OPERATOR_PASS_THROUGH;
1114         
1115         numfiles = filelist_numfiles(sfile->files);
1116
1117         /* check if we are editing a name */
1118         for (i = 0; i < numfiles; ++i) {
1119                 if (filelist_is_selected(sfile->files, i, CHECK_ALL) ) {
1120                         edit_idx = i;
1121                         break;
1122                 }
1123         }
1124
1125         /* if we are not editing, we are done */
1126         if (0 == edit_idx) {
1127                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
1128                 sfile->smoothscroll_timer = NULL;
1129                 return OPERATOR_PASS_THROUGH;
1130         }
1131
1132         /* we need the correct area for scrolling */
1133         ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
1134         if (!ar || ar->regiontype != RGN_TYPE_WINDOW) {
1135                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
1136                 sfile->smoothscroll_timer = NULL;
1137                 return OPERATOR_PASS_THROUGH;
1138         }
1139
1140         offset = ED_fileselect_layout_offset(sfile->layout, (int)ar->v2d.cur.xmin, (int)-ar->v2d.cur.ymax);
1141         if (offset < 0) offset = 0;
1142
1143         /* scroll offset is the first file in the row/column we are editing in */
1144         if (sfile->scroll_offset == 0) {
1145                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
1146                         sfile->scroll_offset = (edit_idx / sfile->layout->rows) * sfile->layout->rows;
1147                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->rows;
1148                 }
1149                 else {
1150                         sfile->scroll_offset = (edit_idx / sfile->layout->columns) * sfile->layout->columns;
1151                         if (sfile->scroll_offset <= offset) sfile->scroll_offset -= sfile->layout->columns;
1152                 }
1153         }
1154         
1155         numfiles_layout = ED_fileselect_layout_numfiles(sfile->layout, ar);
1156         
1157         /* check if we have reached our final scroll position */
1158         if ( (sfile->scroll_offset >= offset) && (sfile->scroll_offset < offset + numfiles_layout) ) {
1159                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), sfile->smoothscroll_timer);
1160                 sfile->smoothscroll_timer = NULL;
1161                 return OPERATOR_FINISHED;
1162         }
1163
1164         /* temporarily set context to the main window region, 
1165          * so the scroll operators work */
1166         CTX_wm_region_set(C, ar);
1167         
1168         /* scroll one step in the desired direction */
1169         if (sfile->scroll_offset < offset) {
1170                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
1171                         WM_operator_name_call(C, "VIEW2D_OT_scroll_left", 0, NULL);
1172                 }
1173                 else {
1174                         WM_operator_name_call(C, "VIEW2D_OT_scroll_up", 0, NULL);
1175                 }
1176                 
1177         }
1178         else {
1179                 if (sfile->layout->flag & FILE_LAYOUT_HOR) {
1180                         WM_operator_name_call(C, "VIEW2D_OT_scroll_right", 0, NULL);
1181                 }
1182                 else {
1183                         WM_operator_name_call(C, "VIEW2D_OT_scroll_down", 0, NULL);
1184                 }
1185         }
1186         
1187         ED_region_tag_redraw(ar);
1188         
1189         /* and restore context */
1190         CTX_wm_region_set(C, oldar);
1191         
1192         return OPERATOR_FINISHED;
1193 }
1194
1195
1196 void FILE_OT_smoothscroll(wmOperatorType *ot)
1197 {
1198         
1199         /* identifiers */
1200         ot->name = "Smooth Scroll";
1201         ot->idname = "FILE_OT_smoothscroll";
1202         ot->description = "Smooth scroll to make editable file visible";
1203         
1204         /* api callbacks */
1205         ot->invoke = file_smoothscroll_invoke;
1206         
1207         ot->poll = ED_operator_file_active;
1208 }
1209
1210
1211 /* create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created.
1212  * The actual name is returned in 'name', 'folder' contains the complete path, including the new folder name.
1213  */
1214 static int new_folder_path(const char *parent, char *folder, char *name)
1215 {
1216         int i = 1;
1217         int len = 0;
1218
1219         BLI_strncpy(name, "New Folder", FILE_MAXFILE);
1220         BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
1221         /* check whether folder with the name already exists, in this case
1222          * add number to the name. Check length of generated name to avoid
1223          * crazy case of huge number of folders each named 'New Folder (x)' */
1224         while (BLI_exists(folder) && (len < FILE_MAXFILE)) {
1225                 len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
1226                 BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
1227                 i++;
1228         }
1229
1230         return (len < FILE_MAXFILE);
1231 }
1232
1233 int file_directory_new_exec(bContext *C, wmOperator *op)
1234 {
1235         char name[FILE_MAXFILE];
1236         char path[FILE_MAX];
1237         int generate_name = 1;
1238
1239         wmWindowManager *wm = CTX_wm_manager(C);
1240         SpaceFile *sfile = CTX_wm_space_file(C);
1241         
1242         if (!sfile->params) {
1243                 BKE_report(op->reports, RPT_WARNING, "No parent directory given");
1244                 return OPERATOR_CANCELLED;
1245         }
1246         
1247         path[0] = '\0';
1248
1249         if (RNA_struct_find_property(op->ptr, "directory")) {
1250                 RNA_string_get(op->ptr, "directory", path);
1251                 if (path[0] != '\0') generate_name = 0;
1252         }
1253
1254         if (generate_name) {
1255                 /* create a new, non-existing folder name */
1256                 if (!new_folder_path(sfile->params->dir, path, name)) {
1257                         BKE_report(op->reports, RPT_ERROR, "Could not create new folder name");
1258                         return OPERATOR_CANCELLED;
1259                 }
1260         }
1261
1262         /* create the file */
1263         BLI_dir_create_recursive(path);
1264
1265         if (!BLI_exists(path)) {
1266                 BKE_report(op->reports, RPT_ERROR, "Could not create new folder");
1267                 return OPERATOR_CANCELLED;
1268         }
1269
1270         /* now remember file to jump into editing */
1271         BLI_strncpy(sfile->params->renamefile, name, FILE_MAXFILE);
1272
1273         /* set timer to smoothly view newly generated file */
1274         sfile->smoothscroll_timer = WM_event_add_timer(wm, CTX_wm_window(C), TIMER1, 1.0 / 1000.0);  /* max 30 frs/sec */
1275         sfile->scroll_offset = 0;
1276
1277         /* reload dir to make sure we're seeing what's in the directory */
1278         ED_fileselect_clear(wm, sfile);
1279
1280         if (RNA_boolean_get(op->ptr, "open")) {
1281                 BLI_strncpy(sfile->params->dir, path, sizeof(sfile->params->dir));
1282                 ED_file_change_dir(C, true);
1283         }
1284
1285         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1286
1287         return OPERATOR_FINISHED;
1288 }
1289
1290
1291 void FILE_OT_directory_new(struct wmOperatorType *ot)
1292 {
1293         PropertyRNA *prop;
1294
1295         /* identifiers */
1296         ot->name = "Create New Directory";
1297         ot->description = "Create a new directory";
1298         ot->idname = "FILE_OT_directory_new";
1299         
1300         /* api callbacks */
1301         ot->invoke = WM_operator_confirm;
1302         ot->exec = file_directory_new_exec;
1303         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1304
1305         prop = RNA_def_string_dir_path(ot->srna, "directory", NULL, FILE_MAX, "Directory", "Name of new directory");
1306         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1307         prop = RNA_def_boolean(ot->srna, "open", false, "Open", "Open new directory");
1308         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1309 }
1310
1311
1312 static void file_expand_directory(bContext *C)
1313 {
1314         SpaceFile *sfile = CTX_wm_space_file(C);
1315         
1316         if (sfile->params) {
1317                 /* TODO, what about // when relbase isn't valid? */
1318                 if (G.relbase_valid && BLI_path_is_rel(sfile->params->dir)) {
1319                         BLI_path_abs(sfile->params->dir, G.main->name);
1320                 }
1321                 else if (sfile->params->dir[0] == '~') {
1322                         char tmpstr[sizeof(sfile->params->dir) - 1];
1323                         BLI_strncpy(tmpstr, sfile->params->dir + 1, sizeof(tmpstr));
1324                         BLI_join_dirfile(sfile->params->dir, sizeof(sfile->params->dir), BKE_appdir_folder_default(), tmpstr);
1325                 }
1326
1327                 else if (sfile->params->dir[0] == '\0')
1328 #ifndef WIN32
1329                 {
1330                         sfile->params->dir[0] = '/';
1331                         sfile->params->dir[1] = '\0';
1332                 }
1333 #else
1334                 {
1335                         get_default_root(sfile->params->dir);
1336                 }
1337                 /* change "C:" --> "C:\", [#28102] */
1338                 else if ((isalpha(sfile->params->dir[0]) &&
1339                           (sfile->params->dir[1] == ':')) &&
1340                          (sfile->params->dir[2] == '\0'))
1341                 {
1342                         sfile->params->dir[2] = '\\';
1343                         sfile->params->dir[3] = '\0';
1344                 }
1345                 else if (BLI_path_is_unc(sfile->params->dir)) {
1346                         BLI_cleanup_unc(sfile->params->dir, FILE_MAX_LIBEXTRA);
1347                 }
1348 #endif
1349         }
1350 }
1351
1352 #if defined(WIN32)
1353 static bool can_create_dir(const char *dir)
1354 {
1355         /* for UNC paths we need to check whether the parent of the new
1356          * directory is a proper directory itself and not a share or the
1357          * UNC root (server name) itself. Calling BLI_is_dir does this
1358          */
1359         if (BLI_path_is_unc(dir)) {
1360                 char parent[PATH_MAX];
1361                 BLI_strncpy(parent, dir, PATH_MAX);
1362                 BLI_parent_dir(parent);
1363                 return BLI_is_dir(parent);
1364         }
1365         return true;
1366 }
1367 #endif
1368
1369 void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UNUSED(arg_but))
1370 {
1371         SpaceFile *sfile = CTX_wm_space_file(C);
1372         
1373         if (sfile->params) {
1374                 file_expand_directory(C);
1375
1376                 /* special case, user may have pasted a filepath into the directory */
1377                 if (BLI_is_file(sfile->params->dir)) {
1378                         char path[sizeof(sfile->params->dir)];
1379                         BLI_strncpy(path, sfile->params->dir, sizeof(path));
1380                         BLI_split_dirfile(path, sfile->params->dir, sfile->params->file, sizeof(sfile->params->dir), sizeof(sfile->params->file));
1381                 }
1382
1383                 BLI_cleanup_dir(G.main->name, sfile->params->dir);
1384
1385                 if (BLI_exists(sfile->params->dir)) {
1386                         /* if directory exists, enter it immediately */
1387                         ED_file_change_dir(C, true);
1388
1389                         /* don't do for now because it selects entire text instead of
1390                          * placing cursor at the end */
1391                         /* UI_textbutton_activate_but(C, but); */
1392                 }
1393 #if defined(WIN32)
1394                 else if (!can_create_dir(sfile->params->dir)) {
1395                         const char *lastdir = folderlist_peeklastdir(sfile->folders_prev);
1396                         if (lastdir)
1397                                 BLI_strncpy(sfile->params->dir, lastdir, sizeof(sfile->params->dir));
1398                 }
1399 #endif
1400                 else {
1401                         const char *lastdir = folderlist_peeklastdir(sfile->folders_prev);
1402
1403                         /* if not, ask to create it and enter if confirmed */
1404                         wmOperatorType *ot = WM_operatortype_find("FILE_OT_directory_new", false);
1405                         PointerRNA ptr;
1406                         WM_operator_properties_create_ptr(&ptr, ot);
1407                         RNA_string_set(&ptr, "directory", sfile->params->dir);
1408                         RNA_boolean_set(&ptr, "open", true);
1409
1410                         if (lastdir)
1411                                 BLI_strncpy(sfile->params->dir, lastdir, sizeof(sfile->params->dir));
1412
1413
1414                         WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
1415                         WM_operator_properties_free(&ptr);
1416                 }
1417
1418                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1419         }
1420 }
1421
1422 void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg_but)
1423 {
1424         SpaceFile *sfile = CTX_wm_space_file(C);
1425         uiBut *but = arg_but;
1426         char matched_file[FILE_MAX];
1427         char filepath[sizeof(sfile->params->dir)];
1428
1429         if (sfile->params) {
1430                 int matches;
1431                 matched_file[0] = '\0';
1432                 filepath[0] = '\0';
1433
1434                 file_expand_directory(C);
1435
1436                 matches = file_select_match(sfile, sfile->params->file, matched_file);
1437
1438                 if (matches) {
1439                         /* int i, numfiles = filelist_numfiles(sfile->files); */ /* XXX UNUSED */
1440                         sfile->params->file[0] = '\0';
1441                         /* replace the pattern (or filename that the user typed in, with the first selected file of the match */
1442                         BLI_strncpy(sfile->params->file, matched_file, sizeof(sfile->params->file));
1443                         
1444                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
1445                 }
1446
1447                 if (matches == 1) {
1448
1449                         BLI_join_dirfile(filepath, sizeof(sfile->params->dir), sfile->params->dir, sfile->params->file);
1450
1451                         /* if directory, open it and empty filename field */
1452                         if (BLI_is_dir(filepath)) {
1453                                 BLI_cleanup_dir(G.main->name, filepath);
1454                                 BLI_strncpy(sfile->params->dir, filepath, sizeof(sfile->params->dir));
1455                                 sfile->params->file[0] = '\0';
1456                                 ED_file_change_dir(C, true);
1457                                 UI_textbutton_activate_but(C, but);
1458                                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
1459                         }
1460                         else if (sfile->params->type == FILE_LOADLIB) {
1461                                 char tdir[FILE_MAX], tgroup[FILE_MAX];
1462                                 BLI_add_slash(filepath);
1463                                 if (BLO_is_a_library(filepath, tdir, tgroup)) {
1464                                         BLI_cleanup_dir(G.main->name, filepath);
1465                                         BLI_strncpy(sfile->params->dir, filepath, sizeof(sfile->params->dir));
1466                                         sfile->params->file[0] = '\0';
1467                                         ED_file_change_dir(C, false);
1468                                         UI_textbutton_activate_but(C, but);
1469                                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1470                                 }
1471                         }
1472                 }
1473         }
1474 }
1475
1476 void FILE_OT_refresh(struct wmOperatorType *ot)
1477 {
1478         /* identifiers */
1479         ot->name = "Refresh Filelist";
1480         ot->description = "Refresh the file list";
1481         ot->idname = "FILE_OT_refresh";
1482         
1483         /* api callbacks */
1484         ot->exec = file_refresh_exec;
1485         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1486 }
1487
1488 static int file_hidedot_exec(bContext *C, wmOperator *UNUSED(unused))
1489 {
1490         wmWindowManager *wm = CTX_wm_manager(C);
1491         SpaceFile *sfile = CTX_wm_space_file(C);
1492         
1493         if (sfile->params) {
1494                 sfile->params->flag ^= FILE_HIDE_DOT;
1495                 ED_fileselect_clear(wm, sfile);
1496                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1497         }
1498         
1499         return OPERATOR_FINISHED;
1500 }
1501
1502
1503 void FILE_OT_hidedot(struct wmOperatorType *ot)
1504 {
1505         /* identifiers */
1506         ot->name = "Toggle Hide Dot Files";
1507         ot->description = "Toggle hide hidden dot files";
1508         ot->idname = "FILE_OT_hidedot";
1509         
1510         /* api callbacks */
1511         ot->exec = file_hidedot_exec;
1512         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1513 }
1514
1515 ARegion *file_tools_region(ScrArea *sa)
1516 {
1517         ARegion *ar, *arnew;
1518
1519         if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOLS)) != NULL)
1520                 return ar;
1521
1522         /* add subdiv level; after header */
1523         ar = BKE_area_find_region_type(sa, RGN_TYPE_HEADER);
1524         
1525         /* is error! */
1526         if (ar == NULL)
1527                 return NULL;
1528         
1529         arnew = MEM_callocN(sizeof(ARegion), "tools for file");
1530         BLI_insertlinkafter(&sa->regionbase, ar, arnew);
1531         arnew->regiontype = RGN_TYPE_TOOLS;
1532         arnew->alignment = RGN_ALIGN_LEFT;
1533
1534         ar = MEM_callocN(sizeof(ARegion), "tool props for file");
1535         BLI_insertlinkafter(&sa->regionbase, arnew, ar);
1536         ar->regiontype = RGN_TYPE_TOOL_PROPS;
1537         ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
1538
1539         return arnew;
1540 }
1541
1542 static int file_bookmark_toggle_exec(bContext *C, wmOperator *UNUSED(unused))
1543 {
1544         ScrArea *sa = CTX_wm_area(C);
1545         ARegion *ar = file_tools_region(sa);
1546         
1547         if (ar)
1548                 ED_region_toggle_hidden(C, ar);
1549
1550         return OPERATOR_FINISHED;
1551 }
1552
1553 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
1554 {
1555         /* identifiers */
1556         ot->name = "Toggle Bookmarks";
1557         ot->description = "Toggle bookmarks display";
1558         ot->idname = "FILE_OT_bookmark_toggle";
1559         
1560         /* api callbacks */
1561         ot->exec = file_bookmark_toggle_exec;
1562         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1563 }
1564
1565
1566 static int file_filenum_exec(bContext *C, wmOperator *op)
1567 {
1568         SpaceFile *sfile = CTX_wm_space_file(C);
1569         ScrArea *sa = CTX_wm_area(C);
1570         
1571         int inc = RNA_int_get(op->ptr, "increment");
1572         if (sfile->params && (inc != 0)) {
1573                 BLI_newname(sfile->params->file, inc);
1574                 ED_area_tag_redraw(sa);
1575                 file_draw_check_cb(C, NULL, NULL);
1576                 // WM_event_add_notifier(C, NC_WINDOW, NULL);
1577         }
1578         
1579         return OPERATOR_FINISHED;
1580
1581 }
1582
1583 void FILE_OT_filenum(struct wmOperatorType *ot)
1584 {
1585         /* identifiers */
1586         ot->name = "Increment Number in Filename";
1587         ot->description = "Increment number in filename";
1588         ot->idname = "FILE_OT_filenum";
1589         
1590         /* api callbacks */
1591         ot->exec = file_filenum_exec;
1592         ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
1593
1594         /* props */
1595         RNA_def_int(ot->srna, "increment", 1, -100, 100, "Increment", "", -100, 100);
1596 }
1597
1598 static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
1599 {
1600         ScrArea *sa = CTX_wm_area(C);
1601         SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
1602         
1603         if (sfile->params) {
1604                 int idx = sfile->params->active_file;
1605                 int numfiles = filelist_numfiles(sfile->files);
1606                 if ( (0 <= idx) && (idx < numfiles) ) {
1607                         struct direntry *file = filelist_file(sfile->files, idx);
1608                         filelist_select_file(sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
1609                         BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE);
1610                         sfile->params->renamefile[0] = '\0';
1611                 }
1612                 ED_area_tag_redraw(sa);
1613         }
1614         
1615         return OPERATOR_FINISHED;
1616
1617 }
1618
1619 static int file_rename_poll(bContext *C)
1620 {
1621         int poll = ED_operator_file_active(C);
1622         SpaceFile *sfile = CTX_wm_space_file(C);
1623
1624         if (sfile && sfile->params) {
1625                 int idx = sfile->params->active_file;
1626
1627                 if (idx >= 0) {
1628                         struct direntry *file = filelist_file(sfile->files, idx);
1629                         if (FILENAME_IS_CURRPAR(file->relname)) {
1630                                 poll = 0;
1631                         }
1632                 }
1633
1634                 if (sfile->params->active_file < 0) {
1635                         poll = 0;
1636                 }
1637                 else {
1638                         char dir[FILE_MAX], group[FILE_MAX];
1639                         if (filelist_islibrary(sfile->files, dir, group)) poll = 0;
1640                 }
1641         }
1642         else
1643                 poll = 0;
1644         return poll;
1645 }
1646
1647 void FILE_OT_rename(struct wmOperatorType *ot)
1648 {
1649         /* identifiers */
1650         ot->name = "Rename File or Directory";
1651         ot->description = "Rename file or file directory";
1652         ot->idname = "FILE_OT_rename";
1653         
1654         /* api callbacks */
1655         ot->exec = file_rename_exec;
1656         ot->poll = file_rename_poll; 
1657
1658 }
1659
1660 static int file_delete_poll(bContext *C)
1661 {
1662         int poll = ED_operator_file_active(C);
1663         SpaceFile *sfile = CTX_wm_space_file(C);
1664
1665         if (sfile && sfile->params) {
1666                 char dir[FILE_MAX], group[FILE_MAX];
1667                 int numfiles = filelist_numfiles(sfile->files);
1668                 int i;
1669                 int num_selected = 0;
1670
1671                 if (filelist_islibrary(sfile->files, dir, group)) poll = 0;
1672                 for (i = 0; i < numfiles; i++) {
1673                         if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
1674                                 num_selected++;
1675                         }
1676                 }
1677                 if (num_selected <= 0) {
1678                         poll = 0;
1679                 }
1680         }
1681         else
1682                 poll = 0;
1683                 
1684         return poll;
1685 }
1686
1687 int file_delete_exec(bContext *C, wmOperator *UNUSED(op))
1688 {
1689         char str[FILE_MAX];
1690         wmWindowManager *wm = CTX_wm_manager(C);
1691         SpaceFile *sfile = CTX_wm_space_file(C);
1692         struct direntry *file;  
1693         int numfiles = filelist_numfiles(sfile->files);
1694         int i;
1695
1696         for (i = 0; i < numfiles; i++) {
1697                 if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
1698                         file = filelist_file(sfile->files, i);
1699                         BLI_make_file_string(G.main->name, str, sfile->params->dir, file->relname);
1700                         BLI_delete(str, false, false);
1701                 }
1702         }
1703         
1704         ED_fileselect_clear(wm, sfile);
1705         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
1706         
1707         return OPERATOR_FINISHED;
1708
1709 }
1710
1711 void FILE_OT_delete(struct wmOperatorType *ot)
1712 {
1713         /* identifiers */
1714         ot->name = "Delete Selected Files";
1715         ot->description = "Delete selected files";
1716         ot->idname = "FILE_OT_delete";
1717         
1718         /* api callbacks */
1719         ot->invoke = WM_operator_confirm;
1720         ot->exec = file_delete_exec;
1721         ot->poll = file_delete_poll; /* <- important, handler is on window level */
1722 }
1723
1724
1725 void ED_operatormacros_file(void)
1726 {
1727 //      wmOperatorType *ot;
1728 //      wmOperatorTypeMacro *otmacro;
1729         
1730         /* future macros */
1731 }