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