1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup spfile
22  */
23 
24 #include <stdio.h>
25 #include <string.h>
26 
27 #include "MEM_guardedalloc.h"
28 
29 #include "BLI_blenlib.h"
30 #include "BLI_utildefines.h"
31 
32 #include "BKE_appdir.h"
33 #include "BKE_context.h"
34 #include "BKE_global.h"
35 #include "BKE_screen.h"
36 
37 #include "RNA_access.h"
38 
39 #include "WM_api.h"
40 #include "WM_message.h"
41 #include "WM_types.h"
42 
43 #include "ED_fileselect.h"
44 #include "ED_screen.h"
45 #include "ED_space_api.h"
46 
47 #include "IMB_imbuf_types.h"
48 #include "IMB_thumbs.h"
49 
50 #include "UI_resources.h"
51 #include "UI_view2d.h"
52 
53 #include "GPU_framebuffer.h"
54 #include "file_intern.h" /* own include */
55 #include "filelist.h"
56 #include "fsmenu.h"
57 
file_execute_region_ensure(ScrArea * area,ARegion * region_prev)58 static ARegion *file_execute_region_ensure(ScrArea *area, ARegion *region_prev)
59 {
60   ARegion *region;
61 
62   if ((region = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE)) != NULL) {
63     return region;
64   }
65 
66   region = MEM_callocN(sizeof(ARegion), "execute region for file");
67   BLI_insertlinkafter(&area->regionbase, region_prev, region);
68   region->regiontype = RGN_TYPE_EXECUTE;
69   region->alignment = RGN_ALIGN_BOTTOM;
70   region->flag = RGN_FLAG_DYNAMIC_SIZE;
71 
72   return region;
73 }
74 
file_tool_props_region_ensure(ScrArea * area,ARegion * region_prev)75 static ARegion *file_tool_props_region_ensure(ScrArea *area, ARegion *region_prev)
76 {
77   ARegion *region;
78 
79   if ((region = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS)) != NULL) {
80     return region;
81   }
82 
83   /* add subdiv level; after execute region */
84   region = MEM_callocN(sizeof(ARegion), "tool props for file");
85   BLI_insertlinkafter(&area->regionbase, region_prev, region);
86   region->regiontype = RGN_TYPE_TOOL_PROPS;
87   region->alignment = RGN_ALIGN_RIGHT;
88 
89   return region;
90 }
91 
92 /* ******************** default callbacks for file space ***************** */
93 
file_create(const ScrArea * UNUSED (area),const Scene * UNUSED (scene))94 static SpaceLink *file_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
95 {
96   ARegion *region;
97   SpaceFile *sfile;
98 
99   sfile = MEM_callocN(sizeof(SpaceFile), "initfile");
100   sfile->spacetype = SPACE_FILE;
101 
102   /* header */
103   region = MEM_callocN(sizeof(ARegion), "header for file");
104   BLI_addtail(&sfile->regionbase, region);
105   region->regiontype = RGN_TYPE_HEADER;
106   /* Ignore user preference "USER_HEADER_BOTTOM" here (always show top for new types). */
107   region->alignment = RGN_ALIGN_TOP;
108 
109   /* Tools region */
110   region = MEM_callocN(sizeof(ARegion), "tools region for file");
111   BLI_addtail(&sfile->regionbase, region);
112   region->regiontype = RGN_TYPE_TOOLS;
113   region->alignment = RGN_ALIGN_LEFT;
114 
115   /* ui list region */
116   region = MEM_callocN(sizeof(ARegion), "ui region for file");
117   BLI_addtail(&sfile->regionbase, region);
118   region->regiontype = RGN_TYPE_UI;
119   region->alignment = RGN_ALIGN_TOP;
120   region->flag |= RGN_FLAG_DYNAMIC_SIZE;
121 
122   /* Tool props and execute region are added as needed, see file_refresh(). */
123 
124   /* main region */
125   region = MEM_callocN(sizeof(ARegion), "main region for file");
126   BLI_addtail(&sfile->regionbase, region);
127   region->regiontype = RGN_TYPE_WINDOW;
128   region->v2d.scroll = (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
129   region->v2d.align = (V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_POS_Y);
130   region->v2d.keepzoom = (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_LIMITZOOM | V2D_KEEPASPECT);
131   region->v2d.keeptot = V2D_KEEPTOT_STRICT;
132   region->v2d.minzoom = region->v2d.maxzoom = 1.0f;
133 
134   return (SpaceLink *)sfile;
135 }
136 
137 /* not spacelink itself */
file_free(SpaceLink * sl)138 static void file_free(SpaceLink *sl)
139 {
140   SpaceFile *sfile = (SpaceFile *)sl;
141 
142   BLI_assert(sfile->previews_timer == NULL);
143 
144   if (sfile->files) {
145     /* XXX would need to do thumbnails_stop here, but no context available */
146     filelist_freelib(sfile->files);
147     filelist_free(sfile->files);
148     MEM_freeN(sfile->files);
149     sfile->files = NULL;
150   }
151 
152   if (sfile->folders_prev) {
153     folderlist_free(sfile->folders_prev);
154     MEM_freeN(sfile->folders_prev);
155     sfile->folders_prev = NULL;
156   }
157 
158   if (sfile->folders_next) {
159     folderlist_free(sfile->folders_next);
160     MEM_freeN(sfile->folders_next);
161     sfile->folders_next = NULL;
162   }
163 
164   if (sfile->params) {
165     MEM_freeN(sfile->params);
166     sfile->params = NULL;
167   }
168 
169   if (sfile->layout) {
170     MEM_freeN(sfile->layout);
171     sfile->layout = NULL;
172   }
173 }
174 
175 /* spacetype; init callback, area size changes, screen set, etc */
file_init(wmWindowManager * UNUSED (wm),ScrArea * area)176 static void file_init(wmWindowManager *UNUSED(wm), ScrArea *area)
177 {
178   SpaceFile *sfile = (SpaceFile *)area->spacedata.first;
179 
180   if (sfile->layout) {
181     sfile->layout->dirty = true;
182   }
183 }
184 
file_exit(wmWindowManager * wm,ScrArea * area)185 static void file_exit(wmWindowManager *wm, ScrArea *area)
186 {
187   SpaceFile *sfile = (SpaceFile *)area->spacedata.first;
188 
189   if (sfile->previews_timer) {
190     WM_event_remove_timer_notifier(wm, NULL, sfile->previews_timer);
191     sfile->previews_timer = NULL;
192   }
193 
194   ED_fileselect_exit(wm, NULL, sfile);
195 }
196 
file_duplicate(SpaceLink * sl)197 static SpaceLink *file_duplicate(SpaceLink *sl)
198 {
199   SpaceFile *sfileo = (SpaceFile *)sl;
200   SpaceFile *sfilen = MEM_dupallocN(sl);
201 
202   /* clear or remove stuff from old */
203   sfilen->op = NULL; /* file window doesn't own operators */
204 
205   sfilen->previews_timer = NULL;
206   sfilen->smoothscroll_timer = NULL;
207 
208   if (sfileo->params) {
209     sfilen->files = filelist_new(sfileo->params->type);
210     sfilen->params = MEM_dupallocN(sfileo->params);
211     filelist_setdir(sfilen->files, sfilen->params->dir);
212   }
213 
214   if (sfileo->folders_prev) {
215     sfilen->folders_prev = folderlist_duplicate(sfileo->folders_prev);
216   }
217 
218   if (sfileo->folders_next) {
219     sfilen->folders_next = folderlist_duplicate(sfileo->folders_next);
220   }
221 
222   if (sfileo->layout) {
223     sfilen->layout = MEM_dupallocN(sfileo->layout);
224   }
225   return (SpaceLink *)sfilen;
226 }
227 
file_ensure_valid_region_state(bContext * C,wmWindowManager * wm,wmWindow * win,ScrArea * area,SpaceFile * sfile,FileSelectParams * params)228 static void file_ensure_valid_region_state(bContext *C,
229                                            wmWindowManager *wm,
230                                            wmWindow *win,
231                                            ScrArea *area,
232                                            SpaceFile *sfile,
233                                            FileSelectParams *params)
234 {
235   ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI);
236   ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS);
237   ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE);
238   bool needs_init = false; /* To avoid multiple ED_area_init() calls. */
239 
240   /* If there's an file-operation, ensure we have the option and execute region */
241   if (sfile->op && (region_props == NULL)) {
242     region_execute = file_execute_region_ensure(area, region_ui);
243     region_props = file_tool_props_region_ensure(area, region_execute);
244 
245     if (params->flag & FILE_HIDE_TOOL_PROPS) {
246       region_props->flag |= RGN_FLAG_HIDDEN;
247     }
248     else {
249       region_props->flag &= ~RGN_FLAG_HIDDEN;
250     }
251 
252     needs_init = true;
253   }
254   /* If there's _no_ file-operation, ensure we _don't_ have the option and execute region */
255   else if ((sfile->op == NULL) && (region_props != NULL)) {
256     BLI_assert(region_execute != NULL);
257 
258     ED_region_remove(C, area, region_props);
259     ED_region_remove(C, area, region_execute);
260     needs_init = true;
261   }
262 
263   if (needs_init) {
264     ED_area_init(wm, win, area);
265   }
266 }
267 
file_refresh(const bContext * C,ScrArea * area)268 static void file_refresh(const bContext *C, ScrArea *area)
269 {
270   wmWindowManager *wm = CTX_wm_manager(C);
271   wmWindow *win = CTX_wm_window(C);
272   SpaceFile *sfile = CTX_wm_space_file(C);
273   FileSelectParams *params = ED_fileselect_get_params(sfile);
274   struct FSMenu *fsmenu = ED_fsmenu_get();
275 
276   if (!sfile->folders_prev) {
277     sfile->folders_prev = folderlist_new();
278   }
279   if (!sfile->files) {
280     sfile->files = filelist_new(params->type);
281     params->highlight_file = -1; /* added this so it opens nicer (ton) */
282   }
283   filelist_setdir(sfile->files, params->dir);
284   filelist_setrecursion(sfile->files, params->recursion_level);
285   filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT);
286   filelist_setfilter_options(
287       sfile->files,
288       (params->flag & FILE_FILTER) != 0,
289       (params->flag & FILE_HIDE_DOT) != 0,
290       true, /* Just always hide parent, prefer to not add an extra user option for this. */
291       params->filter,
292       params->filter_id,
293       params->filter_glob,
294       params->filter_search);
295 
296   /* Update the active indices of bookmarks & co. */
297   sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir);
298   sfile->system_bookmarknr = fsmenu_get_active_indices(
299       fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, params->dir);
300   sfile->bookmarknr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir);
301   sfile->recentnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_RECENT, params->dir);
302 
303   if (filelist_force_reset(sfile->files)) {
304     filelist_readjob_stop(wm, CTX_data_scene(C));
305     filelist_clear(sfile->files);
306   }
307 
308   if (filelist_empty(sfile->files)) {
309     if (!filelist_pending(sfile->files)) {
310       filelist_readjob_start(sfile->files, C);
311     }
312   }
313 
314   filelist_sort(sfile->files);
315   filelist_filter(sfile->files);
316 
317   if (params->display == FILE_IMGDISPLAY) {
318     filelist_cache_previews_set(sfile->files, true);
319   }
320   else {
321     filelist_cache_previews_set(sfile->files, false);
322     if (sfile->previews_timer) {
323       WM_event_remove_timer_notifier(wm, win, sfile->previews_timer);
324       sfile->previews_timer = NULL;
325     }
326   }
327 
328   if (params->rename_flag != 0) {
329     file_params_renamefile_activate(sfile, params);
330   }
331 
332   if (sfile->layout) {
333     sfile->layout->dirty = true;
334   }
335 
336   /* Might be called with NULL area, see file_main_region_draw() below. */
337   if (area) {
338     file_ensure_valid_region_state((bContext *)C, wm, win, area, sfile, params);
339   }
340 
341   ED_area_tag_redraw(area);
342 }
343 
file_listener(wmWindow * UNUSED (win),ScrArea * area,wmNotifier * wmn,Scene * UNUSED (scene))344 static void file_listener(wmWindow *UNUSED(win),
345                           ScrArea *area,
346                           wmNotifier *wmn,
347                           Scene *UNUSED(scene))
348 {
349   SpaceFile *sfile = (SpaceFile *)area->spacedata.first;
350 
351   /* context changes */
352   switch (wmn->category) {
353     case NC_SPACE:
354       switch (wmn->data) {
355         case ND_SPACE_FILE_LIST:
356           ED_area_tag_refresh(area);
357           break;
358         case ND_SPACE_FILE_PARAMS:
359           ED_area_tag_refresh(area);
360           break;
361         case ND_SPACE_FILE_PREVIEW:
362           if (sfile->files && filelist_cache_previews_update(sfile->files)) {
363             ED_area_tag_refresh(area);
364           }
365           break;
366       }
367       break;
368   }
369 }
370 
371 /* add handlers, stuff you only do once or on area/region changes */
file_main_region_init(wmWindowManager * wm,ARegion * region)372 static void file_main_region_init(wmWindowManager *wm, ARegion *region)
373 {
374   wmKeyMap *keymap;
375 
376   UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy);
377 
378   /* own keymaps */
379   keymap = WM_keymap_ensure(wm->defaultconf, "File Browser", SPACE_FILE, 0);
380   WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
381 
382   keymap = WM_keymap_ensure(wm->defaultconf, "File Browser Main", SPACE_FILE, 0);
383   WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
384 }
385 
file_main_region_listener(wmWindow * UNUSED (win),ScrArea * UNUSED (area),ARegion * region,wmNotifier * wmn,const Scene * UNUSED (scene))386 static void file_main_region_listener(wmWindow *UNUSED(win),
387                                       ScrArea *UNUSED(area),
388                                       ARegion *region,
389                                       wmNotifier *wmn,
390                                       const Scene *UNUSED(scene))
391 {
392   /* context changes */
393   switch (wmn->category) {
394     case NC_SPACE:
395       switch (wmn->data) {
396         case ND_SPACE_FILE_LIST:
397           ED_region_tag_redraw(region);
398           break;
399         case ND_SPACE_FILE_PARAMS:
400           ED_region_tag_redraw(region);
401           break;
402       }
403       break;
404   }
405 }
406 
file_main_region_message_subscribe(const struct bContext * UNUSED (C),struct WorkSpace * UNUSED (workspace),struct Scene * UNUSED (scene),struct bScreen * screen,struct ScrArea * area,struct ARegion * region,struct wmMsgBus * mbus)407 static void file_main_region_message_subscribe(const struct bContext *UNUSED(C),
408                                                struct WorkSpace *UNUSED(workspace),
409                                                struct Scene *UNUSED(scene),
410                                                struct bScreen *screen,
411                                                struct ScrArea *area,
412                                                struct ARegion *region,
413                                                struct wmMsgBus *mbus)
414 {
415   SpaceFile *sfile = area->spacedata.first;
416   FileSelectParams *params = ED_fileselect_get_params(sfile);
417   /* This is a bit odd that a region owns the subscriber for an area,
418    * keep for now since all subscribers for WM are regions.
419    * May be worth re-visiting later. */
420   wmMsgSubscribeValue msg_sub_value_area_tag_refresh = {
421       .owner = region,
422       .user_data = area,
423       .notify = ED_area_do_msg_notify_tag_refresh,
424   };
425 
426   /* SpaceFile itself. */
427   {
428     PointerRNA ptr;
429     RNA_pointer_create(&screen->id, &RNA_SpaceFileBrowser, sfile, &ptr);
430 
431     /* All properties for this space type. */
432     WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_area_tag_refresh, __func__);
433   }
434 
435   /* FileSelectParams */
436   {
437     PointerRNA ptr;
438     RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, &ptr);
439 
440     /* All properties for this space type. */
441     WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_area_tag_refresh, __func__);
442   }
443 }
444 
file_main_region_draw(const bContext * C,ARegion * region)445 static void file_main_region_draw(const bContext *C, ARegion *region)
446 {
447   /* draw entirely, view changes should be handled here */
448   SpaceFile *sfile = CTX_wm_space_file(C);
449   FileSelectParams *params = ED_fileselect_get_params(sfile);
450 
451   View2D *v2d = &region->v2d;
452 
453   /* Needed, because filelist is not initialized on loading */
454   if (!sfile->files || filelist_empty(sfile->files)) {
455     file_refresh(C, NULL);
456   }
457 
458   /* clear and setup matrix */
459   UI_ThemeClearColor(TH_BACK);
460 
461   /* Allow dynamically sliders to be set, saves notifiers etc. */
462 
463   if (params->display == FILE_IMGDISPLAY) {
464     v2d->scroll = V2D_SCROLL_RIGHT;
465     v2d->keepofs &= ~V2D_LOCKOFS_Y;
466     v2d->keepofs |= V2D_LOCKOFS_X;
467   }
468   else if (params->display == FILE_VERTICALDISPLAY) {
469     v2d->scroll = V2D_SCROLL_RIGHT;
470     v2d->keepofs &= ~V2D_LOCKOFS_Y;
471     v2d->keepofs |= V2D_LOCKOFS_X;
472   }
473   else {
474     v2d->scroll = V2D_SCROLL_BOTTOM;
475     v2d->keepofs &= ~V2D_LOCKOFS_X;
476     v2d->keepofs |= V2D_LOCKOFS_Y;
477 
478     /* XXX this happens on scaling down Screen (like from startup.blend) */
479     /* view2d has no type specific for filewindow case, which doesn't scroll vertically */
480     if (v2d->cur.ymax < 0) {
481       v2d->cur.ymin -= v2d->cur.ymax;
482       v2d->cur.ymax = 0;
483     }
484   }
485   /* v2d has initialized flag, so this call will only set the mask correct */
486   UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy);
487 
488   /* sets tile/border settings in sfile */
489   file_calc_previews(C, region);
490 
491   /* set view */
492   UI_view2d_view_ortho(v2d);
493 
494   /* on first read, find active file */
495   if (params->highlight_file == -1) {
496     wmEvent *event = CTX_wm_window(C)->eventstate;
497     file_highlight_set(sfile, region, event->x, event->y);
498   }
499 
500   file_draw_list(C, region);
501 
502   /* reset view matrix */
503   UI_view2d_view_restore(C);
504 
505   /* scrollers */
506   rcti view_rect;
507   ED_fileselect_layout_maskrect(sfile->layout, v2d, &view_rect);
508   UI_view2d_scrollers_draw(v2d, &view_rect);
509 }
510 
file_operatortypes(void)511 static void file_operatortypes(void)
512 {
513   WM_operatortype_append(FILE_OT_select);
514   WM_operatortype_append(FILE_OT_select_walk);
515   WM_operatortype_append(FILE_OT_select_all);
516   WM_operatortype_append(FILE_OT_select_box);
517   WM_operatortype_append(FILE_OT_select_bookmark);
518   WM_operatortype_append(FILE_OT_highlight);
519   WM_operatortype_append(FILE_OT_sort_column_ui_context);
520   WM_operatortype_append(FILE_OT_execute);
521   WM_operatortype_append(FILE_OT_cancel);
522   WM_operatortype_append(FILE_OT_parent);
523   WM_operatortype_append(FILE_OT_previous);
524   WM_operatortype_append(FILE_OT_next);
525   WM_operatortype_append(FILE_OT_refresh);
526   WM_operatortype_append(FILE_OT_bookmark_add);
527   WM_operatortype_append(FILE_OT_bookmark_delete);
528   WM_operatortype_append(FILE_OT_bookmark_cleanup);
529   WM_operatortype_append(FILE_OT_bookmark_move);
530   WM_operatortype_append(FILE_OT_reset_recent);
531   WM_operatortype_append(FILE_OT_hidedot);
532   WM_operatortype_append(FILE_OT_filenum);
533   WM_operatortype_append(FILE_OT_directory_new);
534   WM_operatortype_append(FILE_OT_delete);
535   WM_operatortype_append(FILE_OT_rename);
536   WM_operatortype_append(FILE_OT_smoothscroll);
537   WM_operatortype_append(FILE_OT_filepath_drop);
538   WM_operatortype_append(FILE_OT_start_filter);
539 }
540 
541 /* NOTE: do not add .blend file reading on this level */
file_keymap(struct wmKeyConfig * keyconf)542 static void file_keymap(struct wmKeyConfig *keyconf)
543 {
544   /* keys for all regions */
545   WM_keymap_ensure(keyconf, "File Browser", SPACE_FILE, 0);
546 
547   /* keys for main region */
548   WM_keymap_ensure(keyconf, "File Browser Main", SPACE_FILE, 0);
549 
550   /* keys for button region (top) */
551   WM_keymap_ensure(keyconf, "File Browser Buttons", SPACE_FILE, 0);
552 }
553 
file_tools_region_init(wmWindowManager * wm,ARegion * region)554 static void file_tools_region_init(wmWindowManager *wm, ARegion *region)
555 {
556   wmKeyMap *keymap;
557 
558   region->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_VERTICAL_HIDE;
559   ED_region_panels_init(wm, region);
560 
561   /* own keymaps */
562   keymap = WM_keymap_ensure(wm->defaultconf, "File Browser", SPACE_FILE, 0);
563   WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
564 }
565 
file_tools_region_draw(const bContext * C,ARegion * region)566 static void file_tools_region_draw(const bContext *C, ARegion *region)
567 {
568   ED_region_panels(C, region);
569 }
570 
file_tools_region_listener(wmWindow * UNUSED (win),ScrArea * UNUSED (area),ARegion * UNUSED (region),wmNotifier * UNUSED (wmn),const Scene * UNUSED (scene))571 static void file_tools_region_listener(wmWindow *UNUSED(win),
572                                        ScrArea *UNUSED(area),
573                                        ARegion *UNUSED(region),
574                                        wmNotifier *UNUSED(wmn),
575                                        const Scene *UNUSED(scene))
576 {
577 #if 0
578   /* context changes */
579   switch (wmn->category) {
580     /* pass */
581   }
582 #endif
583 }
584 
585 /* add handlers, stuff you only do once or on area/region changes */
file_header_region_init(wmWindowManager * wm,ARegion * region)586 static void file_header_region_init(wmWindowManager *wm, ARegion *region)
587 {
588   wmKeyMap *keymap;
589 
590   ED_region_header_init(region);
591 
592   keymap = WM_keymap_ensure(wm->defaultconf, "File Browser", SPACE_FILE, 0);
593   WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
594 }
595 
file_header_region_draw(const bContext * C,ARegion * region)596 static void file_header_region_draw(const bContext *C, ARegion *region)
597 {
598   ED_region_header(C, region);
599 }
600 
601 /* add handlers, stuff you only do once or on area/region changes */
file_ui_region_init(wmWindowManager * wm,ARegion * region)602 static void file_ui_region_init(wmWindowManager *wm, ARegion *region)
603 {
604   wmKeyMap *keymap;
605 
606   ED_region_panels_init(wm, region);
607   region->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y;
608 
609   /* own keymap */
610   keymap = WM_keymap_ensure(wm->defaultconf, "File Browser", SPACE_FILE, 0);
611   WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
612 
613   keymap = WM_keymap_ensure(wm->defaultconf, "File Browser Buttons", SPACE_FILE, 0);
614   WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
615 }
616 
file_ui_region_draw(const bContext * C,ARegion * region)617 static void file_ui_region_draw(const bContext *C, ARegion *region)
618 {
619   ED_region_panels(C, region);
620 }
621 
file_execution_region_init(wmWindowManager * wm,ARegion * region)622 static void file_execution_region_init(wmWindowManager *wm, ARegion *region)
623 {
624   wmKeyMap *keymap;
625 
626   ED_region_panels_init(wm, region);
627   region->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y;
628 
629   /* own keymap */
630   keymap = WM_keymap_ensure(wm->defaultconf, "File Browser", SPACE_FILE, 0);
631   WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
632 }
633 
file_execution_region_draw(const bContext * C,ARegion * region)634 static void file_execution_region_draw(const bContext *C, ARegion *region)
635 {
636   ED_region_panels(C, region);
637 }
638 
file_ui_region_listener(wmWindow * UNUSED (win),ScrArea * UNUSED (area),ARegion * region,wmNotifier * wmn,const Scene * UNUSED (scene))639 static void file_ui_region_listener(wmWindow *UNUSED(win),
640                                     ScrArea *UNUSED(area),
641                                     ARegion *region,
642                                     wmNotifier *wmn,
643                                     const Scene *UNUSED(scene))
644 {
645   /* context changes */
646   switch (wmn->category) {
647     case NC_SPACE:
648       switch (wmn->data) {
649         case ND_SPACE_FILE_LIST:
650           ED_region_tag_redraw(region);
651           break;
652       }
653       break;
654   }
655 }
656 
filepath_drop_poll(bContext * C,wmDrag * drag,const wmEvent * UNUSED (event),const char ** UNUSED (r_tooltip))657 static bool filepath_drop_poll(bContext *C,
658                                wmDrag *drag,
659                                const wmEvent *UNUSED(event),
660                                const char **UNUSED(r_tooltip))
661 {
662   if (drag->type == WM_DRAG_PATH) {
663     SpaceFile *sfile = CTX_wm_space_file(C);
664     if (sfile) {
665       return true;
666     }
667   }
668   return false;
669 }
670 
filepath_drop_copy(wmDrag * drag,wmDropBox * drop)671 static void filepath_drop_copy(wmDrag *drag, wmDropBox *drop)
672 {
673   RNA_string_set(drop->ptr, "filepath", drag->path);
674 }
675 
676 /* region dropbox definition */
file_dropboxes(void)677 static void file_dropboxes(void)
678 {
679   ListBase *lb = WM_dropboxmap_find("Window", SPACE_EMPTY, RGN_TYPE_WINDOW);
680 
681   WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy);
682 }
683 
684 /* only called once, from space/spacetypes.c */
ED_spacetype_file(void)685 void ED_spacetype_file(void)
686 {
687   SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype file");
688   ARegionType *art;
689 
690   st->spaceid = SPACE_FILE;
691   strncpy(st->name, "File", BKE_ST_MAXNAME);
692 
693   st->create = file_create;
694   st->free = file_free;
695   st->init = file_init;
696   st->exit = file_exit;
697   st->duplicate = file_duplicate;
698   st->refresh = file_refresh;
699   st->listener = file_listener;
700   st->operatortypes = file_operatortypes;
701   st->keymap = file_keymap;
702   st->dropboxes = file_dropboxes;
703 
704   /* regions: main window */
705   art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
706   art->regionid = RGN_TYPE_WINDOW;
707   art->init = file_main_region_init;
708   art->draw = file_main_region_draw;
709   art->listener = file_main_region_listener;
710   art->message_subscribe = file_main_region_message_subscribe;
711   art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
712   BLI_addhead(&st->regiontypes, art);
713 
714   /* regions: header */
715   art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
716   art->regionid = RGN_TYPE_HEADER;
717   art->prefsizey = HEADERY;
718   art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
719   art->init = file_header_region_init;
720   art->draw = file_header_region_draw;
721   // art->listener = file_header_region_listener;
722   BLI_addhead(&st->regiontypes, art);
723 
724   /* regions: ui */
725   art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
726   art->regionid = RGN_TYPE_UI;
727   art->keymapflag = ED_KEYMAP_UI;
728   art->listener = file_ui_region_listener;
729   art->init = file_ui_region_init;
730   art->draw = file_ui_region_draw;
731   BLI_addhead(&st->regiontypes, art);
732 
733   /* regions: execution */
734   art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
735   art->regionid = RGN_TYPE_EXECUTE;
736   art->keymapflag = ED_KEYMAP_UI;
737   art->listener = file_ui_region_listener;
738   art->init = file_execution_region_init;
739   art->draw = file_execution_region_draw;
740   BLI_addhead(&st->regiontypes, art);
741   file_execute_region_panels_register(art);
742 
743   /* regions: channels (directories) */
744   art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
745   art->regionid = RGN_TYPE_TOOLS;
746   art->prefsizex = 240;
747   art->prefsizey = 60;
748   art->keymapflag = ED_KEYMAP_UI;
749   art->listener = file_tools_region_listener;
750   art->init = file_tools_region_init;
751   art->draw = file_tools_region_draw;
752   BLI_addhead(&st->regiontypes, art);
753 
754   /* regions: tool properties */
755   art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region");
756   art->regionid = RGN_TYPE_TOOL_PROPS;
757   art->prefsizex = 240;
758   art->prefsizey = 60;
759   art->keymapflag = ED_KEYMAP_UI;
760   art->listener = file_tools_region_listener;
761   art->init = file_tools_region_init;
762   art->draw = file_tools_region_draw;
763   BLI_addhead(&st->regiontypes, art);
764   file_tool_props_region_panels_register(art);
765 
766   BKE_spacetype_register(st);
767 }
768 
ED_file_init(void)769 void ED_file_init(void)
770 {
771   ED_file_read_bookmarks();
772 
773   if (G.background == false) {
774     filelist_init_icons();
775   }
776 
777   IMB_thumb_makedirs();
778 }
779 
ED_file_exit(void)780 void ED_file_exit(void)
781 {
782   fsmenu_free();
783 
784   if (G.background == false) {
785     filelist_free_icons();
786   }
787 }
788 
ED_file_read_bookmarks(void)789 void ED_file_read_bookmarks(void)
790 {
791   const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
792 
793   fsmenu_free();
794 
795   fsmenu_read_system(ED_fsmenu_get(), true);
796 
797   if (cfgdir) {
798     char name[FILE_MAX];
799     BLI_join_dirfile(name, sizeof(name), cfgdir, BLENDER_BOOKMARK_FILE);
800     fsmenu_read_bookmarks(ED_fsmenu_get(), name);
801   }
802 }
803