Fix/Cleanup: I18N: Bad usage of IFACE_ instead of TIP_.
[blender.git] / source / blender / windowmanager / intern / wm_files.c
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) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup wm
22  *
23  * User level access for blend file read/write, file-history and user-preferences
24  * (including relevant operators).
25  */
26
27 /* placed up here because of crappy
28  * winsock stuff.
29  */
30 #include <stddef.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include "zlib.h" /* wm_read_exotic() */
35
36 #ifdef WIN32
37 /* Need to include windows.h so _WIN32_IE is defined. */
38 #  include <windows.h>
39 #  ifndef _WIN32_IE
40 /* Minimal requirements for SHGetSpecialFolderPath on MINGW MSVC has this defined already. */
41 #    define _WIN32_IE 0x0400
42 #  endif
43 /* For SHGetSpecialFolderPath, has to be done before BLI_winstuff
44  * because 'near' is disabled through BLI_windstuff */
45 #  include <shlobj.h>
46 #  include "BLI_winstuff.h"
47 #endif
48
49 #include "MEM_guardedalloc.h"
50 #include "MEM_CacheLimiterC-Api.h"
51
52 #include "BLI_blenlib.h"
53 #include "BLI_linklist.h"
54 #include "BLI_utildefines.h"
55 #include "BLI_threads.h"
56 #include "BLI_callbacks.h"
57 #include "BLI_system.h"
58 #include BLI_SYSTEM_PID_H
59
60 #include "BLT_translation.h"
61
62 #include "BLF_api.h"
63
64 #include "DNA_object_types.h"
65 #include "DNA_space_types.h"
66 #include "DNA_userdef_types.h"
67 #include "DNA_scene_types.h"
68 #include "DNA_screen_types.h"
69 #include "DNA_windowmanager_types.h"
70 #include "DNA_workspace_types.h"
71
72 #include "BKE_appdir.h"
73 #include "BKE_autoexec.h"
74 #include "BKE_blender.h"
75 #include "BKE_blendfile.h"
76 #include "BKE_blender_undo.h"
77 #include "BKE_context.h"
78 #include "BKE_global.h"
79 #include "BKE_idprop.h"
80 #include "BKE_main.h"
81 #include "BKE_packedFile.h"
82 #include "BKE_report.h"
83 #include "BKE_sound.h"
84 #include "BKE_scene.h"
85 #include "BKE_screen.h"
86 #include "BKE_undo_system.h"
87 #include "BKE_workspace.h"
88
89 #include "BLO_readfile.h"
90 #include "BLO_writefile.h"
91 #include "BLO_undofile.h" /* to save from an undo memfile */
92
93 #include "RNA_access.h"
94 #include "RNA_define.h"
95
96 #include "IMB_imbuf.h"
97 #include "IMB_imbuf_types.h"
98 #include "IMB_thumbs.h"
99
100 #include "ED_datafiles.h"
101 #include "ED_fileselect.h"
102 #include "ED_image.h"
103 #include "ED_screen.h"
104 #include "ED_view3d.h"
105 #include "ED_util.h"
106 #include "ED_undo.h"
107
108 #include "GHOST_C-api.h"
109 #include "GHOST_Path-api.h"
110
111 #include "UI_interface.h"
112 #include "UI_resources.h"
113 #include "UI_view2d.h"
114
115 #include "GPU_draw.h"
116
117 /* only to report a missing engine */
118 #include "RE_engine.h"
119
120 #ifdef WITH_PYTHON
121 #  include "BPY_extern.h"
122 #endif
123
124 #include "DEG_depsgraph.h"
125
126 #include "WM_api.h"
127 #include "WM_types.h"
128 #include "WM_message.h"
129 #include "WM_toolsystem.h"
130
131 #include "wm.h"
132 #include "wm_files.h"
133 #include "wm_window.h"
134 #include "wm_event_system.h"
135
136 static RecentFile *wm_file_history_find(const char *filepath);
137 static void wm_history_file_free(RecentFile *recent);
138 static void wm_history_file_update(void);
139 static void wm_history_file_write(void);
140
141 /* To be able to read files without windows closing, opening, moving
142  * we try to prepare for worst case:
143  * - active window gets active screen from file
144  * - restoring the screens from non-active windows
145  * Best case is all screens match, in that case they get assigned to proper window
146  */
147 static void wm_window_match_init(bContext *C, ListBase *wmlist)
148 {
149   wmWindowManager *wm;
150   wmWindow *win, *active_win;
151
152   *wmlist = G_MAIN->wm;
153   BLI_listbase_clear(&G_MAIN->wm);
154
155   active_win = CTX_wm_window(C);
156
157   /* first wrap up running stuff */
158   /* code copied from wm_init_exit.c */
159   for (wm = wmlist->first; wm; wm = wm->id.next) {
160
161     WM_jobs_kill_all(wm);
162
163     for (win = wm->windows.first; win; win = win->next) {
164
165       CTX_wm_window_set(C, win); /* needed by operator close callbacks */
166       WM_event_remove_handlers(C, &win->handlers);
167       WM_event_remove_handlers(C, &win->modalhandlers);
168       ED_screen_exit(C, win, WM_window_get_active_screen(win));
169     }
170   }
171
172   /* reset active window */
173   CTX_wm_window_set(C, active_win);
174
175   /* XXX Hack! We have to clear context menu here, because removing all modalhandlers
176    * above frees the active menu (at least, in the 'startup splash' case),
177    * causing use-after-free error in later handling of the button callbacks in UI code
178    * (see ui_apply_but_funcs_after()).
179    * Tried solving this by always NULL-ing context's menu when setting wm/win/etc.,
180    * but it broke popups refreshing (see T47632),
181    * so for now just handling this specific case here. */
182   CTX_wm_menu_set(C, NULL);
183
184   ED_editors_exit(G_MAIN, true);
185 }
186
187 static void wm_window_substitute_old(wmWindowManager *oldwm,
188                                      wmWindowManager *wm,
189                                      wmWindow *oldwin,
190                                      wmWindow *win)
191 {
192   win->ghostwin = oldwin->ghostwin;
193   win->gpuctx = oldwin->gpuctx;
194   win->active = oldwin->active;
195   if (win->active) {
196     wm->winactive = win;
197   }
198   if (oldwm->windrawable == oldwin) {
199     oldwm->windrawable = NULL;
200     wm->windrawable = win;
201   }
202
203   /* File loading in background mode still calls this. */
204   if (!G.background) {
205     /* Pointer back. */
206     GHOST_SetWindowUserData(win->ghostwin, win);
207   }
208
209   oldwin->ghostwin = NULL;
210   oldwin->gpuctx = NULL;
211
212   win->eventstate = oldwin->eventstate;
213   oldwin->eventstate = NULL;
214
215   /* ensure proper screen rescaling */
216   win->sizex = oldwin->sizex;
217   win->sizey = oldwin->sizey;
218   win->posx = oldwin->posx;
219   win->posy = oldwin->posy;
220 }
221
222 static void wm_window_match_keep_current_wm(const bContext *C,
223                                             ListBase *current_wm_list,
224                                             const bool load_ui,
225                                             ListBase *r_new_wm_list)
226 {
227   Main *bmain = CTX_data_main(C);
228   wmWindowManager *wm = current_wm_list->first;
229   bScreen *screen = NULL;
230
231   /* match oldwm to new dbase, only old files */
232   wm->initialized &= ~WM_WINDOW_IS_INITIALIZED;
233
234   /* when loading without UI, no matching needed */
235   if (load_ui && (screen = CTX_wm_screen(C))) {
236     for (wmWindow *win = wm->windows.first; win; win = win->next) {
237       WorkSpace *workspace;
238
239       BKE_workspace_layout_find_global(bmain, screen, &workspace);
240       BKE_workspace_active_set(win->workspace_hook, workspace);
241       win->scene = CTX_data_scene(C);
242
243       /* all windows get active screen from file */
244       if (screen->winid == 0) {
245         WM_window_set_active_screen(win, workspace, screen);
246       }
247       else {
248         WorkSpaceLayout *layout_old = WM_window_get_active_layout(win);
249         WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(
250             bmain, workspace, layout_old, win);
251
252         WM_window_set_active_layout(win, workspace, layout_new);
253       }
254
255       bScreen *win_screen = WM_window_get_active_screen(win);
256       win_screen->winid = win->winid;
257     }
258   }
259
260   *r_new_wm_list = *current_wm_list;
261 }
262
263 static void wm_window_match_replace_by_file_wm(bContext *C,
264                                                ListBase *current_wm_list,
265                                                ListBase *readfile_wm_list,
266                                                ListBase *r_new_wm_list)
267 {
268   wmWindowManager *oldwm = current_wm_list->first;
269   wmWindowManager *wm = readfile_wm_list->first; /* will become our new WM */
270   bool has_match = false;
271
272   /* this code could move to setup_appdata */
273
274   /* preserve key configurations in new wm, to preserve their keymaps */
275   wm->keyconfigs = oldwm->keyconfigs;
276   wm->addonconf = oldwm->addonconf;
277   wm->defaultconf = oldwm->defaultconf;
278   wm->userconf = oldwm->userconf;
279
280   BLI_listbase_clear(&oldwm->keyconfigs);
281   oldwm->addonconf = NULL;
282   oldwm->defaultconf = NULL;
283   oldwm->userconf = NULL;
284
285   /* ensure making new keymaps and set space types */
286   wm->initialized = 0;
287   wm->winactive = NULL;
288
289   /* Clearing drawable of before deleting any context
290    * to avoid clearing the wrong wm. */
291   wm_window_clear_drawable(oldwm);
292
293   /* only first wm in list has ghostwins */
294   for (wmWindow *win = wm->windows.first; win; win = win->next) {
295     for (wmWindow *oldwin = oldwm->windows.first; oldwin; oldwin = oldwin->next) {
296       if (oldwin->winid == win->winid) {
297         has_match = true;
298
299         wm_window_substitute_old(oldwm, wm, oldwin, win);
300       }
301     }
302   }
303   /* make sure at least one window is kept open so we don't lose the context, check T42303 */
304   if (!has_match) {
305     wm_window_substitute_old(oldwm, wm, oldwm->windows.first, wm->windows.first);
306   }
307
308   wm_close_and_free_all(C, current_wm_list);
309
310   *r_new_wm_list = *readfile_wm_list;
311 }
312
313 /**
314  * Match old WM with new, 4 cases:
315  * 1) No current WM, no WM in file: Make new default.
316  * 2) No current WM, but WM in file: Keep current WM, do nothing else.
317  * 3) Current WM, but not in file: Keep current WM, update windows with screens from file.
318  * 4) Current WM, and WM in file: Try to keep current GHOST windows, use WM from file.
319  *
320  * \param r_new_wm_list: Return argument for the wm list to be used from now on.
321  */
322 static void wm_window_match_do(bContext *C,
323                                ListBase *current_wm_list,
324                                ListBase *readfile_wm_list,
325                                ListBase *r_new_wm_list)
326 {
327   if (BLI_listbase_is_empty(current_wm_list)) {
328     /* case 1 */
329     if (BLI_listbase_is_empty(readfile_wm_list)) {
330       Main *bmain = CTX_data_main(C);
331       /* Neither current, no newly read file have a WM -> add the default one. */
332       wm_add_default(bmain, C);
333       *r_new_wm_list = bmain->wm;
334     }
335     /* case 2 */
336     else {
337       *r_new_wm_list = *readfile_wm_list;
338     }
339   }
340   else {
341     /* case 3 */
342     if (BLI_listbase_is_empty(readfile_wm_list)) {
343       /* We've read file without wm, keep current one entirely alive.
344        * Happens when reading pre 2.5 files (no WM back then) */
345       wm_window_match_keep_current_wm(
346           C, current_wm_list, (G.fileflags & G_FILE_NO_UI) == 0, r_new_wm_list);
347     }
348     /* case 4 */
349     else {
350       wm_window_match_replace_by_file_wm(C, current_wm_list, readfile_wm_list, r_new_wm_list);
351     }
352   }
353 }
354
355 /* in case UserDef was read, we re-initialize all, and do versioning */
356 static void wm_init_userdef(Main *bmain, const bool read_userdef_from_memory)
357 {
358   /* versioning is here */
359   UI_init_userdef(bmain);
360
361   /* needed so loading a file from the command line respects user-pref [#26156] */
362   SET_FLAG_FROM_TEST(G.fileflags, U.flag & USER_FILENOUI, G_FILE_NO_UI);
363
364   /* set the python auto-execute setting from user prefs */
365   /* enabled by default, unless explicitly enabled in the command line which overrides */
366   if ((G.f & G_FLAG_SCRIPT_OVERRIDE_PREF) == 0) {
367     SET_FLAG_FROM_TEST(G.f, (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0, G_FLAG_SCRIPT_AUTOEXEC);
368   }
369
370   /* avoid re-saving for every small change to our prefs, allow overrides */
371   if (read_userdef_from_memory) {
372     BLO_update_defaults_userpref_blend();
373   }
374
375   MEM_CacheLimiter_set_maximum(((size_t)U.memcachelimit) * 1024 * 1024);
376   BKE_sound_init(bmain);
377
378   /* update tempdir from user preferences */
379   BKE_tempdir_init(U.tempdir);
380 }
381
382 /* return codes */
383 #define BKE_READ_EXOTIC_FAIL_PATH -3   /* file format is not supported */
384 #define BKE_READ_EXOTIC_FAIL_FORMAT -2 /* file format is not supported */
385 #define BKE_READ_EXOTIC_FAIL_OPEN -1   /* Can't open the file */
386 #define BKE_READ_EXOTIC_OK_BLEND 0     /* .blend file */
387 #if 0
388 #  define BKE_READ_EXOTIC_OK_OTHER 1 /* other supported formats */
389 #endif
390
391 /* intended to check for non-blender formats but for now it only reads blends */
392 static int wm_read_exotic(const char *name)
393 {
394   int len;
395   gzFile gzfile;
396   char header[7];
397   int retval;
398
399   /* make sure we're not trying to read a directory.... */
400
401   len = strlen(name);
402   if (len > 0 && ELEM(name[len - 1], '/', '\\')) {
403     retval = BKE_READ_EXOTIC_FAIL_PATH;
404   }
405   else {
406     gzfile = BLI_gzopen(name, "rb");
407     if (gzfile == NULL) {
408       retval = BKE_READ_EXOTIC_FAIL_OPEN;
409     }
410     else {
411       len = gzread(gzfile, header, sizeof(header));
412       gzclose(gzfile);
413       if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) {
414         retval = BKE_READ_EXOTIC_OK_BLEND;
415       }
416       else {
417         /* We may want to support loading other file formats
418          * from their header bytes or file extension.
419          * This used to be supported in the code below and may be added
420          * back at some point. */
421 #if 0
422         WM_cursor_wait(true);
423
424         if (is_foo_format(name)) {
425           read_foo(name);
426           retval = BKE_READ_EXOTIC_OK_OTHER;
427         }
428         else
429 #endif
430         {
431           retval = BKE_READ_EXOTIC_FAIL_FORMAT;
432         }
433 #if 0
434         WM_cursor_wait(false);
435 #endif
436       }
437     }
438   }
439
440   return retval;
441 }
442
443 void WM_file_autoexec_init(const char *filepath)
444 {
445   if (G.f & G_FLAG_SCRIPT_OVERRIDE_PREF) {
446     return;
447   }
448
449   if (G.f & G_FLAG_SCRIPT_AUTOEXEC) {
450     char path[FILE_MAX];
451     BLI_split_dir_part(filepath, path, sizeof(path));
452     if (BKE_autoexec_match(path)) {
453       G.f &= ~G_FLAG_SCRIPT_AUTOEXEC;
454     }
455   }
456 }
457
458 void wm_file_read_report(bContext *C, Main *bmain)
459 {
460   ReportList *reports = NULL;
461   Scene *sce;
462
463   for (sce = bmain->scenes.first; sce; sce = sce->id.next) {
464     if (sce->r.engine[0] &&
465         BLI_findstring(&R_engines, sce->r.engine, offsetof(RenderEngineType, idname)) == NULL) {
466       if (reports == NULL) {
467         reports = CTX_wm_reports(C);
468       }
469
470       BKE_reportf(reports,
471                   RPT_ERROR,
472                   "Engine '%s' not available for scene '%s' (an add-on may need to be installed "
473                   "or enabled)",
474                   sce->r.engine,
475                   sce->id.name + 2);
476     }
477   }
478
479   if (reports) {
480     if (!G.background) {
481       WM_report_banner_show();
482     }
483   }
484 }
485
486 /**
487  * Logic shared between #WM_file_read & #wm_homefile_read,
488  * updates to make after reading a file.
489  */
490 static void wm_file_read_post(bContext *C,
491                               const bool is_startup_file,
492                               const bool is_factory_startup,
493                               const bool use_data,
494                               const bool use_userdef,
495                               const bool reset_app_template)
496 {
497   bool addons_loaded = false;
498   wmWindowManager *wm = CTX_wm_manager(C);
499
500   if (use_data) {
501     if (!G.background) {
502       /* remove windows which failed to be added via WM_check */
503       wm_window_ghostwindows_remove_invalid(C, wm);
504     }
505     CTX_wm_window_set(C, wm->windows.first);
506   }
507
508 #ifdef WITH_PYTHON
509   if (is_startup_file) {
510     /* possible python hasn't been initialized */
511     if (CTX_py_init_get(C)) {
512       if (reset_app_template) {
513         /* Only run when we have a template path found. */
514         if (BKE_appdir_app_template_any()) {
515           BPY_execute_string(
516               C, (const char *[]){"bl_app_template_utils", NULL}, "bl_app_template_utils.reset()");
517         }
518         /* sync addons, these may have changed from the defaults */
519         BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()");
520       }
521       if (use_data) {
522         BPY_python_reset(C);
523       }
524       addons_loaded = true;
525     }
526   }
527   else {
528     /* run any texts that were loaded in and flagged as modules */
529     if (use_data) {
530       BPY_python_reset(C);
531     }
532     addons_loaded = true;
533   }
534 #else
535   UNUSED_VARS(is_startup_file, reset_app_template);
536 #endif /* WITH_PYTHON */
537
538   Main *bmain = CTX_data_main(C);
539
540   if (use_userdef) {
541     if (is_factory_startup) {
542       BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_FACTORY_USERDEF_POST);
543     }
544   }
545
546   if (use_data) {
547     /* important to do before NULL'ing the context */
548     BLI_callback_exec(bmain, NULL, BLI_CB_EVT_VERSION_UPDATE);
549     BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_POST);
550     if (is_factory_startup) {
551       BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_FACTORY_STARTUP_POST);
552     }
553   }
554
555   if (use_data) {
556     WM_operatortype_last_properties_clear_all();
557
558     /* After load post, so for example the driver namespace can be filled
559      * before evaluating the depsgraph. */
560     wm_event_do_depsgraph(C, true);
561
562     ED_editors_init(C);
563
564 #if 1
565     WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL);
566 #else
567     WM_msg_publish_static(CTX_wm_message_bus(C), WM_MSG_STATICTYPE_FILE_READ);
568 #endif
569   }
570
571   /* report any errors.
572    * currently disabled if addons aren't yet loaded */
573   if (addons_loaded) {
574     wm_file_read_report(C, bmain);
575   }
576
577   if (use_data) {
578     if (!G.background) {
579       if (wm->undo_stack == NULL) {
580         wm->undo_stack = BKE_undosys_stack_create();
581       }
582       else {
583         BKE_undosys_stack_clear(wm->undo_stack);
584       }
585       BKE_undosys_stack_init_from_main(wm->undo_stack, bmain);
586       BKE_undosys_stack_init_from_context(wm->undo_stack, C);
587     }
588   }
589
590   if (use_data) {
591     if (!G.background) {
592       /* in background mode this makes it hard to load
593        * a blend file and do anything since the screen
594        * won't be set to a valid value again */
595       CTX_wm_window_set(C, NULL); /* exits queues */
596
597       /* Ensure tools are registered. */
598       WM_toolsystem_init(C);
599     }
600   }
601 }
602
603 bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
604 {
605   /* assume automated tasks with background, don't write recent file list */
606   const bool do_history = (G.background == false) && (CTX_wm_manager(C)->op_undo_depth == 0);
607   bool success = false;
608   int retval;
609
610   /* so we can get the error message */
611   errno = 0;
612
613   WM_cursor_wait(1);
614
615   BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_PRE);
616
617   UI_view2d_zoom_cache_reset();
618
619   /* first try to append data from exotic file formats... */
620   /* it throws error box when file doesn't exist and returns -1 */
621   /* note; it should set some error message somewhere... (ton) */
622   retval = wm_read_exotic(filepath);
623
624   /* we didn't succeed, now try to read Blender file */
625   if (retval == BKE_READ_EXOTIC_OK_BLEND) {
626     bool use_data = true;
627     bool use_userdef = false;
628     const int G_f_orig = G.f;
629     ListBase wmbase;
630
631     /* put aside screens to match with persistent windows later */
632     /* also exit screens and editors */
633     wm_window_match_init(C, &wmbase);
634
635     /* confusing this global... */
636     G.relbase_valid = 1;
637     retval = BKE_blendfile_read(C, filepath, &(const struct BlendFileReadParams){0}, reports);
638
639     /* BKE_file_read sets new Main into context. */
640     Main *bmain = CTX_data_main(C);
641
642     /* when loading startup.blend's, we can be left with a blank path */
643     if (BKE_main_blendfile_path(bmain)[0] != '\0') {
644       G.save_over = 1;
645     }
646     else {
647       G.save_over = 0;
648       G.relbase_valid = 0;
649     }
650
651     /* this flag is initialized by the operator but overwritten on read.
652      * need to re-enable it here else drivers + registered scripts wont work. */
653     if (G.f != G_f_orig) {
654       const int flags_keep = G_FLAG_ALL_RUNTIME;
655       G.f &= G_FLAG_ALL_READFILE;
656       G.f = (G.f & ~flags_keep) | (G_f_orig & flags_keep);
657     }
658
659     /* match the read WM with current WM */
660     wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm);
661     WM_check(C); /* opens window(s), checks keymaps */
662
663     if (retval == BKE_BLENDFILE_READ_OK_USERPREFS) {
664       /* in case a userdef is read from regular .blend */
665       wm_init_userdef(bmain, false);
666       use_userdef = true;
667     }
668
669     if (retval != BKE_BLENDFILE_READ_FAIL) {
670       if (do_history) {
671         wm_history_file_update();
672       }
673     }
674
675     wm_file_read_post(C, false, false, use_data, use_userdef, false);
676
677     success = true;
678   }
679 #if 0
680   else if (retval == BKE_READ_EXOTIC_OK_OTHER) {
681     BKE_undo_write(C, "Import file");
682   }
683 #endif
684   else if (retval == BKE_READ_EXOTIC_FAIL_OPEN) {
685     BKE_reportf(reports,
686                 RPT_ERROR,
687                 "Cannot read file '%s': %s",
688                 filepath,
689                 errno ? strerror(errno) : TIP_("unable to open the file"));
690   }
691   else if (retval == BKE_READ_EXOTIC_FAIL_FORMAT) {
692     BKE_reportf(reports, RPT_ERROR, "File format is not supported in file '%s'", filepath);
693   }
694   else if (retval == BKE_READ_EXOTIC_FAIL_PATH) {
695     BKE_reportf(reports, RPT_ERROR, "File path '%s' invalid", filepath);
696   }
697   else {
698     BKE_reportf(reports, RPT_ERROR, "Unknown error loading '%s'", filepath);
699     BLI_assert(!"invalid 'retval'");
700   }
701
702   if (success == false) {
703     /* remove from recent files list */
704     if (do_history) {
705       RecentFile *recent = wm_file_history_find(filepath);
706       if (recent) {
707         wm_history_file_free(recent);
708         wm_history_file_write();
709       }
710     }
711   }
712
713   WM_cursor_wait(0);
714
715   return success;
716 }
717
718 static struct {
719   char app_template[64];
720   bool override;
721 } wm_init_state_app_template = {{0}};
722
723 /**
724  * Used for setting app-template from the command line:
725  * - non-empty string: overrides.
726  * - empty string: override, using no app template.
727  * - NULL: clears override.
728  */
729 void WM_init_state_app_template_set(const char *app_template)
730 {
731   if (app_template) {
732     STRNCPY(wm_init_state_app_template.app_template, app_template);
733     wm_init_state_app_template.override = true;
734   }
735   else {
736     wm_init_state_app_template.app_template[0] = '\0';
737     wm_init_state_app_template.override = false;
738   }
739 }
740
741 const char *WM_init_state_app_template_get(void)
742 {
743   return wm_init_state_app_template.override ? wm_init_state_app_template.app_template : NULL;
744 }
745
746 /**
747  * Called on startup, (context entirely filled with NULLs)
748  * or called for 'New File' both startup.blend and userpref.blend are checked.
749  *
750  * \param use_factory_settings:
751  * Ignore on-disk startup file, use bundled ``datatoc_startup_blend`` instead.
752  * Used for "Restore Factory Settings".
753  *
754  * \param use_userdef: Load factory settings as well as startup file.
755  * Disabled for "File New" we don't want to reload preferences.
756  *
757  * \param filepath_startup_override:
758  * Optional path pointing to an alternative blend file (may be NULL).
759  *
760  * \param app_template_override:
761  * Template to use instead of the template defined in user-preferences.
762  * When not-null, this is written into the user preferences.
763  */
764 void wm_homefile_read(bContext *C,
765                       ReportList *reports,
766                       bool use_factory_settings,
767                       bool use_empty_data,
768                       bool use_data,
769                       bool use_userdef,
770                       const char *filepath_startup_override,
771                       const char *app_template_override,
772                       bool *r_is_factory_startup)
773 {
774   Main *bmain = G_MAIN; /* Context does not always have valid main pointer here... */
775   ListBase wmbase;
776   bool success = false;
777
778   bool filepath_startup_is_factory = true;
779   char filepath_startup[FILE_MAX];
780   char filepath_userdef[FILE_MAX];
781
782   /* When 'app_template' is set:
783    * '{BLENDER_USER_CONFIG}/{app_template}' */
784   char app_template_system[FILE_MAX];
785   /* When 'app_template' is set:
786    * '{BLENDER_SYSTEM_SCRIPTS}/startup/bl_app_templates_system/{app_template}' */
787   char app_template_config[FILE_MAX];
788
789   /* Indicates whether user preferences were really load from memory.
790    *
791    * This is used for versioning code, and for this we can not rely on use_factory_settings
792    * passed via argument. This is because there might be configuration folder
793    * exists but it might not have userpref.blend and in this case we fallback to
794    * reading home file from memory.
795    *
796    * And in this case versioning code is to be run.
797    */
798   bool read_userdef_from_memory = false;
799   eBLOReadSkip skip_flags = 0;
800
801   if (use_data == false) {
802     skip_flags |= BLO_READ_SKIP_DATA;
803   }
804   if (use_userdef == false) {
805     skip_flags |= BLO_READ_SKIP_USERDEF;
806   }
807
808   /* True if we load startup.blend from memory
809    * or use app-template startup.blend which the user hasn't saved. */
810   bool is_factory_startup = true;
811
812   /* options exclude eachother */
813   BLI_assert((use_factory_settings && filepath_startup_override) == 0);
814
815   if ((G.f & G_FLAG_SCRIPT_OVERRIDE_PREF) == 0) {
816     SET_FLAG_FROM_TEST(G.f, (U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0, G_FLAG_SCRIPT_AUTOEXEC);
817   }
818
819   if (use_data) {
820     BLI_callback_exec(CTX_data_main(C), NULL, BLI_CB_EVT_LOAD_PRE);
821
822     G.relbase_valid = 0;
823
824     /* put aside screens to match with persistent windows later */
825     wm_window_match_init(C, &wmbase);
826   }
827
828   UI_view2d_zoom_cache_reset();
829
830   filepath_startup[0] = '\0';
831   filepath_userdef[0] = '\0';
832   app_template_system[0] = '\0';
833   app_template_config[0] = '\0';
834
835   const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
836   if (!use_factory_settings) {
837     if (cfgdir) {
838       BLI_path_join(
839           filepath_startup, sizeof(filepath_startup), cfgdir, BLENDER_STARTUP_FILE, NULL);
840       filepath_startup_is_factory = false;
841       if (use_userdef) {
842         BLI_path_join(
843             filepath_userdef, sizeof(filepath_startup), cfgdir, BLENDER_USERPREF_FILE, NULL);
844       }
845     }
846     else {
847       use_factory_settings = true;
848     }
849
850     if (filepath_startup_override) {
851       BLI_strncpy(filepath_startup, filepath_startup_override, FILE_MAX);
852       filepath_startup_is_factory = false;
853     }
854   }
855
856   /* load preferences before startup.blend */
857   if (use_userdef) {
858     if (!use_factory_settings && BLI_exists(filepath_userdef)) {
859       UserDef *userdef = BKE_blendfile_userdef_read(filepath_userdef, NULL);
860       if (userdef != NULL) {
861         BKE_blender_userdef_data_set_and_free(userdef);
862         userdef = NULL;
863
864         skip_flags |= BLO_READ_SKIP_USERDEF;
865         printf("Read prefs: %s\n", filepath_userdef);
866       }
867     }
868   }
869
870   const char *app_template = NULL;
871   bool update_defaults = false;
872   bool reset_app_template = false;
873
874   if (filepath_startup_override != NULL) {
875     /* pass */
876   }
877   else if (app_template_override) {
878     /* This may be clearing the current template by setting to an empty string. */
879     app_template = app_template_override;
880   }
881   else if (!use_factory_settings && U.app_template[0]) {
882     app_template = U.app_template;
883   }
884
885   if ((!app_template && U.app_template[0]) ||
886       (app_template && !STREQ(app_template, U.app_template))) {
887     /* Always load UI when switching to another template. */
888     G.fileflags &= ~G_FILE_NO_UI;
889     reset_app_template = true;
890   }
891
892   if ((app_template != NULL) && (app_template[0] != '\0')) {
893     if (!BKE_appdir_app_template_id_search(
894             app_template, app_template_system, sizeof(app_template_system))) {
895       /* Can safely continue with code below, just warn it's not found. */
896       BKE_reportf(reports, RPT_WARNING, "Application Template '%s' not found", app_template);
897     }
898
899     /* Insert template name into startup file. */
900
901     /* note that the path is being set even when 'use_factory_settings == true'
902      * this is done so we can load a templates factory-settings */
903     if (!use_factory_settings) {
904       BLI_path_join(app_template_config, sizeof(app_template_config), cfgdir, app_template, NULL);
905       BLI_path_join(filepath_startup,
906                     sizeof(filepath_startup),
907                     app_template_config,
908                     BLENDER_STARTUP_FILE,
909                     NULL);
910       filepath_startup_is_factory = false;
911       if (BLI_access(filepath_startup, R_OK) != 0) {
912         filepath_startup[0] = '\0';
913       }
914     }
915     else {
916       filepath_startup[0] = '\0';
917     }
918
919     if (filepath_startup[0] == '\0') {
920       BLI_path_join(filepath_startup,
921                     sizeof(filepath_startup),
922                     app_template_system,
923                     BLENDER_STARTUP_FILE,
924                     NULL);
925       filepath_startup_is_factory = true;
926
927       /* Update defaults only for system templates. */
928       update_defaults = true;
929     }
930   }
931
932   if (!use_factory_settings || (filepath_startup[0] != '\0')) {
933     if (BLI_access(filepath_startup, R_OK) == 0) {
934       success = BKE_blendfile_read(C,
935                                    filepath_startup,
936                                    &(const struct BlendFileReadParams){
937                                        .is_startup = true,
938                                        .skip_flags = skip_flags,
939                                    },
940                                    NULL) != BKE_BLENDFILE_READ_FAIL;
941     }
942     if (BLI_listbase_is_empty(&U.themes)) {
943       if (G.debug & G_DEBUG) {
944         printf("\nNote: No (valid) '%s' found, fall back to built-in default.\n\n",
945                filepath_startup);
946       }
947       success = false;
948     }
949     if (success) {
950       if (update_defaults) {
951         if (use_data) {
952           BLO_update_defaults_startup_blend(CTX_data_main(C), app_template);
953         }
954       }
955       is_factory_startup = filepath_startup_is_factory;
956     }
957   }
958
959   if (success == false && filepath_startup_override && reports) {
960     /* We can not return from here because wm is already reset */
961     BKE_reportf(reports, RPT_ERROR, "Could not read '%s'", filepath_startup_override);
962   }
963
964   if (success == false) {
965     success = BKE_blendfile_read_from_memory(C,
966                                              datatoc_startup_blend,
967                                              datatoc_startup_blend_size,
968                                              true,
969                                              &(const struct BlendFileReadParams){
970                                                  .is_startup = true,
971                                                  .skip_flags = skip_flags,
972                                              },
973                                              NULL);
974     if (success) {
975       if (use_userdef) {
976         if ((skip_flags & BLO_READ_SKIP_USERDEF) == 0) {
977           read_userdef_from_memory = true;
978         }
979       }
980     }
981     if (use_data && BLI_listbase_is_empty(&wmbase)) {
982       wm_clear_default_size(C);
983     }
984   }
985
986   if (use_empty_data) {
987     BKE_blendfile_read_make_empty(C);
988   }
989
990   /* Load template preferences,
991    * unlike regular preferences we only use some of the settings,
992    * see: BKE_blender_userdef_set_app_template */
993   if (app_template_system[0] != '\0') {
994     char temp_path[FILE_MAX];
995     temp_path[0] = '\0';
996     if (!use_factory_settings) {
997       BLI_path_join(
998           temp_path, sizeof(temp_path), app_template_config, BLENDER_USERPREF_FILE, NULL);
999       if (BLI_access(temp_path, R_OK) != 0) {
1000         temp_path[0] = '\0';
1001       }
1002     }
1003
1004     if (temp_path[0] == '\0') {
1005       BLI_path_join(
1006           temp_path, sizeof(temp_path), app_template_system, BLENDER_USERPREF_FILE, NULL);
1007     }
1008
1009     if (use_userdef) {
1010       UserDef *userdef_template = NULL;
1011       /* just avoids missing file warning */
1012       if (BLI_exists(temp_path)) {
1013         userdef_template = BKE_blendfile_userdef_read(temp_path, NULL);
1014       }
1015       if (userdef_template == NULL) {
1016         /* we need to have preferences load to overwrite preferences from previous template */
1017         userdef_template = BKE_blendfile_userdef_read_from_memory(
1018             datatoc_startup_blend, datatoc_startup_blend_size, NULL);
1019         read_userdef_from_memory = true;
1020       }
1021       if (userdef_template) {
1022         BKE_blender_userdef_app_template_data_set_and_free(userdef_template);
1023         userdef_template = NULL;
1024       }
1025     }
1026   }
1027
1028   if (app_template_override) {
1029     BLI_strncpy(U.app_template, app_template_override, sizeof(U.app_template));
1030   }
1031
1032   if (use_data) {
1033     /* Prevent buggy files that had G_FILE_RELATIVE_REMAP written out by mistake.
1034      * Screws up autosaves otherwise can remove this eventually,
1035      * only in a 2.53 and older, now its not written. */
1036     G.fileflags &= ~G_FILE_RELATIVE_REMAP;
1037   }
1038
1039   bmain = CTX_data_main(C);
1040
1041   if (use_userdef) {
1042     /* check userdef before open window, keymaps etc */
1043     wm_init_userdef(bmain, read_userdef_from_memory);
1044     reset_app_template = true;
1045   }
1046
1047   if (use_data) {
1048     /* match the read WM with current WM */
1049     wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm);
1050   }
1051
1052   if (use_factory_settings) {
1053     /*  Clear keymaps because the current default keymap may have been initialized
1054      *  from user preferences, which have been reset. */
1055     for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
1056       if (wm->defaultconf) {
1057         wm->defaultconf->flag &= ~KEYCONF_INIT_DEFAULT;
1058       }
1059     }
1060   }
1061
1062   if (use_data) {
1063     WM_check(C); /* opens window(s), checks keymaps */
1064
1065     bmain->name[0] = '\0';
1066
1067     /* start with save preference untitled.blend */
1068     G.save_over = 0;
1069   }
1070
1071   wm_file_read_post(C, true, is_factory_startup, use_data, use_userdef, reset_app_template);
1072
1073   if (r_is_factory_startup) {
1074     *r_is_factory_startup = is_factory_startup;
1075   }
1076 }
1077
1078 /** \name WM History File API
1079  * \{ */
1080
1081 void wm_history_file_read(void)
1082 {
1083   char name[FILE_MAX];
1084   LinkNode *l, *lines;
1085   struct RecentFile *recent;
1086   const char *line;
1087   int num;
1088   const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
1089
1090   if (!cfgdir) {
1091     return;
1092   }
1093
1094   BLI_make_file_string("/", name, cfgdir, BLENDER_HISTORY_FILE);
1095
1096   lines = BLI_file_read_as_lines(name);
1097
1098   BLI_listbase_clear(&G.recent_files);
1099
1100   /* read list of recent opened files from recent-files.txt to memory */
1101   for (l = lines, num = 0; l && (num < U.recent_files); l = l->next) {
1102     line = l->link;
1103     /* don't check if files exist, causes slow startup for remote/external drives */
1104     if (line[0]) {
1105       recent = (RecentFile *)MEM_mallocN(sizeof(RecentFile), "RecentFile");
1106       BLI_addtail(&(G.recent_files), recent);
1107       recent->filepath = BLI_strdup(line);
1108       num++;
1109     }
1110   }
1111
1112   BLI_file_free_lines(lines);
1113 }
1114
1115 static RecentFile *wm_history_file_new(const char *filepath)
1116 {
1117   RecentFile *recent = MEM_mallocN(sizeof(RecentFile), "RecentFile");
1118   recent->filepath = BLI_strdup(filepath);
1119   return recent;
1120 }
1121
1122 static void wm_history_file_free(RecentFile *recent)
1123 {
1124   BLI_assert(BLI_findindex(&G.recent_files, recent) != -1);
1125   MEM_freeN(recent->filepath);
1126   BLI_freelinkN(&G.recent_files, recent);
1127 }
1128
1129 static RecentFile *wm_file_history_find(const char *filepath)
1130 {
1131   return BLI_findstring_ptr(&G.recent_files, filepath, offsetof(RecentFile, filepath));
1132 }
1133
1134 /**
1135  * Write #BLENDER_HISTORY_FILE as-is, without checking the environment
1136  * (that's handled by #wm_history_file_update).
1137  */
1138 static void wm_history_file_write(void)
1139 {
1140   const char *user_config_dir;
1141   char name[FILE_MAX];
1142   FILE *fp;
1143
1144   /* will be NULL in background mode */
1145   user_config_dir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL);
1146   if (!user_config_dir) {
1147     return;
1148   }
1149
1150   BLI_make_file_string("/", name, user_config_dir, BLENDER_HISTORY_FILE);
1151
1152   fp = BLI_fopen(name, "w");
1153   if (fp) {
1154     struct RecentFile *recent;
1155     for (recent = G.recent_files.first; recent; recent = recent->next) {
1156       fprintf(fp, "%s\n", recent->filepath);
1157     }
1158     fclose(fp);
1159   }
1160 }
1161
1162 /**
1163  * Run after saving a file to refresh the #BLENDER_HISTORY_FILE list.
1164  */
1165 static void wm_history_file_update(void)
1166 {
1167   RecentFile *recent;
1168   const char *blendfile_name = BKE_main_blendfile_path_from_global();
1169
1170   /* no write history for recovered startup files */
1171   if (blendfile_name[0] == '\0') {
1172     return;
1173   }
1174
1175   recent = G.recent_files.first;
1176   /* refresh recent-files.txt of recent opened files, when current file was changed */
1177   if (!(recent) || (BLI_path_cmp(recent->filepath, blendfile_name) != 0)) {
1178
1179     recent = wm_file_history_find(blendfile_name);
1180     if (recent) {
1181       BLI_remlink(&G.recent_files, recent);
1182     }
1183     else {
1184       RecentFile *recent_next;
1185       for (recent = BLI_findlink(&G.recent_files, U.recent_files - 1); recent;
1186            recent = recent_next) {
1187         recent_next = recent->next;
1188         wm_history_file_free(recent);
1189       }
1190       recent = wm_history_file_new(blendfile_name);
1191     }
1192
1193     /* add current file to the beginning of list */
1194     BLI_addhead(&(G.recent_files), recent);
1195
1196     /* write current file to recent-files.txt */
1197     wm_history_file_write();
1198
1199     /* also update most recent files on System */
1200     GHOST_addToSystemRecentFiles(blendfile_name);
1201   }
1202 }
1203
1204 /** \} */
1205
1206 /* screen can be NULL */
1207 static ImBuf *blend_file_thumb(const bContext *C,
1208                                Scene *scene,
1209                                bScreen *screen,
1210                                BlendThumbnail **thumb_pt)
1211 {
1212   /* will be scaled down, but gives some nice oversampling */
1213   ImBuf *ibuf;
1214   BlendThumbnail *thumb;
1215   char err_out[256] = "unknown";
1216
1217   /* screen if no camera found */
1218   ScrArea *sa = NULL;
1219   ARegion *ar = NULL;
1220   View3D *v3d = NULL;
1221
1222   /* In case we are given a valid thumbnail data, just generate image from it. */
1223   if (*thumb_pt) {
1224     thumb = *thumb_pt;
1225     return BKE_main_thumbnail_to_imbuf(NULL, thumb);
1226   }
1227
1228   /* scene can be NULL if running a script at startup and calling the save operator */
1229   if (G.background || scene == NULL) {
1230     return NULL;
1231   }
1232
1233   if ((scene->camera == NULL) && (screen != NULL)) {
1234     sa = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0);
1235     ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
1236     if (ar) {
1237       v3d = sa->spacedata.first;
1238     }
1239   }
1240
1241   if (scene->camera == NULL && v3d == NULL) {
1242     return NULL;
1243   }
1244
1245   /* gets scaled to BLEN_THUMB_SIZE */
1246   Depsgraph *depsgraph = CTX_data_depsgraph(C);
1247
1248   if (scene->camera) {
1249     ibuf = ED_view3d_draw_offscreen_imbuf_simple(depsgraph,
1250                                                  scene,
1251                                                  NULL,
1252                                                  OB_SOLID,
1253                                                  scene->camera,
1254                                                  BLEN_THUMB_SIZE * 2,
1255                                                  BLEN_THUMB_SIZE * 2,
1256                                                  IB_rect,
1257                                                  V3D_OFSDRAW_NONE,
1258                                                  R_ALPHAPREMUL,
1259                                                  0,
1260                                                  NULL,
1261                                                  NULL,
1262                                                  err_out);
1263   }
1264   else {
1265     ibuf = ED_view3d_draw_offscreen_imbuf(depsgraph,
1266                                           scene,
1267                                           OB_SOLID,
1268                                           v3d,
1269                                           ar,
1270                                           BLEN_THUMB_SIZE * 2,
1271                                           BLEN_THUMB_SIZE * 2,
1272                                           IB_rect,
1273                                           R_ALPHAPREMUL,
1274                                           0,
1275                                           NULL,
1276                                           NULL,
1277                                           err_out);
1278   }
1279
1280   if (ibuf) {
1281     float aspect = (scene->r.xsch * scene->r.xasp) / (scene->r.ysch * scene->r.yasp);
1282
1283     /* dirty oversampling */
1284     IMB_scaleImBuf(ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE);
1285
1286     /* add pretty overlay */
1287     IMB_thumb_overlay_blend(ibuf->rect, ibuf->x, ibuf->y, aspect);
1288
1289     thumb = BKE_main_thumbnail_from_imbuf(NULL, ibuf);
1290   }
1291   else {
1292     /* '*thumb_pt' needs to stay NULL to prevent a bad thumbnail from being handled */
1293     fprintf(stderr, "blend_file_thumb failed to create thumbnail: %s\n", err_out);
1294     thumb = NULL;
1295   }
1296
1297   /* must be freed by caller */
1298   *thumb_pt = thumb;
1299
1300   return ibuf;
1301 }
1302
1303 /* easy access from gdb */
1304 bool write_crash_blend(void)
1305 {
1306   char path[FILE_MAX];
1307   int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on crash file */
1308
1309   BLI_strncpy(path, BKE_main_blendfile_path_from_global(), sizeof(path));
1310   BLI_path_extension_replace(path, sizeof(path), "_crash.blend");
1311   if (BLO_write_file(G_MAIN, path, fileflags, NULL, NULL)) {
1312     printf("written: %s\n", path);
1313     return 1;
1314   }
1315   else {
1316     printf("failed: %s\n", path);
1317     return 0;
1318   }
1319 }
1320
1321 /**
1322  * \see #wm_homefile_write_exec wraps #BLO_write_file in a similar way.
1323  */
1324 static bool wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports)
1325 {
1326   Main *bmain = CTX_data_main(C);
1327   Library *li;
1328   int len;
1329   int ok = false;
1330   BlendThumbnail *thumb, *main_thumb;
1331   ImBuf *ibuf_thumb = NULL;
1332
1333   len = strlen(filepath);
1334
1335   if (len == 0) {
1336     BKE_report(reports, RPT_ERROR, "Path is empty, cannot save");
1337     return ok;
1338   }
1339
1340   if (len >= FILE_MAX) {
1341     BKE_report(reports, RPT_ERROR, "Path too long, cannot save");
1342     return ok;
1343   }
1344
1345   /* Check if file write permission is ok */
1346   if (BLI_exists(filepath) && !BLI_file_is_writable(filepath)) {
1347     BKE_reportf(reports, RPT_ERROR, "Cannot save blend file, path '%s' is not writable", filepath);
1348     return ok;
1349   }
1350
1351   /* note: used to replace the file extension (to ensure '.blend'),
1352    * no need to now because the operator ensures,
1353    * its handy for scripts to save to a predefined name without blender editing it */
1354
1355   /* send the OnSave event */
1356   for (li = bmain->libraries.first; li; li = li->id.next) {
1357     if (BLI_path_cmp(li->filepath, filepath) == 0) {
1358       BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath);
1359       return ok;
1360     }
1361   }
1362
1363   /* Call pre-save callbacks befores writing preview,
1364    * that way you can generate custom file thumbnail. */
1365   BLI_callback_exec(bmain, NULL, BLI_CB_EVT_SAVE_PRE);
1366
1367   /* blend file thumbnail */
1368   /* Save before exit_editmode, otherwise derivedmeshes for shared data corrupt T27765. */
1369   /* Main now can store a '.blend' thumbnail, useful for background mode
1370    * or thumbnail customization. */
1371   main_thumb = thumb = bmain->blen_thumb;
1372   if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) {
1373     ibuf_thumb = blend_file_thumb(C, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
1374   }
1375
1376   /* operator now handles overwrite checks */
1377
1378   if (G.fileflags & G_FILE_AUTOPACK) {
1379     packAll(bmain, reports, false);
1380   }
1381
1382   /* don't forget not to return without! */
1383   WM_cursor_wait(1);
1384
1385   ED_editors_flush_edits(bmain, false);
1386
1387   fileflags |= G_FILE_HISTORY; /* write file history */
1388
1389   /* first time saving */
1390   /* XXX temp solution to solve bug, real fix coming (ton) */
1391   if ((BKE_main_blendfile_path(bmain)[0] == '\0') && !(fileflags & G_FILE_SAVE_COPY)) {
1392     BLI_strncpy(bmain->name, filepath, sizeof(bmain->name));
1393   }
1394
1395   /* XXX temp solution to solve bug, real fix coming (ton) */
1396   bmain->recovered = 0;
1397
1398   if (BLO_write_file(CTX_data_main(C), filepath, fileflags, reports, thumb)) {
1399     const bool do_history = (G.background == false) && (CTX_wm_manager(C)->op_undo_depth == 0);
1400
1401     if (!(fileflags & G_FILE_SAVE_COPY)) {
1402       G.relbase_valid = 1;
1403       BLI_strncpy(bmain->name, filepath, sizeof(bmain->name)); /* is guaranteed current file */
1404
1405       G.save_over = 1; /* disable untitled.blend convention */
1406     }
1407
1408     SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
1409
1410     /* prevent background mode scripts from clobbering history */
1411     if (do_history) {
1412       wm_history_file_update();
1413     }
1414
1415     BLI_callback_exec(bmain, NULL, BLI_CB_EVT_SAVE_POST);
1416
1417     /* run this function after because the file cant be written before the blend is */
1418     if (ibuf_thumb) {
1419       IMB_thumb_delete(filepath, THB_FAIL); /* without this a failed thumb overrides */
1420       ibuf_thumb = IMB_thumb_create(filepath, THB_LARGE, THB_SOURCE_BLEND, ibuf_thumb);
1421     }
1422
1423     /* Success. */
1424     ok = true;
1425   }
1426
1427   if (ibuf_thumb) {
1428     IMB_freeImBuf(ibuf_thumb);
1429   }
1430   if (thumb && thumb != main_thumb) {
1431     MEM_freeN(thumb);
1432   }
1433
1434   WM_cursor_wait(0);
1435
1436   return ok;
1437 }
1438
1439 /************************ autosave ****************************/
1440
1441 void wm_autosave_location(char *filepath)
1442 {
1443   const int pid = abs(getpid());
1444   char path[1024];
1445 #ifdef WIN32
1446   const char *savedir;
1447 #endif
1448
1449   if (G_MAIN && G.relbase_valid) {
1450     const char *basename = BLI_path_basename(BKE_main_blendfile_path_from_global());
1451     int len = strlen(basename) - 6;
1452     BLI_snprintf(path, sizeof(path), "%.*s_%d_autosave.blend", len, basename, pid);
1453   }
1454   else {
1455     BLI_snprintf(path, sizeof(path), "%d_autosave.blend", pid);
1456   }
1457
1458 #ifdef WIN32
1459   /* XXX Need to investigate how to handle default location of '/tmp/'
1460    * This is a relative directory on Windows, and it may be
1461    * found. Example:
1462    * Blender installed on D:\ drive, D:\ drive has D:\tmp\
1463    * Now, BLI_exists() will find '/tmp/' exists, but
1464    * BLI_make_file_string will create string that has it most likely on C:\
1465    * through get_default_root().
1466    * If there is no C:\tmp autosave fails. */
1467   if (!BLI_exists(BKE_tempdir_base())) {
1468     savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL);
1469     BLI_make_file_string("/", filepath, savedir, path);
1470     return;
1471   }
1472 #endif
1473
1474   BLI_make_file_string("/", filepath, BKE_tempdir_base(), path);
1475 }
1476
1477 void WM_autosave_init(wmWindowManager *wm)
1478 {
1479   wm_autosave_timer_ended(wm);
1480
1481   if (U.flag & USER_AUTOSAVE) {
1482     wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, U.savetime * 60.0);
1483   }
1484 }
1485
1486 void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *UNUSED(wt))
1487 {
1488   char filepath[FILE_MAX];
1489
1490   WM_event_remove_timer(wm, NULL, wm->autosavetimer);
1491
1492   /* if a modal operator is running, don't autosave, but try again in 10 seconds */
1493   for (wmWindow *win = wm->windows.first; win; win = win->next) {
1494     LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
1495       if (handler_base->type == WM_HANDLER_TYPE_OP) {
1496         wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
1497         if (handler->op) {
1498           wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, 10.0);
1499           if (G.debug) {
1500             printf("Skipping auto-save, modal operator running, retrying in ten seconds...\n");
1501           }
1502           return;
1503         }
1504       }
1505     }
1506   }
1507
1508   wm_autosave_location(filepath);
1509
1510   if (U.uiflag & USER_GLOBALUNDO) {
1511     /* fast save of last undobuffer, now with UI */
1512     struct MemFile *memfile = ED_undosys_stack_memfile_get_active(wm->undo_stack);
1513     if (memfile) {
1514       BLO_memfile_write_file(memfile, filepath);
1515     }
1516   }
1517   else {
1518     /*  save as regular blend file */
1519     Main *bmain = CTX_data_main(C);
1520     int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY);
1521
1522     ED_editors_flush_edits(bmain, false);
1523
1524     /* Error reporting into console */
1525     BLO_write_file(bmain, filepath, fileflags, NULL, NULL);
1526   }
1527   /* do timer after file write, just in case file write takes a long time */
1528   wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, U.savetime * 60.0);
1529 }
1530
1531 void wm_autosave_timer_ended(wmWindowManager *wm)
1532 {
1533   if (wm->autosavetimer) {
1534     WM_event_remove_timer(wm, NULL, wm->autosavetimer);
1535     wm->autosavetimer = NULL;
1536   }
1537 }
1538
1539 void wm_autosave_delete(void)
1540 {
1541   char filename[FILE_MAX];
1542
1543   wm_autosave_location(filename);
1544
1545   if (BLI_exists(filename)) {
1546     char str[FILE_MAX];
1547     BLI_make_file_string("/", str, BKE_tempdir_base(), BLENDER_QUIT_FILE);
1548
1549     /* if global undo; remove tempsave, otherwise rename */
1550     if (U.uiflag & USER_GLOBALUNDO) {
1551       BLI_delete(filename, false, false);
1552     }
1553     else {
1554       BLI_rename(filename, str);
1555     }
1556   }
1557 }
1558
1559 void wm_autosave_read(bContext *C, ReportList *reports)
1560 {
1561   char filename[FILE_MAX];
1562
1563   wm_autosave_location(filename);
1564   WM_file_read(C, filename, reports);
1565 }
1566
1567 /** \name Initialize WM_OT_open_xxx properties
1568  *
1569  * Check if load_ui was set by the caller.
1570  * Fall back to user preference when file flags not specified.
1571  *
1572  * \{ */
1573
1574 void wm_open_init_load_ui(wmOperator *op, bool use_prefs)
1575 {
1576   PropertyRNA *prop = RNA_struct_find_property(op->ptr, "load_ui");
1577   if (!RNA_property_is_set(op->ptr, prop)) {
1578     bool value = use_prefs ? ((U.flag & USER_FILENOUI) == 0) : ((G.fileflags & G_FILE_NO_UI) == 0);
1579
1580     RNA_property_boolean_set(op->ptr, prop, value);
1581   }
1582 }
1583
1584 void wm_open_init_use_scripts(wmOperator *op, bool use_prefs)
1585 {
1586   PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
1587   if (!RNA_property_is_set(op->ptr, prop)) {
1588     /* use G_FLAG_SCRIPT_AUTOEXEC rather than the userpref because this means if
1589      * the flag has been disabled from the command line, then opening
1590      * from the menu wont enable this setting. */
1591     bool value = use_prefs ? ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) :
1592                              ((G.f & G_FLAG_SCRIPT_AUTOEXEC) != 0);
1593
1594     RNA_property_boolean_set(op->ptr, prop, value);
1595   }
1596 }
1597
1598 /** \} */
1599
1600 void WM_file_tag_modified(void)
1601 {
1602   wmWindowManager *wm = G_MAIN->wm.first;
1603   if (wm->file_saved) {
1604     wm->file_saved = 0;
1605     /* notifier that data changed, for save-over warning or header */
1606     WM_main_add_notifier(NC_WM | ND_DATACHANGED, NULL);
1607   }
1608 }
1609
1610 /** \name Preferences/startup save & load.
1611  *
1612  * \{ */
1613
1614 /**
1615  * \see #wm_file_write wraps #BLO_write_file in a similar way.
1616  * \return success.
1617  */
1618 static int wm_homefile_write_exec(bContext *C, wmOperator *op)
1619 {
1620   Main *bmain = CTX_data_main(C);
1621   wmWindowManager *wm = CTX_wm_manager(C);
1622   wmWindow *win = CTX_wm_window(C);
1623   char filepath[FILE_MAX];
1624   int fileflags;
1625
1626   const char *app_template = U.app_template[0] ? U.app_template : NULL;
1627   const char *const cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, app_template);
1628   if (cfgdir == NULL) {
1629     BKE_report(op->reports, RPT_ERROR, "Unable to create user config path");
1630     return OPERATOR_CANCELLED;
1631   }
1632
1633   BLI_callback_exec(bmain, NULL, BLI_CB_EVT_SAVE_PRE);
1634
1635   /* check current window and close it if temp */
1636   if (win && WM_window_is_temp_screen(win)) {
1637     wm_window_close(C, wm, win);
1638   }
1639
1640   /* update keymaps in user preferences */
1641   WM_keyconfig_update(wm);
1642
1643   BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_STARTUP_FILE, NULL);
1644
1645   printf("Writing homefile: '%s' ", filepath);
1646
1647   ED_editors_flush_edits(bmain, false);
1648
1649   /*  force save as regular blend file */
1650   fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY);
1651
1652   if (BLO_write_file(bmain, filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) {
1653     printf("fail\n");
1654     return OPERATOR_CANCELLED;
1655   }
1656
1657   printf("ok\n");
1658
1659   G.save_over = 0;
1660
1661   BLI_callback_exec(bmain, NULL, BLI_CB_EVT_SAVE_POST);
1662
1663   return OPERATOR_FINISHED;
1664 }
1665
1666 void WM_OT_save_homefile(wmOperatorType *ot)
1667 {
1668   ot->name = "Save Startup File";
1669   ot->idname = "WM_OT_save_homefile";
1670   ot->description = "Make the current file the default .blend file";
1671
1672   ot->invoke = WM_operator_confirm;
1673   ot->exec = wm_homefile_write_exec;
1674 }
1675
1676 static int wm_userpref_autoexec_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
1677 {
1678   bPathCompare *path_cmp = MEM_callocN(sizeof(bPathCompare), "bPathCompare");
1679   BLI_addtail(&U.autoexec_paths, path_cmp);
1680   U.runtime.is_dirty = true;
1681   return OPERATOR_FINISHED;
1682 }
1683
1684 void WM_OT_userpref_autoexec_path_add(wmOperatorType *ot)
1685 {
1686   ot->name = "Add Autoexec Path";
1687   ot->idname = "WM_OT_userpref_autoexec_path_add";
1688   ot->description = "Add path to exclude from autoexecution";
1689
1690   ot->exec = wm_userpref_autoexec_add_exec;
1691
1692   ot->flag = OPTYPE_INTERNAL;
1693 }
1694
1695 static int wm_userpref_autoexec_remove_exec(bContext *UNUSED(C), wmOperator *op)
1696 {
1697   const int index = RNA_int_get(op->ptr, "index");
1698   bPathCompare *path_cmp = BLI_findlink(&U.autoexec_paths, index);
1699   if (path_cmp) {
1700     BLI_freelinkN(&U.autoexec_paths, path_cmp);
1701     U.runtime.is_dirty = true;
1702   }
1703   return OPERATOR_FINISHED;
1704 }
1705
1706 void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot)
1707 {
1708   ot->name = "Remove Autoexec Path";
1709   ot->idname = "WM_OT_userpref_autoexec_path_remove";
1710   ot->description = "Remove path to exclude from autoexecution";
1711
1712   ot->exec = wm_userpref_autoexec_remove_exec;
1713
1714   ot->flag = OPTYPE_INTERNAL;
1715
1716   RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
1717 }
1718
1719 /* Only save the prefs block. operator entry */
1720 static int wm_userpref_write_exec(bContext *C, wmOperator *op)
1721 {
1722   wmWindowManager *wm = CTX_wm_manager(C);
1723
1724   /* Update keymaps in user preferences. */
1725   WM_keyconfig_update(wm);
1726
1727   const bool ok = BKE_blendfile_userdef_write_all(op->reports);
1728
1729   return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1730 }
1731
1732 void WM_OT_save_userpref(wmOperatorType *ot)
1733 {
1734   ot->name = "Save Preferences";
1735   ot->idname = "WM_OT_save_userpref";
1736   ot->description = "Save preferences separately, overrides startup file preferences";
1737
1738   ot->invoke = WM_operator_confirm;
1739   ot->exec = wm_userpref_write_exec;
1740 }
1741
1742 /**
1743  * When reading preferences, there are some exceptions for values which are reset.
1744  */
1745 static void wm_userpref_read_exceptions(UserDef *userdef_curr,
1746                                         const UserDef *userdef_prev,
1747                                         const bool use_factory_settings)
1748 {
1749 #define USERDEF_RESTORE(member) \
1750   { \
1751     userdef_curr->member = userdef_prev->member; \
1752   } \
1753   ((void)0)
1754
1755   /* Current visible preferences category. */
1756   USERDEF_RESTORE(userpref);
1757
1758   if (use_factory_settings) {
1759     /* Preferences about the preferences.
1760      * Technically correct not to reset however this causes issues in practice.
1761      * Since loading factory settings will then overwrite your preferences on exit, see: T65702. */
1762     USERDEF_RESTORE(pref_flag);
1763   }
1764
1765 #undef USERDEF_RESTORE
1766 }
1767
1768 static void rna_struct_update_when_changed(bContext *C,
1769                                            Main *bmain,
1770                                            PointerRNA *ptr_a,
1771                                            PointerRNA *ptr_b)
1772 {
1773   CollectionPropertyIterator iter;
1774   PropertyRNA *iterprop = RNA_struct_iterator_property(ptr_a->type);
1775   BLI_assert(ptr_a->type == ptr_b->type);
1776   RNA_property_collection_begin(ptr_a, iterprop, &iter);
1777   for (; iter.valid; RNA_property_collection_next(&iter)) {
1778     PropertyRNA *prop = iter.ptr.data;
1779     if (STREQ(RNA_property_identifier(prop), "rna_type")) {
1780       continue;
1781     }
1782     switch (RNA_property_type(prop)) {
1783       case PROP_POINTER: {
1784         PointerRNA ptr_sub_a = RNA_property_pointer_get(ptr_a, prop);
1785         PointerRNA ptr_sub_b = RNA_property_pointer_get(ptr_b, prop);
1786         rna_struct_update_when_changed(C, bmain, &ptr_sub_a, &ptr_sub_b);
1787         break;
1788       }
1789       case PROP_COLLECTION:
1790         /* Don't handle collections. */
1791         break;
1792       default: {
1793         if (!RNA_property_equals(bmain, ptr_a, ptr_b, prop, RNA_EQ_STRICT)) {
1794           RNA_property_update(C, ptr_b, prop);
1795         }
1796       }
1797     }
1798   }
1799   RNA_property_collection_end(&iter);
1800 }
1801
1802 static void wm_userpref_update_when_changed(bContext *C,
1803                                             Main *bmain,
1804                                             UserDef *userdef_prev,
1805                                             UserDef *userdef_curr)
1806 {
1807   PointerRNA ptr_a, ptr_b;
1808   RNA_pointer_create(NULL, &RNA_Preferences, userdef_prev, &ptr_a);
1809   RNA_pointer_create(NULL, &RNA_Preferences, userdef_curr, &ptr_b);
1810   const bool is_dirty = userdef_curr->runtime.is_dirty;
1811
1812   rna_struct_update_when_changed(C, bmain, &ptr_a, &ptr_b);
1813
1814 #ifdef WITH_PYTHON
1815   BPY_execute_string(C, (const char *[]){"addon_utils", NULL}, "addon_utils.reset_all()");
1816 #endif
1817
1818   WM_keyconfig_reload(C);
1819   userdef_curr->runtime.is_dirty = is_dirty;
1820 }
1821
1822 static int wm_userpref_read_exec(bContext *C, wmOperator *op)
1823 {
1824   const bool use_data = false;
1825   const bool use_userdef = true;
1826   const bool use_factory_settings = STREQ(op->type->idname, "WM_OT_read_factory_userpref");
1827
1828   UserDef U_backup = U;
1829
1830   wm_homefile_read(C,
1831                    op->reports,
1832                    use_factory_settings,
1833                    false,
1834                    use_data,
1835                    use_userdef,
1836                    NULL,
1837                    WM_init_state_app_template_get(),
1838                    NULL);
1839
1840   wm_userpref_read_exceptions(&U, &U_backup, use_factory_settings);
1841
1842   Main *bmain = CTX_data_main(C);
1843
1844   wm_userpref_update_when_changed(C, bmain, &U_backup, &U);
1845
1846   if (use_factory_settings) {
1847     U.runtime.is_dirty = true;
1848   }
1849
1850   /* Needed to recalculate UI scaling values (eg, #UserDef.inv_dpi_fac). */
1851   wm_window_clear_drawable(bmain->wm.first);
1852
1853   WM_event_add_notifier(C, NC_WINDOW, NULL);
1854
1855   return OPERATOR_FINISHED;
1856 }
1857
1858 void WM_OT_read_userpref(wmOperatorType *ot)
1859 {
1860   ot->name = "Load Preferences";
1861   ot->idname = "WM_OT_read_userpref";
1862   ot->description = "Load last saved preferences";
1863
1864   ot->invoke = WM_operator_confirm;
1865   ot->exec = wm_userpref_read_exec;
1866 }
1867
1868 void WM_OT_read_factory_userpref(wmOperatorType *ot)
1869 {
1870   ot->name = "Load Factory Preferences";
1871   ot->idname = "WM_OT_read_factory_userpref";
1872   ot->description = "Load default preferences";
1873
1874   ot->invoke = WM_operator_confirm;
1875   ot->exec = wm_userpref_read_exec;
1876 }
1877
1878 static int wm_history_file_read_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
1879 {
1880   ED_file_read_bookmarks();
1881   wm_history_file_read();
1882   return OPERATOR_FINISHED;
1883 }
1884
1885 void WM_OT_read_history(wmOperatorType *ot)
1886 {
1887   ot->name = "Reload History File";
1888   ot->idname = "WM_OT_read_history";
1889   ot->description = "Reloads history and bookmarks";
1890
1891   ot->invoke = WM_operator_confirm;
1892   ot->exec = wm_history_file_read_exec;
1893
1894   /* this operator is only used for loading settings from a previous blender install */
1895   ot->flag = OPTYPE_INTERNAL;
1896 }
1897
1898 static int wm_homefile_read_exec(bContext *C, wmOperator *op)
1899 {
1900   const bool use_factory_settings = (STREQ(op->type->idname, "WM_OT_read_factory_settings"));
1901   bool use_userdef = false;
1902   char filepath_buf[FILE_MAX];
1903   const char *filepath = NULL;
1904   UserDef U_backup = U;
1905
1906   if (!use_factory_settings) {
1907     PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath");
1908
1909     /* This can be used when loading of a start-up file should only change
1910      * the scene content but keep the blender UI as it is. */
1911     wm_open_init_load_ui(op, true);
1912     SET_FLAG_FROM_TEST(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
1913
1914     if (RNA_property_is_set(op->ptr, prop)) {
1915       RNA_property_string_get(op->ptr, prop, filepath_buf);
1916       filepath = filepath_buf;
1917       if (BLI_access(filepath, R_OK)) {
1918         BKE_reportf(
1919             op->reports, RPT_ERROR, "Can't read alternative start-up file: '%s'", filepath);
1920         return OPERATOR_CANCELLED;
1921       }
1922     }
1923   }
1924   else {
1925     /* always load UI for factory settings (prefs will re-init) */
1926     G.fileflags &= ~G_FILE_NO_UI;
1927     /* Always load preferences with factory settings. */
1928     use_userdef = true;
1929   }
1930
1931   char app_template_buf[sizeof(U.app_template)];
1932   const char *app_template;
1933   PropertyRNA *prop_app_template = RNA_struct_find_property(op->ptr, "app_template");
1934   const bool use_splash = !use_factory_settings && RNA_boolean_get(op->ptr, "use_splash");
1935   const bool use_empty_data = RNA_boolean_get(op->ptr, "use_empty");
1936   const bool use_temporary_preferences = RNA_boolean_get(op->ptr, "use_temporary_preferences");
1937
1938   if (prop_app_template && RNA_property_is_set(op->ptr, prop_app_template)) {
1939     RNA_property_string_get(op->ptr, prop_app_template, app_template_buf);
1940     app_template = app_template_buf;
1941
1942     /* Always load preferences when switching templates with own preferences. */
1943     use_userdef = BKE_appdir_app_template_has_userpref(app_template) ||
1944                   BKE_appdir_app_template_has_userpref(U.app_template);
1945
1946     /* Turn override off, since we're explicitly loading a different app-template. */
1947     WM_init_state_app_template_set(NULL);
1948   }
1949   else {
1950     /* Normally NULL, only set when overriding from the command-line. */
1951     app_template = WM_init_state_app_template_get();
1952   }
1953
1954   bool use_data = true;
1955   wm_homefile_read(C,
1956                    op->reports,
1957                    use_factory_settings,
1958                    use_empty_data,
1959                    use_data,
1960                    use_userdef,
1961                    filepath,
1962                    app_template,
1963                    NULL);
1964   if (use_splash) {
1965     WM_init_splash(C);
1966   }
1967   SET_FLAG_FROM_TEST(G.f, use_temporary_preferences, G_FLAG_USERPREF_NO_SAVE_ON_EXIT);
1968
1969   if (use_userdef) {
1970     wm_userpref_read_exceptions(&U, &U_backup, use_factory_settings);
1971   }
1972
1973   return OPERATOR_FINISHED;
1974 }
1975
1976 static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data)
1977 {
1978   WM_operator_name_call_with_properties(
1979       C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data);
1980 }
1981
1982 static void wm_free_operator_properties_callback(void *user_data)
1983 {
1984   IDProperty *properties = (IDProperty *)user_data;
1985   IDP_FreeProperty(properties);
1986 }
1987
1988 static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1989 {
1990   if (U.uiflag & USER_SAVE_PROMPT && wm_file_or_image_is_modified(C)) {
1991     wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
1992     callback->exec = wm_homefile_read_after_dialog_callback;
1993     callback->user_data = IDP_CopyProperty(op->properties);
1994     callback->free_user_data = wm_free_operator_properties_callback;
1995     wm_close_file_dialog(C, callback);
1996     return OPERATOR_INTERFACE;
1997   }
1998   else {
1999     return wm_homefile_read_exec(C, op);
2000   }
2001 }
2002
2003 static void read_homefile_props(wmOperatorType *ot)
2004 {
2005   PropertyRNA *prop;
2006
2007   prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", "");
2008   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
2009
2010   prop = RNA_def_boolean(ot->srna, "use_empty", false, "Empty", "");
2011   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
2012
2013   prop = RNA_def_boolean(ot->srna,
2014                          "use_temporary_preferences",
2015                          false,
2016                          "Temporary Preferences",
2017                          "Don't save preferences on exit");
2018   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
2019 }
2020
2021 void WM_OT_read_homefile(wmOperatorType *ot)
2022 {
2023   PropertyRNA *prop;
2024   ot->name = "Reload Start-Up File";
2025   ot->idname = "WM_OT_read_homefile";
2026   ot->description = "Open the default file (doesn't save the current file)";
2027
2028   ot->invoke = wm_homefile_read_invoke;
2029   ot->exec = wm_homefile_read_exec;
2030
2031   prop = RNA_def_string_file_path(
2032       ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Path to an alternative start-up file");
2033   RNA_def_property_flag(prop, PROP_HIDDEN);
2034
2035   /* So scripts can use an alternative start-up file without the UI */
2036   prop = RNA_def_boolean(
2037       ot->srna, "load_ui", true, "Load UI", "Load user interface setup from the .blend file");
2038   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
2039
2040   /* So the splash can be kept open after loading a file (for templates). */
2041   prop = RNA_def_boolean(ot->srna, "use_splash", false, "Splash", "");
2042   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
2043
2044   read_homefile_props(ot);
2045
2046   /* omit poll to run in background mode */
2047 }
2048
2049 void WM_OT_read_factory_settings(wmOperatorType *ot)
2050 {
2051   ot->name = "Load Factory Settings";
2052   ot->idname = "WM_OT_read_factory_settings";
2053   ot->description = "Load default file and preferences";
2054
2055   ot->invoke = WM_operator_confirm;
2056   ot->exec = wm_homefile_read_exec;
2057
2058   read_homefile_props(ot);
2059   /* omit poll to run in background mode */
2060 }
2061
2062 /** \} */
2063
2064 /** \name Open main .blend file.
2065  *
2066  * \{ */
2067
2068 /**
2069  * Wrap #WM_file_read, shared by file reading operators.
2070  */
2071 static bool wm_file_read_opwrap(bContext *C,
2072                                 const char *filepath,
2073                                 ReportList *reports,
2074                                 const bool autoexec_init)
2075 {
2076   bool success;
2077
2078   /* XXX wm in context is not set correctly after WM_file_read -> crash */
2079   /* do it before for now, but is this correct with multiple windows? */
2080   WM_event_add_notifier(C, NC_WINDOW, NULL);
2081
2082   if (autoexec_init) {
2083     WM_file_autoexec_init(filepath);
2084   }
2085
2086   success = WM_file_read(C, filepath, reports);
2087
2088   return success;
2089 }
2090
2091 /* Generic operator state utilities
2092  *********************************************/
2093
2094 static void create_operator_state(wmOperatorType *ot, int first_state)
2095 {
2096   PropertyRNA *prop = RNA_def_int(
2097       ot->srna, "state", first_state, INT32_MIN, INT32_MAX, "State", "", INT32_MIN, INT32_MAX);
2098   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2099   RNA_def_property_flag(prop, PROP_HIDDEN);
2100 }
2101
2102 static int get_operator_state(wmOperator *op)
2103 {
2104   return RNA_int_get(op->ptr, "state");
2105 }
2106
2107 static void set_next_operator_state(wmOperator *op, int state)
2108 {
2109   RNA_int_set(op->ptr, "state", state);
2110 }
2111
2112 typedef struct OperatorDispatchTarget {
2113   int state;
2114   int (*run)(bContext *C, wmOperator *op);
2115 } OperatorDispatchTarget;
2116
2117 static int operator_state_dispatch(bContext *C, wmOperator *op, OperatorDispatchTarget *targets)
2118 {
2119   int state = get_operator_state(op);
2120   for (int i = 0; targets[i].run; i++) {
2121     OperatorDispatchTarget target = targets[i];
2122     if (target.state == state) {
2123       return target.run(C, op);
2124     }
2125   }
2126   BLI_assert(false);
2127   return OPERATOR_CANCELLED;
2128 }
2129
2130 /* Open Mainfile operator
2131  ********************************************/
2132
2133 enum {
2134   OPEN_MAINFILE_STATE_DISCARD_CHANGES,
2135   OPEN_MAINFILE_STATE_SELECT_FILE_PATH,
2136   OPEN_MAINFILE_STATE_OPEN,
2137 };
2138
2139 static int wm_open_mainfile_dispatch(bContext *C, wmOperator *op);
2140
2141 static void wm_open_mainfile_after_dialog_callback(bContext *C, void *user_data)
2142 {
2143   WM_operator_name_call_with_properties(
2144       C, "WM_OT_open_mainfile", WM_OP_INVOKE_DEFAULT, (IDProperty *)user_data);
2145 }
2146
2147 static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op)
2148 {
2149   if (RNA_boolean_get(op->ptr, "display_file_selector")) {
2150     set_next_operator_state(op, OPEN_MAINFILE_STATE_SELECT_FILE_PATH);
2151   }
2152   else {
2153     set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN);
2154   }
2155
2156   if (U.uiflag & USER_SAVE_PROMPT && wm_file_or_image_is_modified(C)) {
2157     wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
2158     callback->exec = wm_open_mainfile_after_dialog_callback;
2159     callback->user_data = IDP_CopyProperty(op->properties);
2160     callback->free_user_data = wm_free_operator_properties_callback;
2161     wm_close_file_dialog(C, callback);
2162     return OPERATOR_INTERFACE;
2163   }
2164   else {
2165     return wm_open_mainfile_dispatch(C, op);
2166   }
2167 }
2168
2169 static int wm_open_mainfile__select_file_path(bContext *C, wmOperator *op)
2170 {
2171   set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN);
2172
2173   Main *bmain = CTX_data_main(C);
2174   const char *openname = BKE_main_blendfile_path(bmain);
2175
2176   if (CTX_wm_window(C) == NULL) {
2177     /* in rare cases this could happen, when trying to invoke in background
2178      * mode on load for example. Don't use poll for this because exec()
2179      * can still run without a window */
2180     BKE_report(op->reports, RPT_ERROR, "Context window not set");
2181     return OPERATOR_CANCELLED;
2182   }
2183
2184   /* if possible, get the name of the most recently used .blend file */
2185   if (G.recent_files.first) {
2186     struct RecentFile *recent = G.recent_files.first;
2187     openname = recent->filepath;
2188   }
2189
2190   RNA_string_set(op->ptr, "filepath", openname);
2191   wm_open_init_load_ui(op, true);
2192   wm_open_init_use_scripts(op, true);
2193   op->customdata = NULL;
2194
2195   WM_event_add_fileselect(C, op);
2196
2197   return OPERATOR_RUNNING_MODAL;
2198 }
2199
2200 static int wm_open_mainfile__open(bContext *C, wmOperator *op)
2201 {
2202   char filepath[FILE_MAX];
2203   bool success;
2204
2205   RNA_string_get(op->ptr, "filepath", filepath);
2206
2207   /* re-use last loaded setting so we can reload a file without changing */
2208   wm_open_init_load_ui(op, false);
2209   wm_open_init_use_scripts(op, false);
2210
2211   if (RNA_boolean_get(op->ptr, "load_ui")) {
2212     G.fileflags &= ~G_FILE_NO_UI;
2213   }
2214   else {
2215     G.fileflags |= G_FILE_NO_UI;
2216   }
2217
2218   if (RNA_boolean_get(op->ptr, "use_scripts")) {
2219     G.f |= G_FLAG_SCRIPT_AUTOEXEC;
2220   }
2221   else {
2222     G.f &= ~G_FLAG_SCRIPT_AUTOEXEC;
2223   }
2224
2225   success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_FLAG_SCRIPT_AUTOEXEC));
2226
2227   /* for file open also popup for warnings, not only errors */
2228   BKE_report_print_level_set(op->reports, RPT_WARNING);
2229
2230   if (success) {
2231     return OPERATOR_FINISHED;
2232   }
2233   else {
2234     return OPERATOR_CANCELLED;
2235   }
2236 }
2237
2238 static OperatorDispatchTarget wm_open_mainfile_dispatch_targets[] = {
2239     {OPEN_MAINFILE_STATE_DISCARD_CHANGES, wm_open_mainfile__discard_changes},
2240     {OPEN_MAINFILE_STATE_SELECT_FILE_PATH, wm_open_mainfile__select_file_path},
2241     {OPEN_MAINFILE_STATE_OPEN, wm_open_mainfile__open},
2242     {0, NULL},
2243 };
2244
2245 static int wm_open_mainfile_dispatch(bContext *C, wmOperator *op)
2246 {
2247   return operator_state_dispatch(C, op, wm_open_mainfile_dispatch_targets);
2248 }
2249
2250 static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2251 {
2252   return wm_open_mainfile_dispatch(C, op);
2253 }
2254
2255 static int wm_open_mainfile_exec(bContext *C, wmOperator *op)
2256 {
2257   return wm_open_mainfile__open(C, op);
2258 }
2259
2260 /* currently fits in a pointer */
2261 struct FileRuntime {
2262   bool is_untrusted;
2263 };
2264
2265 static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op)
2266 {
2267   struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata;
2268   PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
2269   bool is_untrusted = false;
2270   char path[FILE_MAX];
2271   char *lslash;
2272
2273   RNA_string_get(op->ptr, "filepath", path);
2274
2275   /* get the dir */
2276   lslash = (char *)BLI_last_slash(path);
2277   if (lslash) {
2278     *(lslash + 1) = '\0';
2279   }
2280
2281   if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
2282     if (BKE_autoexec_match(path) == true) {
2283       RNA_property_boolean_set(op->ptr, prop, false);
2284       is_untrusted = true;
2285     }
2286   }
2287
2288   if (file_info) {
2289     file_info->is_untrusted = is_untrusted;
2290   }
2291
2292   return is_untrusted;
2293 }
2294
2295 static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op)
2296 {
2297   struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata;
2298   uiLayout *layout = op->layout;
2299   uiLayout *col = op->layout;
2300   const char *autoexec_text;
2301
2302   uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE);
2303
2304   col = uiLayoutColumn(layout, false);
2305   if (file_info->is_untrusted) {
2306     autoexec_text = IFACE_("Trusted Source [Untrusted Path]");
2307     uiLayoutSetActive(col, false);
2308     uiLayoutSetEnabled(col, false);
2309   }
2310   else {
2311     autoexec_text = IFACE_("Trusted Source");
2312   }
2313
2314   uiItemR(col, op->ptr, "use_scripts", 0, autoexec_text, ICON_NONE);
2315 }
2316
2317 void WM_OT_open_mainfile(wmOperatorType *ot)
2318 {
2319   ot->name = "Open Blender File";
2320   ot->idname = "WM_OT_open_mainfile";
2321   ot->description = "Open a Blender file";
2322
2323   ot->invoke = wm_open_mainfile_invoke;
2324   ot->exec = wm_open_mainfile_exec;
2325   ot->check = wm_open_mainfile_check;
2326   ot->ui = wm_open_mainfile_ui;
2327   /* omit window poll so this can work in background mode */
2328
2329   WM_operator_properties_filesel(ot,
2330                                  FILE_TYPE_FOLDER | FILE_TYPE_BLENDER,
2331                                  FILE_BLENDER,
2332                                  FILE_OPENFILE,
2333                                  WM_FILESEL_FILEPATH,
2334                                  FILE_DEFAULTDISPLAY,
2335                                  FILE_SORT_ALPHA);
2336
2337   RNA_def_boolean(
2338       ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file");
2339   RNA_def_boolean(ot->srna,
2340                   "use_scripts",
2341                   true,
2342                   "Trusted Source",
2343                   "Allow .blend file to execute scripts automatically, default available from "
2344                   "system preferences");
2345
2346   PropertyRNA *prop = RNA_def_boolean(
2347       ot->srna, "display_file_selector", true, "Display File Selector", "");
2348   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2349
2350   create_operator_state(ot, OPEN_MAINFILE_STATE_DISCARD_CHANGES);
2351 }
2352
2353 /** \} */
2354
2355 /** \name Reload (revert) main .blend file.
2356  *
2357  * \{ */
2358
2359 static int wm_revert_mainfile_exec(bContext *C, wmOperator *op)
2360 {
2361   Main *bmain = CTX_data_main(C);
2362   bool success;
2363   char filepath[FILE_MAX];
2364
2365   wm_open_init_use_scripts(op, false);
2366
2367   if (RNA_boolean_get(op->ptr, "use_scripts")) {
2368     G.f |= G_FLAG_SCRIPT_AUTOEXEC;
2369   }
2370   else {
2371     G.f &= ~G_FLAG_SCRIPT_AUTOEXEC;
2372   }
2373
2374   BLI_strncpy(filepath, BKE_main_blendfile_path(bmain), sizeof(filepath));
2375   success = wm_file_read_opwrap(C, filepath, op->reports, !(G.f & G_FLAG_SCRIPT_AUTOEXEC));
2376
2377   if (success) {
2378     return OPERATOR_FINISHED;
2379   }
2380   else {
2381     return OPERATOR_CANCELLED;
2382   }
2383 }
2384
2385 static bool wm_revert_mainfile_poll(bContext *UNUSED(C))
2386 {
2387   return G.relbase_valid;
2388 }
2389
2390 void WM_OT_revert_mainfile(wmOperatorType *ot)
2391 {
2392   ot->name = "Revert";
2393   ot->idname = "WM_OT_revert_mainfile";
2394   ot->description = "Reload the saved file";
2395   ot->invoke = WM_operator_confirm;
2396
2397   RNA_def_boolean(ot->srna,
2398                   "use_scripts",
2399                   true,
2400                   "Trusted Source",
2401                   "Allow .blend file to execute scripts automatically, default available from "
2402                   "system preferences");
2403
2404   ot->exec = wm_revert_mainfile_exec;
2405   ot->poll = wm_revert_mainfile_poll;
2406 }
2407
2408 /** \} */
2409
2410 /** \name Recover last session & auto-save.
2411  *
2412  * \{ */
2413
2414 void WM_recover_last_session(bContext *C, ReportList *reports)
2415 {
2416   char filepath[FILE_MAX];
2417
2418   BLI_make_file_string("/", filepath, BKE_tempdir_base(), BLENDER_QUIT_FILE);
2419   /* if reports==NULL, it's called directly without operator, we add a quick check here */
2420   if (reports || BLI_exists(filepath)) {
2421     G.fileflags |= G_FILE_RECOVER;
2422
2423     wm_file_read_opwrap(C, filepath, reports, true);
2424
2425     G.fileflags &= ~G_FILE_RECOVER;
2426
2427     /* XXX bad global... fixme */
2428     Main *bmain = CTX_data_main(C);
2429     if (BKE_main_blendfile_path(bmain)[0] != '\0') {
2430       G.file_loaded = 1; /* prevents splash to show */
2431     }
2432     else {
2433       G.relbase_valid = 0;
2434       G.save_over = 0; /* start with save preference untitled.blend */
2435     }
2436   }
2437 }
2438
2439 static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
2440 {
2441   WM_recover_last_session(C, op->reports);
2442   return OPERATOR_FINISHED;
2443 }
2444
2445 void WM_OT_recover_last_session(wmOperatorType *ot)
2446 {
2447   ot->name = "Recover Last Session";
2448   ot->idname = "WM_OT_recover_last_session";
2449   ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")";
2450   ot->invoke = WM_operator_confirm;
2451
2452   ot->exec = wm_recover_last_session_exec;
2453 }
2454
2455 static int wm_recover_auto_save_exec(bContext *C, wmOperator *op)
2456 {
2457   char filepath[FILE_MAX];
2458   bool success;
2459
2460   RNA_string_get(op->ptr, "filepath", filepath);
2461
2462   G.fileflags |= G_FILE_RECOVER;
2463
2464   success = wm_file_read_opwrap(C, filepath, op->reports, true);
2465
2466   G.fileflags &= ~G_FILE_RECOVER;
2467
2468   if (success) {
2469     return OPERATOR_FINISHED;
2470   }
2471   else {
2472     return OPERATOR_CANCELLED;
2473   }
2474 }
2475
2476 static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2477 {
2478   char filename[FILE_MAX];
2479
2480   wm_autosave_location(filename);
2481   RNA_string_set(op->ptr, "filepath", filename);
2482   WM_event_add_fileselect(C, op);
2483
2484   return OPERATOR_RUNNING_MODAL;
2485 }
2486
2487 void WM_OT_recover_auto_save(wmOperatorType *ot)
2488 {
2489   ot->name = "Recover Auto Save";
2490   ot->idname = "WM_OT_recover_auto_save";
2491   ot->description = "Open an automatically saved file to recover it";
2492
2493   ot->exec = wm_recover_auto_save_exec;
2494   ot->invoke = wm_recover_auto_save_invoke;
2495
2496   WM_operator_properties_filesel(ot,
2497                                  FILE_TYPE_BLENDER,
2498                                  FILE_BLENDER,
2499                                  FILE_OPENFILE,
2500                                  WM_FILESEL_FILEPATH,
2501                                  FILE_LONGDISPLAY,
2502                                  FILE_SORT_TIME);
2503 }
2504
2505 /** \} */
2506
2507 /** \name Save main .blend file.
2508  *
2509  * \{ */
2510
2511 static void wm_filepath_default(char *filepath)
2512 {
2513   if (G.save_over == false) {
2514     BLI_ensure_filename(filepath, FILE_MAX, "untitled.blend");
2515   }
2516 }
2517
2518 static void save_set_compress(wmOperator *op)
2519 {
2520   PropertyRNA *prop;
2521
2522   prop = RNA_struct_find_property(op->ptr, "compress");
2523   if (!RNA_property_is_set(op->ptr, prop)) {
2524     if (G.save_over) { /* keep flag for existing file */
2525       RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0);
2526     }
2527     else { /* use userdef for new file */
2528       RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0);
2529     }
2530   }
2531 }
2532
2533 static void save_set_filepath(bContext *C, wmOperator *op)
2534 {
2535   Main *bmain = CTX_data_main(C);
2536   PropertyRNA *prop;
2537   char name[FILE_MAX];
2538
2539   prop = RNA_struct_find_property(op->ptr, "filepath");
2540   if (!RNA_property_is_set(op->ptr, prop)) {
2541     /* if not saved before, get the name of the most recently used .blend file */
2542     if (BKE_main_blendfile_path(bmain)[0] == '\0' && G.recent_files.first) {
2543       struct RecentFile *recent = G.recent_files.first;
2544       BLI_strncpy(name, recent->filepath, FILE_MAX);
2545     }
2546     else {
2547       BLI_strncpy(name, bmain->name, FILE_MAX);
2548     }
2549
2550     wm_filepath_default(name);
2551     RNA_property_string_set(op->ptr, prop, name);
2552   }
2553 }
2554
2555 static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2556 {
2557
2558   save_set_compress(op);
2559   save_set_filepath(C, op);
2560
2561   WM_event_add_fileselect(C, op);
2562
2563   return OPERATOR_RUNNING_MODAL;
2564 }
2565
2566 /* function used for WM_OT_save_mainfile too */
2567 static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
2568 {
2569   Main *bmain = CTX_data_main(C);
2570   char path[FILE_MAX];
2571   const bool is_save_as = (op->type->invoke == wm_save_as_mainfile_invoke);
2572
2573   save_set_compress(op);
2574
2575   if (RNA_struct_property_is_set(op->ptr, "filepath")) {
2576     RNA_string_get(op->ptr, "filepath", path);
2577   }
2578   else {
2579     BLI_strncpy(path, BKE_main_blendfile_path(bmain), FILE_MAX);
2580     wm_filepath_default(path);
2581   }
2582
2583   const int fileflags_orig = G.fileflags;
2584   int fileflags = G.fileflags & ~G_FILE_USERPREFS;
2585
2586   /* set compression flag */
2587   SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "compress"), G_FILE_COMPRESS);
2588   SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "relative_remap"), G_FILE_RELATIVE_REMAP);
2589   SET_FLAG_FROM_TEST(
2590       fileflags,
2591       (RNA_struct_property_is_set(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")),
2592       G_FILE_SAVE_COPY);
2593
2594   const bool ok = wm_file_write(C, path, fileflags, op->reports);
2595
2596   if ((op->flag & OP_IS_INVOKE) == 0) {
2597     /* OP_IS_INVOKE is set when the operator is called from the GUI.
2598      * If it is not set, the operator is called from a script and
2599      * shouldn't influence G.fileflags. */
2600     G.fileflags = fileflags_orig;
2601   }
2602
2603   if (ok == false) {
2604     return OPERATOR_CANCELLED;
2605   }
2606
2607   WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL);
2608
2609   if (!is_save_as && RNA_boolean_get(op->ptr, "exit")) {
2610     wm_exit_schedule_delayed(C);
2611   }
2612
2613   return OPERATOR_FINISHED;
2614 }
2615
2616 /* function used for WM_OT_save_mainfile too */
2617 static bool blend_save_check(bContext *UNUSED(C), wmOperator *op)
2618 {
2619   char filepath[FILE_MAX];
2620   RNA_string_get(op->ptr, "filepath", filepath);
2621   if (!BLO_has_bfile_extension(filepath)) {
2622     /* some users would prefer BLI_path_extension_replace(),
2623      * we keep getting nitpicking bug reports about this - campbell */
2624     BLI_path_extension_ensure(filepath, FILE_MAX, ".blend");
2625     RNA_string_set(op->ptr, "filepath", filepath);
2626     return true;
2627   }
2628   return false;
2629 }
2630
2631 void WM_OT_save_as_mainfile(wmOperatorType *ot)
2632 {
2633   PropertyRNA *prop;
2634
2635   ot->name = "Save As Blender File";
2636   ot->idname = "WM_OT_save_as_mainfile";
2637   ot->description = "Save the current file in the desired location";
2638
2639   ot->invoke = wm_save_as_mainfile_invoke;
2640   ot->exec = wm_save_as_mainfile_exec;
2641   ot->check = blend_save_check;
2642   /* omit window poll so this can work in background mode */
2643
2644   WM_operator_properties_filesel(ot,
2645                                  FILE_TYPE_FOLDER | FILE_TYPE_BLENDER,
2646                                  FILE_BLENDER,
2647                                  FILE_SAVE,
2648                                  WM_FILESEL_FILEPATH,
2649                                  FILE_DEFAULTDISPLAY,
2650                                  FILE_SORT_ALPHA);
2651   RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
2652   RNA_def_boolean(ot->srna,
2653                   "relative_remap",
2654                   true,
2655                   "Remap Relative",
2656                   "Make paths relative when saving to a different directory");
2657   prop = RNA_def_boolean(
2658       ot->srna,
2659       "copy",
2660       false,
2661       "Save Copy",
2662       "Save a copy of the actual working state but does not make saved file active");
2663   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2664 }
2665
2666 static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2667 {
2668   int ret;
2669
2670   /* cancel if no active window */
2671   if (CTX_wm_window(C) == NULL) {
2672     return OPERATOR_CANCELLED;
2673   }
2674
2675   save_set_compress(op);
2676   save_set_filepath(C, op);
2677
2678   /* if we're saving for the first time and prefer relative paths -
2679    * any existing paths will be absolute,
2680    * enable the option to remap paths to avoid confusion T37240. */
2681   if ((G.relbase_valid == false) && (U.flag & USER_RELPATHS)) {
2682     PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
2683     if (!RNA_property_is_set(op->ptr, prop)) {
2684       RNA_property_boolean_set(op->ptr, prop, true);
2685     }
2686   }
2687
2688   if (G.save_over) {
2689     char path[FILE_MAX];
2690
2691     RNA_string_get(op->ptr, "filepath", path);
2692     ret = wm_save_as_mainfile_exec(C, op);
2693     /* Without this there is no feedback the file was saved. */
2694     BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", BLI_path_basename(path));
2695   }
2696   else {
2697     WM_event_add_fileselect(C, op);
2698     ret = OPERATOR_RUNNING_MODAL;
2699   }
2700
2701   return ret;
2702 }
2703
2704 void WM_OT_save_mainfile(wmOperatorType *ot)
2705 {
2706   ot->name = "Save Blender File";
2707   ot->idname = "WM_OT_save_mainfile";
2708   ot->description = "Save the current Blender file";
2709
2710   ot->invoke = wm_save_mainfile_invoke;
2711   ot->exec = wm_save_as_mainfile_exec;
2712   ot->check = blend_save_check;
2713   /* omit window poll so this can work in background mode */
2714
2715   PropertyRNA *prop;
2716   WM_operator_properties_filesel(ot,
2717                                  FILE_TYPE_FOLDER | FILE_TYPE_BLENDER,
2718                                  FILE_BLENDER,
2719                                  FILE_SAVE,
2720                                  WM_FILESEL_FILEPATH,
2721                                  FILE_DEFAULTDISPLAY,
2722                                  FILE_SORT_ALPHA);
2723   RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
2724   RNA_def_boolean(ot->srna,
2725                   "relative_remap",
2726                   false,
2727                   "Remap Relative",
2728                   "Make paths relative when saving to a different directory");
2729
2730   prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "Exit Blender after saving");
2731   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
2732 }
2733
2734 /** \} */
2735
2736 /** \name Auto-execution of scripts warning popup
2737  *
2738  * \{ */
2739
2740 static void wm_block_autorun_warning_ignore(bContext *C, void *arg_block, void *UNUSED(arg))
2741 {
2742   wmWindow *win = CTX_wm_window(C);
2743   UI_popup_block_close(C, win, arg_block);
2744 }
2745
2746 static void wm_block_autorun_warning_reload_with_scripts(bContext *C,
2747                                                          void *arg_block,
2748                                                          void *UNUSED(arg))
2749 {
2750   wmWindow *win = CTX_wm_window(C);
2751
2752   UI_popup_block_close(C, win, arg_block);
2753
2754   /* Save user preferences for permanent execution. */
2755   if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
2756     WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, NULL);
2757   }
2758
2759   /* Load file again with scripts enabled.
2760    * The reload is necessary to allow scripts to run when the files loads. */
2761   wmOperatorType *ot = WM_operatortype_find("WM_OT_revert_mainfile", false);
2762
2763   PointerRNA props_ptr;
2764   WM_operator_properties_create_ptr(&props_ptr, ot);
2765   RNA_boolean_set(&props_ptr, "use_scripts", true);
2766   WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &props_ptr);
2767   WM_operator_properties_free(&props_ptr);
2768 }
2769
2770 static void wm_block_autorun_warning_enable_scripts(bContext *C,
2771                                                     void *arg_block,
2772                                                     void *UNUSED(arg))
2773 {
2774   wmWindow *win = CTX_wm_window(C);
2775   Main *bmain = CTX_data_main(C);
2776
2777   UI_popup_block_close(C, win, arg_block);
2778
2779   /* Save user preferences for permanent execution. */
2780   if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
2781     WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, NULL);
2782   }
2783
2784   /* Force a full refresh, but without reloading the file. */
2785   LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
2786     BKE_scene_free_depsgraph_hash(scene);
2787   }
2788 }
2789
2790 /* Build the autorun warning dialog UI */
2791 static uiBlock *block_create_autorun_warning(struct bContext *C,
2792                                              struct ARegion *ar,
2793                                              void *UNUSED(arg1))
2794 {
2795   wmWindowManager *wm = CTX_wm_manager(C);
2796   uiStyle *style = UI_style_get();
2797   uiBlock *block = UI_block_begin(C, ar, "autorun_warning_popup", UI_EMBOSS);
2798
2799   UI_block_flag_enable(
2800       block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT);
2801   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
2802   UI_block_emboss_set(block, UI_EMBOSS);
2803
2804   uiLayout *layout = UI_block_layout(block,
2805                                      UI_LAYOUT_VERTICAL,
2806                                      UI_LAYOUT_PANEL,
2807                                      10,
2808                                      2,
2809                                      U.widget_unit * 24,
2810                                      U.widget_unit * 6,
2811                                      0,
2812                                      style);
2813
2814   /* Text and some vertical space */
2815   uiLayout *col = uiLayoutColumn(layout, true);
2816   uiItemL(col,
2817           TIP_("For security reasons, automatic execution of Python scripts in this file was "
2818                "disabled:"),
2819           ICON_ERROR);
2820   uiLayout *sub = uiLayoutRow(col, true);
2821   uiLayoutSetRedAlert(sub, true);
2822   uiItemL(sub, G.autoexec_fail, ICON_BLANK1);
2823   uiItemL(col, TIP_("This may lead to unexpected behavior"), ICON_BLANK1);
2824
2825   uiItemS(layout);
2826
2827   PointerRNA pref_ptr;
2828   RNA_pointer_create(NULL, &RNA_PreferencesFilePaths, &U, &pref_ptr);
2829   uiItemR(layout,
2830           &pref_ptr,
2831           "use_scripts_auto_execute",
2832           0,
2833           TIP_("Permanently allow execution of scripts"),
2834           ICON_NONE);
2835
2836   uiItemS(layout);
2837
2838   /* Buttons */
2839   uiBut *but;
2840   uiLayout *split = uiLayoutSplit(layout, 0.0f, true);
2841   col = uiLayoutColumn(split, false);
2842
2843   /* Allow reload if we have a saved file.
2844    * Otherwise just enable scripts and reset the depsgraphs. */
2845   if (G.relbase_valid && wm->file_saved) {
2846     but = uiDefIconTextBut(block,
2847                            UI_BTYPE_BUT,
2848                            0,
2849                            ICON_NONE,
2850                            IFACE_("Allow Execution"),
2851                            0,
2852                            0,
2853                            50,
2854                            UI_UNIT_Y,
2855                            NULL,
2856                            0,
2857                            0,
2858                            0,
2859                            0,
2860                            TIP_("Reload file with execution of Python scripts enabled"));
2861     UI_but_func_set(but, wm_block_autorun_warning_reload_with_scripts, block, NULL);
2862   }
2863   else {
2864     but = uiDefIconTextBut(block,
2865                            UI_BTYPE_BUT,
2866                            0,
2867                            ICON_NONE,
2868                            IFACE_("Allow Execution"),
2869                            0,
2870                            0,
2871                            50,
2872                            UI_UNIT_Y,
2873                            NULL,
2874                            0,
2875                            0,
2876                            0,
2877                            0,
2878                            TIP_("Enable scripts"));
2879     UI_but_func_set(but, wm_block_autorun_warning_enable_scripts, block, NULL);
2880   }
2881
2882   /* empty space between buttons */
2883   col = uiLayoutColumn(split, false);
2884   uiItemS(col);
2885
2886   col = uiLayoutColumn(split, 1);
2887   but = uiDefIconTextBut(block,
2888                          UI_BTYPE_BUT,
2889                          0,
2890                          ICON_NONE,
2891                          IFACE_("Ignore"),
2892                          0,
2893                          0,
2894                          50,
2895                          UI_UNIT_Y,
2896                          NULL,
2897                          0,
2898                          0,
2899                          0,
2900                          0,
2901                          TIP_("Continue using file without Python scripts"));
2902   UI_but_func_set(but, wm_block_autorun_warning_ignore, block, NULL);
2903
2904   UI_block_bounds_set_centered(block, 10);
2905
2906   return block;
2907 }
2908
2909 void wm_test_autorun_warning(bContext *C)
2910 {
2911   /* Test if any auto-execution of scripts failed. */
2912   if ((G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL) == 0) {
2913     return;
2914   }
2915
2916   /* Only show the warning once. */
2917   if (G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET) {
2918     return;
2919   }
2920
2921   G.f |= G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET;
2922
2923   wmWindowManager *wm = CTX_wm_manager(C);
2924   wmWindow *win = (wm->winactive) ? wm->winactive : wm->windows.first;
2925
2926   if (win) {
2927     wmWindow *prevwin = CTX_wm_window(C);
2928     CTX_wm_window_set(C, win);
2929     UI_popup_block_invoke(C, block_create_autorun_warning, NULL, NULL);
2930     CTX_wm_window_set(C, prevwin);
2931   }
2932 }
2933
2934 /* Close File Dialog
2935  *************************************/
2936
2937 static char save_images_when_file_is_closed = true;
2938
2939 static void wm_block_file_close_cancel(bContext *C, void *arg_block, void *UNUSED(arg_data))
2940 {
2941   wmWindow *win = CTX_wm_window(C);
2942   UI_popup_block_close(C, win, arg_block);
2943 }
2944
2945 static void wm_block_file_close_discard(bContext *C, void *arg_block, void *arg_data)
2946 {
2947   wmGenericCallback *callback = WM_generic_callback_steal((wmGenericCallback *)arg_data);
2948
2949   /* Close the popup before executing the callback. Otherwise
2950    * the popup might be closed by the callback, which will lead
2951    * to a crash. */
2952   wmWindow *win = CTX_wm_window(C);
2953   UI_popup_block_close(C, win, arg_block);
2954
2955   callback->exec(C, callback->user_data);
2956   WM_generic_callback_free(callback);
2957 }
2958
2959 static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_data)
2960 {
2961   wmGenericCallback *callback = WM_generic_callback_steal((wmGenericCallback *)arg_data);
2962   bool execute_callback = true;
2963
2964   wmWindow *win = CTX_wm_window(C);
2965   UI_popup_block_close(C, win, arg_block);
2966
2967   if (save_images_when_file_is_closed) {
2968     ReportList *reports = CTX_wm_reports(C);
2969     if (!ED_image_save_all_modified(C, reports)) {
2970       execute_callback = false;
2971     }
2972     WM_report_banner_show();
2973   }
2974
2975   Main *bmain = CTX_data_main(C);
2976   bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0';
2977
2978   if (file_has_been_saved_before) {
2979     WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL);
2980   }
2981   else {
2982     WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL);
2983     execute_callback = false;
2984   }
2985
2986   if (execute_callback) {
2987     callback->exec(C, callback->user_data);
2988   }
2989   WM_generic_callback_free(callback);
2990 }
2991
2992 static void wm_block_file_close_cancel_button(uiBlock *block, wmGenericCallback *post_action)
2993 {
2994   uiBut *but = uiDefIconTextBut(
2995       block, UI_BTYPE_BUT, 0, 0, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, "");
2996   UI_but_func_set(but, wm_block_file_close_cancel, block, post_action);
2997   UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
2998 }
2999
3000 static void wm_block_file_close_discard_button(uiBlock *block, wmGenericCallback *post_action)
3001 {
3002   uiBut *but = uiDefIconTextBut(
3003       block, UI_BTYPE_BUT, 0, 0, IFACE_("Discard Changes"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, "");
3004   UI_but_func_set(but, wm_block_file_close_discard, block, post_action);
3005   UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
3006 }
3007
3008 static void wm_block_file_close_save_button(uiBlock *block, wmGenericCallback *post_action)
3009 {
3010   uiBut *but = uiDefIconTextBut(
3011       block, UI_BTYPE_BUT, 0, 0, IFACE_("Save"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, "");
3012   UI_but_func_set(but, wm_block_file_close_save, block, post_action);
3013   UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
3014   UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT);
3015 }
3016
3017 static const char *close_file_dialog_name = "file_close_popup";
3018
3019 static uiBlock *block_create__close_file_dialog(struct bContext *C, struct ARegion *ar, void *arg1)
3020 {
3021   wmGenericCallback *post_action = (wmGenericCallback *)arg1;
3022   Main *bmain = CTX_data_main(C);
3023
3024   uiStyle *style = UI_style_get();
3025   uiBlock *block = UI_block_begin(C, ar, close_file_dialog_name, UI_EMBOSS);
3026
3027   UI_block_flag_enable(
3028       block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT);
3029   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
3030   UI_block_emboss_set(block, UI_EMBOSS);
3031
3032   uiLayout *layout = UI_block_layout(block,
3033                                      UI_LAYOUT_VERTICAL,
3034                                      UI_LAYOUT_PANEL,
3035                                      10,
3036                                      2,
3037                                      U.widget_unit * 24,
3038                                      U.widget_unit * 6,
3039                                      0,
3040                                      style);
3041
3042   /* Title */
3043   bool blend_file_is_saved = BKE_main_blendfile_path(bmain)[0] != '\0';
3044   if (blend_file_is_saved) {
3045     uiItemL(layout, "This file has unsaved changes.", ICON_INFO);
3046   }
3047   else {
3048     uiItemL(layout, "This file has not been saved yet.", ICON_INFO);
3049   }
3050
3051   /* Image Saving */
3052   ReportList reports;
3053   BKE_reports_init(&reports, RPT_STORE);
3054   uint modified_images_count = ED_image_save_all_modified_info(C, &reports);
3055
3056   if (modified_images_count > 0) {
3057     char message[64];
3058     BLI_snprintf(message,
3059                  sizeof(message),
3060                  (modified_images_count == 1) ? "Save %u modified image" :
3061                                                 "Save %u modified images",
3062                  modified_images_count);
3063     uiDefButBitC(block,
3064                  UI_BTYPE_CHECKBOX,
3065                  1,
3066                  0,
3067                  message,
3068                  0,
3069                  0,
3070                  0,
3071                  UI_UNIT_Y,
3072                  &save_images_when_file_is_closed,
3073                  0,
3074                  0,
3075                  0,
3076                  0,
3077                  "");
3078
3079     LISTBASE_FOREACH (Report *, report, &reports.list) {
3080       uiItemL(layout, report->message, ICON_ERROR);
3081     }
3082   }
3083
3084   BKE_reports_clear(&reports);
3085
3086   uiItemL(layout, "", ICON_NONE);
3087
3088   /* Buttons */
3089 #ifdef _WIN32
3090   const bool windows_layout = true;
3091 #else
3092   const bool windows_layout = false;
3093 #endif
3094
3095   uiLayout *split = uiLayoutSplit(layout, 0.0f, true);
3096
3097   if (windows_layout) {
3098     /* Windows standard layout. */
3099     uiLayout *col = uiLayoutColumn(split, false);
3100     uiItemS(col);
3101
3102     col = uiLayoutColumn(split, false);
3103     wm_block_file_close_save_button(block, post_action);
3104
3105     col = uiLayoutColumn(split, false);
3106     wm_block_file_close_discard_button(block, post_action);
3107
3108     col = uiLayoutColumn(split, false);
3109     wm_block_file_close_cancel_button(block, post_action);
3110   }
3111   else {
3112     /* macOS and Linux standard layout. */
3113     uiLayout *col = uiLayoutColumn(split, false);
3114     wm_block_file_close_discard_button(block, post_action);
3115
3116     col = uiLayoutColumn(split, false);
3117     uiItemS(col);
3118
3119     col = uiLayoutColumn(split, false);
3120     wm_block_file_close_cancel_button(block, post_action);
3121
3122     col = uiLayoutColumn(split, false);
3123     wm_block_file_close_save_button(block, post_action);
3124   }
3125
3126   UI_block_bounds_set_centered(block, 10);
3127   return block;
3128 }
3129
3130 static void free_post_file_close_action(void *arg)
3131 {
3132   wmGenericCallback *action = (wmGenericCallback *)arg;
3133   WM_generic_callback_free(action);
3134 }
3135
3136 void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action)
3137 {
3138   if (!UI_popup_block_name_exists(C, close_file_dialog_name)) {
3139     UI_popup_block_invoke(
3140         C, block_create__close_file_dialog, post_action, free_post_file_close_action);
3141   }
3142   else {
3143     WM_generic_callback_free(post_action);
3144   }
3145 }
3146
3147 bool wm_file_or_image_is_modified(const bContext *C)
3148 {
3149   wmWindowManager *wm = CTX_wm_manager(C);
3150   return !wm->file_saved || ED_image_should_save_modified(C);
3151 }
3152
3153 /** \} */