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