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