Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / space_file / space_file.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): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_file/space_file.c
28  *  \ingroup spfile
29  */
30
31 #include <string.h>
32 #include <stdio.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "BIF_gl.h"
37
38 #include "BLI_blenlib.h"
39 #include "BLI_utildefines.h"
40 #include "BLI_fileops_types.h"
41
42
43 #include "BKE_appdir.h"
44 #include "BKE_context.h"
45 #include "BKE_screen.h"
46 #include "BKE_global.h"
47
48 #include "RNA_access.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52 #include "WM_message.h"
53
54 #include "ED_space_api.h"
55 #include "ED_screen.h"
56 #include "ED_fileselect.h"
57
58 #include "IMB_imbuf_types.h"
59 #include "IMB_thumbs.h"
60
61 #include "UI_resources.h"
62 #include "UI_view2d.h"
63
64
65 #include "file_intern.h"    // own include
66 #include "fsmenu.h"
67 #include "filelist.h"
68
69 /* ******************** default callbacks for file space ***************** */
70
71 static SpaceLink *file_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
72 {
73         ARegion *ar;
74         SpaceFile *sfile;
75
76         sfile = MEM_callocN(sizeof(SpaceFile), "initfile");
77         sfile->spacetype = SPACE_FILE;
78
79         /* header */
80         ar = MEM_callocN(sizeof(ARegion), "header for file");
81         BLI_addtail(&sfile->regionbase, ar);
82         ar->regiontype = RGN_TYPE_HEADER;
83         ar->alignment = RGN_ALIGN_TOP;
84
85         /* Tools region */
86         ar = MEM_callocN(sizeof(ARegion), "tools region for file");
87         BLI_addtail(&sfile->regionbase, ar);
88         ar->regiontype = RGN_TYPE_TOOLS;
89         ar->alignment = RGN_ALIGN_LEFT;
90
91         /* Tool props (aka operator) region */
92         ar = MEM_callocN(sizeof(ARegion), "tool props region for file");
93         BLI_addtail(&sfile->regionbase, ar);
94         ar->regiontype = RGN_TYPE_TOOL_PROPS;
95         ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
96
97         /* ui list region */
98         ar = MEM_callocN(sizeof(ARegion), "ui region for file");
99         BLI_addtail(&sfile->regionbase, ar);
100         ar->regiontype = RGN_TYPE_UI;
101         ar->alignment = RGN_ALIGN_TOP;
102
103         /* main region */
104         ar = MEM_callocN(sizeof(ARegion), "main region for file");
105         BLI_addtail(&sfile->regionbase, ar);
106         ar->regiontype = RGN_TYPE_WINDOW;
107         ar->v2d.scroll = (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
108         ar->v2d.align = (V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_POS_Y);
109         ar->v2d.keepzoom = (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_LIMITZOOM | V2D_KEEPASPECT);
110         ar->v2d.keeptot = V2D_KEEPTOT_STRICT;
111         ar->v2d.minzoom = ar->v2d.maxzoom = 1.0f;
112
113         return (SpaceLink *)sfile;
114 }
115
116 /* not spacelink itself */
117 static void file_free(SpaceLink *sl)
118 {
119         SpaceFile *sfile = (SpaceFile *) sl;
120
121         BLI_assert(sfile->previews_timer == NULL);
122
123         if (sfile->files) {
124                 // XXXXX would need to do thumbnails_stop here, but no context available
125                 filelist_freelib(sfile->files);
126                 filelist_free(sfile->files);
127                 MEM_freeN(sfile->files);
128                 sfile->files = NULL;
129         }
130
131         if (sfile->folders_prev) {
132                 folderlist_free(sfile->folders_prev);
133                 MEM_freeN(sfile->folders_prev);
134                 sfile->folders_prev = NULL;
135         }
136
137         if (sfile->folders_next) {
138                 folderlist_free(sfile->folders_next);
139                 MEM_freeN(sfile->folders_next);
140                 sfile->folders_next = NULL;
141         }
142
143         if (sfile->params) {
144                 MEM_freeN(sfile->params);
145                 sfile->params = NULL;
146         }
147
148         if (sfile->layout) {
149                 MEM_freeN(sfile->layout);
150                 sfile->layout = NULL;
151         }
152 }
153
154
155 /* spacetype; init callback, area size changes, screen set, etc */
156 static void file_init(wmWindowManager *UNUSED(wm), ScrArea *sa)
157 {
158         SpaceFile *sfile = (SpaceFile *)sa->spacedata.first;
159
160         /* refresh system directory list */
161         fsmenu_refresh_system_category(ED_fsmenu_get());
162
163         /* Update bookmarks 'valid' state.
164          * Done here, because it seems BLI_is_dir() can have huge impact on performances
165          * in some cases, on win systems... See T43684.
166          */
167         fsmenu_refresh_bookmarks_status(ED_fsmenu_get());
168
169         if (sfile->layout) sfile->layout->dirty = true;
170 }
171
172 static void file_exit(wmWindowManager *wm, ScrArea *sa)
173 {
174         SpaceFile *sfile = (SpaceFile *)sa->spacedata.first;
175
176         if (sfile->previews_timer) {
177                 WM_event_remove_timer_notifier(wm, NULL, sfile->previews_timer);
178                 sfile->previews_timer = NULL;
179         }
180
181         ED_fileselect_exit(wm, sa, sfile);
182 }
183
184 static SpaceLink *file_duplicate(SpaceLink *sl)
185 {
186         SpaceFile *sfileo = (SpaceFile *)sl;
187         SpaceFile *sfilen = MEM_dupallocN(sl);
188
189         /* clear or remove stuff from old */
190         sfilen->op = NULL; /* file window doesn't own operators */
191
192         sfilen->previews_timer = NULL;
193         sfilen->smoothscroll_timer = NULL;
194
195         if (sfileo->params) {
196                 sfilen->files = filelist_new(sfileo->params->type);
197                 sfilen->params = MEM_dupallocN(sfileo->params);
198                 filelist_setdir(sfilen->files, sfilen->params->dir);
199         }
200
201         if (sfileo->folders_prev)
202                 sfilen->folders_prev = folderlist_duplicate(sfileo->folders_prev);
203
204         if (sfileo->folders_next)
205                 sfilen->folders_next = folderlist_duplicate(sfileo->folders_next);
206
207         if (sfileo->layout) {
208                 sfilen->layout = MEM_dupallocN(sfileo->layout);
209         }
210         return (SpaceLink *)sfilen;
211 }
212
213 static void file_refresh(const bContext *C, ScrArea *sa)
214 {
215         wmWindowManager *wm = CTX_wm_manager(C);
216         SpaceFile *sfile = CTX_wm_space_file(C);
217         FileSelectParams *params = ED_fileselect_get_params(sfile);
218         struct FSMenu *fsmenu = ED_fsmenu_get();
219
220         if (!sfile->folders_prev) {
221                 sfile->folders_prev = folderlist_new();
222         }
223         if (!sfile->files) {
224                 sfile->files = filelist_new(params->type);
225                 params->highlight_file = -1; /* added this so it opens nicer (ton) */
226         }
227         filelist_setdir(sfile->files, params->dir);
228         filelist_setrecursion(sfile->files, params->recursion_level);
229         filelist_setsorting(sfile->files, params->sort);
230         filelist_setfilter_options(sfile->files, (params->flag & FILE_FILTER) != 0,
231                                                  (params->flag & FILE_HIDE_DOT) != 0,
232                                                  false, /* TODO hide_parent, should be controllable? */
233                                                  params->filter,
234                                                  params->filter_id,
235                                                  params->filter_glob,
236                                                  params->filter_search);
237
238         /* Update the active indices of bookmarks & co. */
239         sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir);
240         sfile->system_bookmarknr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, params->dir);
241         sfile->bookmarknr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir);
242         sfile->recentnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_RECENT, params->dir);
243
244         if (filelist_force_reset(sfile->files)) {
245                 filelist_readjob_stop(wm, sa);
246                 filelist_clear(sfile->files);
247         }
248
249         if (filelist_empty(sfile->files)) {
250                 if (!filelist_pending(sfile->files)) {
251                         filelist_readjob_start(sfile->files, C);
252                 }
253         }
254
255         filelist_sort(sfile->files);
256         filelist_filter(sfile->files);
257
258         if (params->display == FILE_IMGDISPLAY) {
259                 filelist_cache_previews_set(sfile->files, true);
260         }
261         else {
262                 filelist_cache_previews_set(sfile->files, false);
263                 if (sfile->previews_timer) {
264                         WM_event_remove_timer_notifier(wm, CTX_wm_window(C), sfile->previews_timer);
265                         sfile->previews_timer = NULL;
266                 }
267         }
268
269         if (params->renamefile[0] != '\0') {
270                 int idx = filelist_file_findpath(sfile->files, params->renamefile);
271                 if (idx >= 0) {
272                         FileDirEntry *file = filelist_file(sfile->files, idx);
273                         if (file) {
274                                 filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
275                         }
276                 }
277                 BLI_strncpy(sfile->params->renameedit, sfile->params->renamefile, sizeof(sfile->params->renameedit));
278                 /* File listing is now async, do not clear renamefile if matching entry not found
279                  * and dirlist is not finished! */
280                 if (idx >= 0 || filelist_is_ready(sfile->files)) {
281                         params->renamefile[0] = '\0';
282                 }
283         }
284
285         if (sfile->layout) {
286                 sfile->layout->dirty = true;
287         }
288
289         /* Might be called with NULL sa, see file_main_region_draw() below. */
290         if (sa && BKE_area_find_region_type(sa, RGN_TYPE_TOOLS) == NULL) {
291                 /* Create TOOLS/TOOL_PROPS regions. */
292                 file_tools_region(sa);
293
294                 ED_area_initialize(wm, CTX_wm_window(C), sa);
295         }
296
297         ED_area_tag_redraw(sa);
298 }
299
300 static void file_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn, Scene *UNUSED(scene),
301                           WorkSpace *UNUSED(workspace))
302 {
303         SpaceFile *sfile = (SpaceFile *)sa->spacedata.first;
304
305         /* context changes */
306         switch (wmn->category) {
307                 case NC_SPACE:
308                         switch (wmn->data) {
309                                 case ND_SPACE_FILE_LIST:
310                                         ED_area_tag_refresh(sa);
311                                         break;
312                                 case ND_SPACE_FILE_PARAMS:
313                                         ED_area_tag_refresh(sa);
314                                         break;
315                                 case ND_SPACE_FILE_PREVIEW:
316                                         if (sfile->files && filelist_cache_previews_update(sfile->files)) {
317                                                 ED_area_tag_refresh(sa);
318                                         }
319                                         break;
320                         }
321                         break;
322         }
323 }
324
325 /* add handlers, stuff you only do once or on area/region changes */
326 static void file_main_region_init(wmWindowManager *wm, ARegion *ar)
327 {
328         wmKeyMap *keymap;
329
330         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy);
331
332         /* own keymaps */
333         keymap = WM_keymap_find(wm->defaultconf, "File Browser", SPACE_FILE, 0);
334         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
335
336         keymap = WM_keymap_find(wm->defaultconf, "File Browser Main", SPACE_FILE, 0);
337         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
338 }
339
340 static void file_main_region_listener(
341         bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar,
342         wmNotifier *wmn, const Scene *UNUSED(scene))
343 {
344         /* context changes */
345         switch (wmn->category) {
346                 case NC_SPACE:
347                         switch (wmn->data) {
348                                 case ND_SPACE_FILE_LIST:
349                                         ED_region_tag_redraw(ar);
350                                         break;
351                                 case ND_SPACE_FILE_PARAMS:
352                                         ED_region_tag_redraw(ar);
353                                         break;
354                         }
355                         break;
356         }
357 }
358
359 static void file_main_region_message_subscribe(
360         const struct bContext *UNUSED(C),
361         struct WorkSpace *UNUSED(workspace), struct Scene *UNUSED(scene),
362         struct bScreen *screen, struct ScrArea *sa, struct ARegion *ar,
363         struct wmMsgBus *mbus)
364 {
365         SpaceFile *sfile = sa->spacedata.first;
366         FileSelectParams *params = ED_fileselect_get_params(sfile);
367         /* This is a bit odd that a region owns the subscriber for an area,
368          * keep for now since all subscribers for WM are regions.
369          * May be worth re-visiting later. */
370         wmMsgSubscribeValue msg_sub_value_area_tag_refresh = {
371                 .owner = ar,
372                 .user_data = sa,
373                 .notify = ED_area_do_msg_notify_tag_refresh,
374         };
375
376         /* SpaceFile itself. */
377         {
378                 PointerRNA ptr;
379                 RNA_pointer_create(&screen->id, &RNA_SpaceFileBrowser, sfile, &ptr);
380
381                 /* All properties for this space type. */
382                 WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_area_tag_refresh, __func__);
383         }
384
385         /* FileSelectParams */
386         {
387                 PointerRNA ptr;
388                 RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, &ptr);
389
390                 /* All properties for this space type. */
391                 WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_area_tag_refresh, __func__);
392         }
393 }
394
395 static void file_main_region_draw(const bContext *C, ARegion *ar)
396 {
397         /* draw entirely, view changes should be handled here */
398         SpaceFile *sfile = CTX_wm_space_file(C);
399         FileSelectParams *params = ED_fileselect_get_params(sfile);
400
401         View2D *v2d = &ar->v2d;
402         View2DScrollers *scrollers;
403         float col[3];
404
405         /* Needed, because filelist is not initialized on loading */
406         if (!sfile->files || filelist_empty(sfile->files))
407                 file_refresh(C, NULL);
408
409         /* clear and setup matrix */
410         UI_GetThemeColor3fv(TH_BACK, col);
411         glClearColor(col[0], col[1], col[2], 0.0);
412         glClear(GL_COLOR_BUFFER_BIT);
413
414         /* Allow dynamically sliders to be set, saves notifiers etc. */
415
416         if (params->display == FILE_IMGDISPLAY) {
417                 v2d->scroll = V2D_SCROLL_RIGHT;
418                 v2d->keepofs &= ~V2D_LOCKOFS_Y;
419                 v2d->keepofs |= V2D_LOCKOFS_X;
420         }
421         else {
422                 v2d->scroll = V2D_SCROLL_BOTTOM;
423                 v2d->keepofs &= ~V2D_LOCKOFS_X;
424                 v2d->keepofs |= V2D_LOCKOFS_Y;
425
426                 /* XXX this happens on scaling down Screen (like from startup.blend) */
427                 /* view2d has no type specific for filewindow case, which doesnt scroll vertically */
428                 if (v2d->cur.ymax < 0) {
429                         v2d->cur.ymin -= v2d->cur.ymax;
430                         v2d->cur.ymax = 0;
431                 }
432         }
433         /* v2d has initialized flag, so this call will only set the mask correct */
434         UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy);
435
436         /* sets tile/border settings in sfile */
437         file_calc_previews(C, ar);
438
439         /* set view */
440         UI_view2d_view_ortho(v2d);
441
442         /* on first read, find active file */
443         if (params->highlight_file == -1) {
444                 wmEvent *event = CTX_wm_window(C)->eventstate;
445                 file_highlight_set(sfile, ar, event->x, event->y);
446         }
447
448         file_draw_list(C, ar);
449
450         /* reset view matrix */
451         UI_view2d_view_restore(C);
452
453         /* scrollers */
454         scrollers = UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
455         UI_view2d_scrollers_draw(C, v2d, scrollers);
456         UI_view2d_scrollers_free(scrollers);
457
458 }
459
460 static void file_operatortypes(void)
461 {
462         WM_operatortype_append(FILE_OT_select);
463         WM_operatortype_append(FILE_OT_select_walk);
464         WM_operatortype_append(FILE_OT_select_all_toggle);
465         WM_operatortype_append(FILE_OT_select_border);
466         WM_operatortype_append(FILE_OT_select_bookmark);
467         WM_operatortype_append(FILE_OT_highlight);
468         WM_operatortype_append(FILE_OT_execute);
469         WM_operatortype_append(FILE_OT_cancel);
470         WM_operatortype_append(FILE_OT_parent);
471         WM_operatortype_append(FILE_OT_previous);
472         WM_operatortype_append(FILE_OT_next);
473         WM_operatortype_append(FILE_OT_refresh);
474         WM_operatortype_append(FILE_OT_bookmark_toggle);
475         WM_operatortype_append(FILE_OT_bookmark_add);
476         WM_operatortype_append(FILE_OT_bookmark_delete);
477         WM_operatortype_append(FILE_OT_bookmark_cleanup);
478         WM_operatortype_append(FILE_OT_bookmark_move);
479         WM_operatortype_append(FILE_OT_reset_recent);
480         WM_operatortype_append(FILE_OT_hidedot);
481         WM_operatortype_append(FILE_OT_filenum);
482         WM_operatortype_append(FILE_OT_directory_new);
483         WM_operatortype_append(FILE_OT_delete);
484         WM_operatortype_append(FILE_OT_rename);
485         WM_operatortype_append(FILE_OT_smoothscroll);
486         WM_operatortype_append(FILE_OT_filepath_drop);
487 }
488
489 /* NOTE: do not add .blend file reading on this level */
490 static void file_keymap(struct wmKeyConfig *keyconf)
491 {
492         wmKeyMapItem *kmi;
493         /* keys for all regions */
494         wmKeyMap *keymap = WM_keymap_find(keyconf, "File Browser", SPACE_FILE, 0);
495
496         /* More common 'fliebrowser-like navigation' shortcuts. */
497         WM_keymap_add_item(keymap, "FILE_OT_parent", UPARROWKEY, KM_PRESS, KM_ALT, 0);
498         WM_keymap_add_item(keymap, "FILE_OT_previous", LEFTARROWKEY, KM_PRESS, KM_ALT, 0);
499         WM_keymap_add_item(keymap, "FILE_OT_next", RIGHTARROWKEY, KM_PRESS, KM_ALT, 0);
500         WM_keymap_add_item(keymap, "FILE_OT_refresh", RKEY, KM_PRESS, 0, 0);
501
502         WM_keymap_add_item(keymap, "FILE_OT_parent", PKEY, KM_PRESS, 0, 0);
503         WM_keymap_add_item(keymap, "FILE_OT_previous", BACKSPACEKEY, KM_PRESS, 0, 0);
504         WM_keymap_add_item(keymap, "FILE_OT_next", BACKSPACEKEY, KM_PRESS, KM_SHIFT, 0);
505         kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", HKEY, KM_PRESS, 0, 0);
506         RNA_string_set(kmi->ptr, "data_path", "space_data.params.show_hidden");
507         WM_keymap_add_item(keymap, "FILE_OT_directory_new", IKEY, KM_PRESS, 0, 0);
508         WM_keymap_add_item(keymap, "FILE_OT_delete", XKEY, KM_PRESS, 0, 0);
509         WM_keymap_add_item(keymap, "FILE_OT_delete", DELKEY, KM_PRESS, 0, 0);
510
511         WM_keymap_verify_item(keymap, "FILE_OT_smoothscroll", TIMER1, KM_ANY, KM_ANY, 0);
512
513         WM_keymap_add_item(keymap, "FILE_OT_bookmark_toggle", TKEY, KM_PRESS, 0, 0);
514         WM_keymap_add_item(keymap, "FILE_OT_bookmark_add", BKEY, KM_PRESS, KM_CTRL, 0);
515
516         /* keys for main region */
517         keymap = WM_keymap_find(keyconf, "File Browser Main", SPACE_FILE, 0);
518         kmi = WM_keymap_add_item(keymap, "FILE_OT_execute", LEFTMOUSE, KM_DBL_CLICK, 0, 0);
519         RNA_boolean_set(kmi->ptr, "need_active", true);
520
521         WM_keymap_add_item(keymap, "FILE_OT_refresh", PADPERIOD, KM_PRESS, 0, 0);
522
523         /* left mouse selects and opens */
524         WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, 0, 0);
525         kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_SHIFT, 0);
526         RNA_boolean_set(kmi->ptr, "extend", true);
527         kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_CTRL | KM_SHIFT, 0);
528         RNA_boolean_set(kmi->ptr, "extend", true);
529         RNA_boolean_set(kmi->ptr, "fill", true);
530
531         /* right mouse selects without opening */
532         kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, 0, 0);
533         RNA_boolean_set(kmi->ptr, "open", false);
534         kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, KM_SHIFT, 0);
535         RNA_boolean_set(kmi->ptr, "extend", true);
536         RNA_boolean_set(kmi->ptr, "open", false);
537         kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, KM_ALT, 0);
538         RNA_boolean_set(kmi->ptr, "extend", true);
539         RNA_boolean_set(kmi->ptr, "fill", true);
540         RNA_boolean_set(kmi->ptr, "open", false);
541
542
543         /* arrow keys navigation (walk selecting) */
544         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", UPARROWKEY, KM_PRESS, 0, 0);
545         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_UP);
546         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", UPARROWKEY, KM_PRESS, KM_SHIFT, 0);
547         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_UP);
548         RNA_boolean_set(kmi->ptr, "extend", true);
549         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", UPARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
550         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_UP);
551         RNA_boolean_set(kmi->ptr, "extend", true);
552         RNA_boolean_set(kmi->ptr, "fill", true);
553
554         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", DOWNARROWKEY, KM_PRESS, 0, 0);
555         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_DOWN);
556         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", DOWNARROWKEY, KM_PRESS, KM_SHIFT, 0);
557         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_DOWN);
558         RNA_boolean_set(kmi->ptr, "extend", true);
559         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", DOWNARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
560         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_DOWN);
561         RNA_boolean_set(kmi->ptr, "extend", true);
562         RNA_boolean_set(kmi->ptr, "fill", true);
563
564         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", LEFTARROWKEY, KM_PRESS, 0, 0);
565         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_LEFT);
566         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", LEFTARROWKEY, KM_PRESS, KM_SHIFT, 0);
567         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_LEFT);
568         RNA_boolean_set(kmi->ptr, "extend", true);
569         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", LEFTARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
570         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_LEFT);
571         RNA_boolean_set(kmi->ptr, "extend", true);
572         RNA_boolean_set(kmi->ptr, "fill", true);
573
574         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", RIGHTARROWKEY, KM_PRESS, 0, 0);
575         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_RIGHT);
576         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", RIGHTARROWKEY, KM_PRESS, KM_SHIFT, 0);
577         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_RIGHT);
578         RNA_boolean_set(kmi->ptr, "extend", true);
579         kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", RIGHTARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
580         RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_RIGHT);
581         RNA_boolean_set(kmi->ptr, "extend", true);
582         RNA_boolean_set(kmi->ptr, "fill", true);
583
584
585         /* front and back mouse folder navigation */
586         WM_keymap_add_item(keymap, "FILE_OT_previous", BUTTON4MOUSE, KM_CLICK, 0, 0);
587         WM_keymap_add_item(keymap, "FILE_OT_next", BUTTON5MOUSE, KM_CLICK, 0, 0);
588
589         WM_keymap_add_item(keymap, "FILE_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0);
590         WM_keymap_add_item(keymap, "FILE_OT_select_border", BKEY, KM_PRESS, 0, 0);
591         WM_keymap_add_item(keymap, "FILE_OT_select_border", EVT_TWEAK_L, KM_ANY, 0, 0);
592         WM_keymap_add_item(keymap, "FILE_OT_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
593         WM_keymap_add_item(keymap, "FILE_OT_highlight", MOUSEMOVE, KM_ANY, KM_ANY, 0);
594         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADPLUSKEY, KM_PRESS, 0, 0);
595         RNA_int_set(kmi->ptr, "increment", 1);
596         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADPLUSKEY, KM_PRESS, KM_SHIFT, 0);
597         RNA_int_set(kmi->ptr, "increment", 10);
598         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
599         RNA_int_set(kmi->ptr, "increment", 100);
600         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADMINUS, KM_PRESS, 0, 0);
601         RNA_int_set(kmi->ptr, "increment", -1);
602         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADMINUS, KM_PRESS, KM_SHIFT, 0);
603         RNA_int_set(kmi->ptr, "increment", -10);
604         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADMINUS, KM_PRESS, KM_CTRL, 0);
605         RNA_int_set(kmi->ptr, "increment", -100);
606
607
608         /* keys for button region (top) */
609         keymap = WM_keymap_find(keyconf, "File Browser Buttons", SPACE_FILE, 0);
610         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADPLUSKEY, KM_PRESS, 0, 0);
611         RNA_int_set(kmi->ptr, "increment", 1);
612         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADPLUSKEY, KM_PRESS, KM_SHIFT, 0);
613         RNA_int_set(kmi->ptr, "increment", 10);
614         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
615         RNA_int_set(kmi->ptr, "increment", 100);
616         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADMINUS, KM_PRESS, 0, 0);
617         RNA_int_set(kmi->ptr, "increment", -1);
618         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADMINUS, KM_PRESS, KM_SHIFT, 0);
619         RNA_int_set(kmi->ptr, "increment", -10);
620         kmi = WM_keymap_add_item(keymap, "FILE_OT_filenum", PADMINUS, KM_PRESS, KM_CTRL, 0);
621         RNA_int_set(kmi->ptr, "increment", -100);
622 }
623
624
625 static void file_tools_region_init(wmWindowManager *wm, ARegion *ar)
626 {
627         wmKeyMap *keymap;
628
629         ar->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_VERTICAL_HIDE;
630         ED_region_panels_init(wm, ar);
631
632         /* own keymaps */
633         keymap = WM_keymap_find(wm->defaultconf, "File Browser", SPACE_FILE, 0);
634         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
635 }
636
637 static void file_tools_region_draw(const bContext *C, ARegion *ar)
638 {
639         ED_region_panels(C, ar, NULL, -1, true);
640 }
641
642 static void file_tools_region_listener(
643         bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *UNUSED(ar),
644         wmNotifier *UNUSED(wmn), const Scene *UNUSED(scene))
645 {
646 #if 0
647         /* context changes */
648         switch (wmn->category) {
649                 /* pass */
650         }
651 #endif
652 }
653
654 /* add handlers, stuff you only do once or on area/region changes */
655 static void file_header_region_init(wmWindowManager *wm, ARegion *ar)
656 {
657         wmKeyMap *keymap;
658
659         ED_region_header_init(ar);
660
661         keymap = WM_keymap_find(wm->defaultconf, "File Browser", SPACE_FILE, 0);
662         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
663 }
664
665 static void file_header_region_draw(const bContext *C, ARegion *ar)
666 {
667         ED_region_header(C, ar);
668 }
669
670 /* add handlers, stuff you only do once or on area/region changes */
671 static void file_ui_region_init(wmWindowManager *wm, ARegion *ar)
672 {
673         wmKeyMap *keymap;
674
675         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy);
676
677         /* own keymap */
678         keymap = WM_keymap_find(wm->defaultconf, "File Browser", SPACE_FILE, 0);
679         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
680
681         keymap = WM_keymap_find(wm->defaultconf, "File Browser Buttons", SPACE_FILE, 0);
682         WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
683 }
684
685 static void file_ui_region_draw(const bContext *C, ARegion *ar)
686 {
687         float col[3];
688         /* clear */
689         UI_GetThemeColor3fv(TH_BACK, col);
690         glClearColor(col[0], col[1], col[2], 0.0);
691         glClear(GL_COLOR_BUFFER_BIT);
692
693         /* scrolling here is just annoying, disable it */
694         ar->v2d.cur.ymax = BLI_rctf_size_y(&ar->v2d.cur);
695         ar->v2d.cur.ymin = 0;
696
697         /* set view2d view matrix for scrolling (without scrollers) */
698         UI_view2d_view_ortho(&ar->v2d);
699
700
701         file_draw_buttons(C, ar);
702
703         UI_view2d_view_restore(C);
704 }
705
706 static void file_ui_region_listener(
707         bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar,
708         wmNotifier *wmn, const Scene *UNUSED(scene))
709 {
710         /* context changes */
711         switch (wmn->category) {
712                 case NC_SPACE:
713                         switch (wmn->data) {
714                                 case ND_SPACE_FILE_LIST:
715                                         ED_region_tag_redraw(ar);
716                                         break;
717                         }
718                         break;
719         }
720 }
721
722 static int filepath_drop_poll(bContext *C, wmDrag *drag, const wmEvent *UNUSED(event))
723 {
724         if (drag->type == WM_DRAG_PATH) {
725                 SpaceFile *sfile = CTX_wm_space_file(C);
726                 if (sfile) {
727                         return 1;
728                 }
729         }
730         return 0;
731 }
732
733 static void filepath_drop_copy(wmDrag *drag, wmDropBox *drop)
734 {
735         RNA_string_set(drop->ptr, "filepath", drag->path);
736 }
737
738 /* region dropbox definition */
739 static void file_dropboxes(void)
740 {
741         ListBase *lb = WM_dropboxmap_find("Window", SPACE_EMPTY, RGN_TYPE_WINDOW);
742
743         WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy);
744 }
745
746 /* only called once, from space/spacetypes.c */
747 void ED_spacetype_file(void)
748 {
749         SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype file");
750         ARegionType *art;
751
752         st->spaceid = SPACE_FILE;
753         strncpy(st->name, "File", BKE_ST_MAXNAME);
754
755         st->new = file_new;
756         st->free = file_free;
757         st->init = file_init;
758         st->exit = file_exit;
759         st->duplicate = file_duplicate;
760         st->refresh = file_refresh;
761         st->listener = file_listener;
762         st->operatortypes = file_operatortypes;
763         st->keymap = file_keymap;
764         st->dropboxes = file_dropboxes;
765
766         /* regions: main window */
767         art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
768         art->regionid = RGN_TYPE_WINDOW;
769         art->init = file_main_region_init;
770         art->draw = file_main_region_draw;
771         art->listener = file_main_region_listener;
772         art->message_subscribe = file_main_region_message_subscribe;
773         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
774         BLI_addhead(&st->regiontypes, art);
775
776         /* regions: header */
777         art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
778         art->regionid = RGN_TYPE_HEADER;
779         art->prefsizey = HEADERY;
780         art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
781         art->init = file_header_region_init;
782         art->draw = file_header_region_draw;
783         // art->listener = file_header_region_listener;
784         BLI_addhead(&st->regiontypes, art);
785
786         /* regions: ui */
787         art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
788         art->regionid = RGN_TYPE_UI;
789         art->prefsizey = 60;
790         art->keymapflag = ED_KEYMAP_UI;
791         art->listener = file_ui_region_listener;
792         art->init = file_ui_region_init;
793         art->draw = file_ui_region_draw;
794         BLI_addhead(&st->regiontypes, art);
795
796         /* regions: channels (directories) */
797         art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
798         art->regionid = RGN_TYPE_TOOLS;
799         art->prefsizex = 240;
800         art->prefsizey = 60;
801         art->keymapflag = ED_KEYMAP_UI;
802         art->listener = file_tools_region_listener;
803         art->init = file_tools_region_init;
804         art->draw = file_tools_region_draw;
805         BLI_addhead(&st->regiontypes, art);
806
807         /* regions: tool properties */
808         art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region");
809         art->regionid = RGN_TYPE_TOOL_PROPS;
810         art->prefsizex = 0;
811         art->prefsizey = 360;
812         art->keymapflag = ED_KEYMAP_UI;
813         art->listener = file_tools_region_listener;
814         art->init = file_tools_region_init;
815         art->draw = file_tools_region_draw;
816         BLI_addhead(&st->regiontypes, art);
817         file_panels_register(art);
818
819         BKE_spacetype_register(st);
820
821 }
822
823 void ED_file_init(void)
824 {
825         ED_file_read_bookmarks();
826
827         if (G.background == false) {
828                 filelist_init_icons();
829         }
830
831         IMB_thumb_makedirs();
832 }
833
834 void ED_file_exit(void)
835 {
836         fsmenu_free();
837
838         if (G.background == false) {
839                 filelist_free_icons();
840         }
841 }
842
843 void ED_file_read_bookmarks(void)
844 {
845         const char * const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
846
847         fsmenu_free();
848
849         fsmenu_read_system(ED_fsmenu_get(), true);
850
851         if (cfgdir) {
852                 char name[FILE_MAX];
853                 BLI_make_file_string("/", name, cfgdir, BLENDER_BOOKMARK_FILE);
854                 fsmenu_read_bookmarks(ED_fsmenu_get(), name);
855         }
856 }
857