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