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