Merge branch 'master' into blender2.8
[blender.git] / source / blender / windowmanager / intern / wm_files.c
index effd1c890773c5feb051f07b8a3c31c466b7b3c8..8c263461af6ce9ec3e6c2be787404bec13f1caae 100644 (file)
@@ -70,6 +70,7 @@
 #include "DNA_scene_types.h"
 #include "DNA_screen_types.h"
 #include "DNA_windowmanager_types.h"
+#include "DNA_workspace_types.h"
 
 #include "BKE_appdir.h"
 #include "BKE_autoexec.h"
@@ -77,9 +78,7 @@
 #include "BKE_blendfile.h"
 #include "BKE_blender_undo.h"
 #include "BKE_context.h"
-#include "BKE_depsgraph.h"
 #include "BKE_global.h"
-#include "BKE_library.h"
 #include "BKE_main.h"
 #include "BKE_packedFile.h"
 #include "BKE_report.h"
@@ -87,6 +86,7 @@
 #include "BKE_scene.h"
 #include "BKE_screen.h"
 #include "BKE_undo_system.h"
+#include "BKE_workspace.h"
 
 #include "BLO_readfile.h"
 #include "BLO_writefile.h"
 #include "BPY_extern.h"
 #endif
 
+#include "DEG_depsgraph.h"
+
 #include "WM_api.h"
 #include "WM_types.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+
 #include "wm.h"
 #include "wm_files.h"
 #include "wm_window.h"
@@ -162,7 +167,7 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist)
                        CTX_wm_window_set(C, win);  /* needed by operator close callbacks */
                        WM_event_remove_handlers(C, &win->handlers);
                        WM_event_remove_handlers(C, &win->modalhandlers);
-                       ED_screen_exit(C, win, win->screen);
+                       ED_screen_exit(C, win, WM_window_get_active_screen(win));
                }
        }
 
@@ -177,36 +182,26 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist)
        CTX_wm_menu_set(C, NULL);
 
        ED_editors_exit(C);
-
-       /* just had return; here from r12991, this code could just get removed?*/
-#if 0
-       if (wm == NULL) return;
-       if (G.fileflags & G_FILE_NO_UI) return;
-
-       /* we take apart the used screens from non-active window */
-       for (win = wm->windows.first; win; win = win->next) {
-               BLI_strncpy(win->screenname, win->screen->id.name, MAX_ID_NAME);
-               if (win != wm->winactive) {
-                       BLI_remlink(&G_MAIN->screen, win->screen);
-                       //BLI_addtail(screenbase, win->screen);
-               }
-       }
-#endif
 }
 
-static void wm_window_substitute_old(wmWindowManager *wm, wmWindow *oldwin, wmWindow *win)
+static void wm_window_substitute_old(wmWindowManager *oldwm, wmWindowManager *wm, wmWindow *oldwin, wmWindow *win)
 {
        win->ghostwin = oldwin->ghostwin;
-       win->multisamples = oldwin->multisamples;
+       win->gpuctx = oldwin->gpuctx;
        win->active = oldwin->active;
-       if (win->active)
+       if (win->active) {
                wm->winactive = win;
+       }
+       if (oldwm->windrawable == oldwin) {
+               oldwm->windrawable = NULL;
+               wm->windrawable = win;
+       }
 
        if (!G.background) /* file loading in background mode still calls this */
                GHOST_SetWindowUserData(win->ghostwin, win);    /* pointer back */
 
        oldwin->ghostwin = NULL;
-       oldwin->multisamples = 0;
+       oldwin->gpuctx = NULL;
 
        win->eventstate = oldwin->eventstate;
        oldwin->eventstate = NULL;
@@ -218,102 +213,132 @@ static void wm_window_substitute_old(wmWindowManager *wm, wmWindow *oldwin, wmWi
        win->posy = oldwin->posy;
 }
 
-/* match old WM with new, 4 cases:
- * 1- no current wm, no read wm: make new default
- * 2- no current wm, but read wm: that's OK, do nothing
- * 3- current wm, but not in file: try match screen names
- * 4- current wm, and wm in file: try match ghostwin
- */
-
-static void wm_window_match_do(Main *bmain, bContext *C, ListBase *oldwmlist)
+static void wm_window_match_keep_current_wm(
+        const bContext *C, ListBase *current_wm_list,
+        const bool load_ui,
+        ListBase *r_new_wm_list)
 {
-       wmWindowManager *oldwm, *wm;
-       wmWindow *oldwin, *win;
-
-       /* cases 1 and 2 */
-       if (BLI_listbase_is_empty(oldwmlist)) {
-               if (bmain->wm.first) {
-                       /* nothing todo */
-               }
-               else {
-                       wm_add_default(C);
-               }
-       }
-       else {
-               /* cases 3 and 4 */
-
-               /* we've read file without wm..., keep current one entirely alive */
-               if (BLI_listbase_is_empty(&bmain->wm)) {
-                       bScreen *screen = NULL;
+       Main *bmain = CTX_data_main(C);
+       wmWindowManager *wm = current_wm_list->first;
+       bScreen *screen = NULL;
 
-                       /* when loading without UI, no matching needed */
-                       if (!(G.fileflags & G_FILE_NO_UI) && (screen = CTX_wm_screen(C))) {
+       /* match oldwm to new dbase, only old files */
+       wm->initialized &= ~WM_WINDOW_IS_INITIALIZED;
 
-                               /* match oldwm to new dbase, only old files */
-                               for (wm = oldwmlist->first; wm; wm = wm->id.next) {
+       /* when loading without UI, no matching needed */
+       if (load_ui && (screen = CTX_wm_screen(C))) {
+               for (wmWindow *win = wm->windows.first; win; win = win->next) {
+                       WorkSpace *workspace;
 
-                                       for (win = wm->windows.first; win; win = win->next) {
-                                               /* all windows get active screen from file */
-                                               if (screen->winid == 0)
-                                                       win->screen = screen;
-                                               else
-                                                       win->screen = ED_screen_duplicate(bmain, win, screen);
+                       BKE_workspace_layout_find_global(bmain, screen, &workspace);
+                       BKE_workspace_active_set(win->workspace_hook, workspace);
+                       win->scene = CTX_data_scene(C);
 
-                                               BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname));
-                                               win->screen->winid = win->winid;
-                                       }
-                               }
+                       /* all windows get active screen from file */
+                       if (screen->winid == 0) {
+                               WM_window_set_active_screen(win, workspace, screen);
                        }
+                       else {
+                               WorkSpaceLayout *layout_old = WM_window_get_active_layout(win);
+                               WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(bmain, workspace, layout_old, win);
 
-                       bmain->wm = *oldwmlist;
+                               WM_window_set_active_layout(win, workspace, layout_new);
+                       }
 
-                       /* screens were read from file! */
-                       ED_screens_initialize(bmain, bmain->wm.first);
+                       bScreen *win_screen = WM_window_get_active_screen(win);
+                       win_screen->winid = win->winid;
                }
-               else {
-                       bool has_match = false;
+       }
+
+       *r_new_wm_list = *current_wm_list;
+}
 
-                       /* what if old was 3, and loaded 1? */
-                       /* this code could move to setup_appdata */
-                       oldwm = oldwmlist->first;
-                       wm = bmain->wm.first;
+static void wm_window_match_replace_by_file_wm(
+        bContext *C, ListBase *current_wm_list, ListBase *readfile_wm_list,
+        ListBase *r_new_wm_list)
+{
+       wmWindowManager *oldwm = current_wm_list->first;
+       wmWindowManager *wm = readfile_wm_list->first; /* will become our new WM */
+       bool has_match = false;
 
-                       /* preserve key configurations in new wm, to preserve their keymaps */
-                       wm->keyconfigs = oldwm->keyconfigs;
-                       wm->addonconf = oldwm->addonconf;
-                       wm->defaultconf = oldwm->defaultconf;
-                       wm->userconf = oldwm->userconf;
+       /* this code could move to setup_appdata */
 
-                       BLI_listbase_clear(&oldwm->keyconfigs);
-                       oldwm->addonconf = NULL;
-                       oldwm->defaultconf = NULL;
-                       oldwm->userconf = NULL;
+       /* preserve key configurations in new wm, to preserve their keymaps */
+       wm->keyconfigs = oldwm->keyconfigs;
+       wm->addonconf = oldwm->addonconf;
+       wm->defaultconf = oldwm->defaultconf;
+       wm->userconf = oldwm->userconf;
 
-                       /* ensure making new keymaps and set space types */
-                       wm->initialized = 0;
-                       wm->winactive = NULL;
+       BLI_listbase_clear(&oldwm->keyconfigs);
+       oldwm->addonconf = NULL;
+       oldwm->defaultconf = NULL;
+       oldwm->userconf = NULL;
 
-                       /* only first wm in list has ghostwins */
-                       for (win = wm->windows.first; win; win = win->next) {
-                               for (oldwin = oldwm->windows.first; oldwin; oldwin = oldwin->next) {
+       /* ensure making new keymaps and set space types */
+       wm->initialized = 0;
+       wm->winactive = NULL;
 
-                                       if (oldwin->winid == win->winid) {
-                                               has_match = true;
+       /* Clearing drawable of before deleting any context
+        * to avoid clearing the wrong wm. */
+       wm_window_clear_drawable(oldwm);
 
-                                               wm_window_substitute_old(wm, oldwin, win);
-                                       }
-                               }
+       /* only first wm in list has ghostwins */
+       for (wmWindow *win = wm->windows.first; win; win = win->next) {
+               for (wmWindow *oldwin = oldwm->windows.first; oldwin; oldwin = oldwin->next) {
+                       if (oldwin->winid == win->winid) {
+                               has_match = true;
+
+                               wm_window_substitute_old(oldwm, wm, oldwin, win);
                        }
+               }
+       }
+       /* make sure at least one window is kept open so we don't lose the context, check T42303 */
+       if (!has_match) {
+               wm_window_substitute_old(oldwm, wm, oldwm->windows.first, wm->windows.first);
+       }
 
-                       /* make sure at least one window is kept open so we don't lose the context, check T42303 */
-                       if (!has_match) {
-                               oldwin = oldwm->windows.first;
-                               win = wm->windows.first;
+       wm_close_and_free_all(C, current_wm_list);
 
-                               wm_window_substitute_old(wm, oldwin, win);
-                       }
+       *r_new_wm_list = *readfile_wm_list;
+}
 
-                       wm_close_and_free_all(C, oldwmlist);
+/**
+ * Match old WM with new, 4 cases:
+ * 1) No current WM, no WM in file: Make new default.
+ * 2) No current WM, but WM in file: Keep current WM, do nothing else.
+ * 3) Current WM, but not in file: Keep current WM, update windows with screens from file.
+ * 4) Current WM, and WM in file: Try to keep current GHOST windows, use WM from file.
+ *
+ * \param r_new_wm_list: Return argument for the wm list to be used from now on.
+ */
+static void wm_window_match_do(
+        bContext *C,
+        ListBase *current_wm_list, ListBase *readfile_wm_list,
+        ListBase *r_new_wm_list)
+{
+       if (BLI_listbase_is_empty(current_wm_list)) {
+               /* case 1 */
+               if (BLI_listbase_is_empty(readfile_wm_list)) {
+                       Main *bmain = CTX_data_main(C);
+                       /* Neither current, no newly read file have a WM -> add the default one. */
+                       wm_add_default(bmain, C);
+                       *r_new_wm_list = bmain->wm;
+               }
+               /* case 2 */
+               else {
+                       *r_new_wm_list = *readfile_wm_list;
+               }
+       }
+       else {
+               /* case 3 */
+               if (BLI_listbase_is_empty(readfile_wm_list)) {
+                       /* We've read file without wm, keep current one entirely alive.
+                        * Happens when reading pre 2.5 files (no WM back then) */
+                       wm_window_match_keep_current_wm(C, current_wm_list, (G.fileflags & G_FILE_NO_UI) == 0, r_new_wm_list);
+               }
+               /* case 4 */
+               else {
+                       wm_window_match_replace_by_file_wm(C, current_wm_list, readfile_wm_list, r_new_wm_list);
                }
        }
 }
@@ -450,9 +475,8 @@ void wm_file_read_report(bContext *C, Main *bmain)
  * Logic shared between #WM_file_read & #wm_homefile_read,
  * updates to make after reading a file.
  */
-static void wm_file_read_post(bContext *C, const bool is_startup_file, const bool use_userdef)
+static void wm_file_read_post(bContext *C, const bool is_startup_file, const bool reset_app_template)
 {
-       Main *bmain = CTX_data_main(C);
        bool addons_loaded = false;
        wmWindowManager *wm = CTX_wm_manager(C);
 
@@ -463,14 +487,17 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo
 
        CTX_wm_window_set(C, wm->windows.first);
 
+       Main *bmain = CTX_data_main(C);
+       DEG_on_visible_update(bmain, true);
+       wm_event_do_depsgraph(C);
+
        ED_editors_init(C);
-       DAG_on_visible_update(bmain, true);
 
 #ifdef WITH_PYTHON
        if (is_startup_file) {
                /* possible python hasn't been initialized */
                if (CTX_py_init_get(C)) {
-                       if (use_userdef) {
+                       if (reset_app_template) {
                                /* Only run when we have a template path found. */
                                if (BKE_appdir_app_template_any()) {
                                        BPY_execute_string(
@@ -492,7 +519,7 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo
                addons_loaded = true;
        }
 #else
-       UNUSED_VARS(use_userdef);
+       UNUSED_VARS(is_startup_file, reset_app_template);
 #endif  /* WITH_PYTHON */
 
        WM_operatortype_last_properties_clear_all();
@@ -501,16 +528,11 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo
        BLI_callback_exec(bmain, NULL, BLI_CB_EVT_VERSION_UPDATE);
        BLI_callback_exec(bmain, NULL, BLI_CB_EVT_LOAD_POST);
 
-       /* Would otherwise be handled by event loop.
-        *
-        * Disabled for startup file, since it causes problems when PyDrivers are used in the startup file.
-        * While its possible state of startup file may be wrong,
-        * in this case users nearly always load a file to replace the startup file. */
-       if (G.background && (is_startup_file == false)) {
-               BKE_scene_update_tagged(bmain->eval_ctx, bmain, CTX_data_scene(C));
-       }
-
+#if 1
        WM_event_add_notifier(C, NC_WM | ND_FILEREAD, NULL);
+#else
+       WM_msg_publish_static(CTX_wm_message_bus(C), WM_MSG_STATICTYPE_FILE_READ);
+#endif
 
        /* report any errors.
         * currently disabled if addons aren't yet loaded */
@@ -534,6 +556,9 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo
                 * a blend file and do anything since the screen
                 * won't be set to a valid value again */
                CTX_wm_window_set(C, NULL); /* exits queues */
+
+               /* Ensure tools are registered. */
+               WM_toolsystem_init(C);
        }
 }
 
@@ -594,7 +619,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
                }
 
                /* match the read WM with current WM */
-               wm_window_match_do(bmain, C, &wmbase);
+               wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm);
                WM_check(C); /* opens window(s), checks keymaps */
 
                if (retval == BKE_BLENDFILE_READ_OK_USERPREFS) {
@@ -612,6 +637,10 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
 
                success = true;
        }
+#if 0
+       else if (retval == BKE_READ_EXOTIC_OK_OTHER)
+               BKE_undo_write(C, "Import file");
+#endif
        else if (retval == BKE_READ_EXOTIC_FAIL_OPEN) {
                BKE_reportf(reports, RPT_ERROR, "Cannot read file '%s': %s", filepath,
                            errno ? strerror(errno) : TIP_("unable to open the file"));
@@ -673,6 +702,25 @@ const char *WM_init_state_app_template_get(void)
        return wm_init_state_app_template.override ? wm_init_state_app_template.app_template : NULL;
 }
 
+
+static bool wm_app_template_has_userpref(const char *app_template)
+{
+       /* Test if app template provides a userpref.blend. If not, we will
+        * share user preferences with the rest of Blender. */
+       if (!app_template && app_template[0]) {
+               return false;
+       }
+
+       char app_template_path[FILE_MAX];
+       if (!BKE_appdir_app_template_id_search(app_template, app_template_path, sizeof(app_template_path))) {
+               return false;
+       }
+
+       char userpref_path[FILE_MAX];
+       BLI_path_join(userpref_path, sizeof(userpref_path), app_template_path, BLENDER_USERPREF_FILE, NULL);
+       return BLI_exists(userpref_path);
+}
+
 /**
  * Called on startup, (context entirely filled with NULLs)
  * or called for 'New File' both startup.blend and userpref.blend are checked.
@@ -767,6 +815,8 @@ void wm_homefile_read(
        }
 
        const char *app_template = NULL;
+       bool update_defaults = false;
+       bool reset_app_template = false;
 
        if (filepath_startup_override != NULL) {
                /* pass */
@@ -779,6 +829,14 @@ void wm_homefile_read(
                app_template = U.app_template;
        }
 
+       if ((!app_template && U.app_template[0]) ||
+           (app_template && !STREQ(app_template, U.app_template)))
+       {
+               /* Always load UI when switching to another template. */
+               G.fileflags &= ~G_FILE_NO_UI;
+               reset_app_template = true;
+       }
+
        if ((app_template != NULL) && (app_template[0] != '\0')) {
                if (!BKE_appdir_app_template_id_search(app_template, app_template_system, sizeof(app_template_system))) {
                        /* Can safely continue with code below, just warn it's not found. */
@@ -802,6 +860,9 @@ void wm_homefile_read(
 
                if (filepath_startup[0] == '\0') {
                        BLI_path_join(filepath_startup, sizeof(filepath_startup), app_template_system, BLENDER_STARTUP_FILE, NULL);
+
+                       /* Update defaults only for system templates. */
+                       update_defaults = true;
                }
        }
 
@@ -820,6 +881,9 @@ void wm_homefile_read(
                                printf("\nNote: No (valid) '%s' found, fall back to built-in default.\n\n", filepath_startup);
                        success = false;
                }
+               if (success && update_defaults) {
+                       BLO_update_defaults_startup_blend(CTX_data_main(C), app_template);
+               }
        }
 
        if (success == false && filepath_startup_override && reports) {
@@ -900,27 +964,30 @@ void wm_homefile_read(
        if (use_userdef) {
                /* check userdef before open window, keymaps etc */
                wm_init_userdef(bmain, read_userdef_from_memory);
+               reset_app_template = true;
        }
 
        /* match the read WM with current WM */
-       wm_window_match_do(bmain, C, &wmbase);
+       wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm);
+
+       if (use_factory_settings) {
+               /*  Clear keymaps because the current default keymap may have been initialized from user preferences,
+                *  which have been reset. */
+               for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
+                       if (wm->defaultconf) {
+                               wm->defaultconf->flag &= ~KEYCONF_INIT_DEFAULT;
+                       }
+               }
+       }
+
        WM_check(C); /* opens window(s), checks keymaps */
 
        bmain->name[0] = '\0';
 
-       if (use_userdef) {
-               /* When loading factory settings, the reset solid OpenGL lights need to be applied. */
-               if (!G.background) {
-                       GPU_default_lights();
-               }
-       }
-
        /* start with save preference untitled.blend */
        G.save_over = 0;
-       /* disable auto-play in startup.blend... */
-       G.fileflags &= ~G_FILE_AUTOPLAY;
 
-       wm_file_read_post(C, true, use_userdef);
+       wm_file_read_post(C, true, reset_app_template);
 }
 
 /** \name WM History File API
@@ -1049,7 +1116,7 @@ static void wm_history_file_update(void)
 
 
 /* screen can be NULL */
-static ImBuf *blend_file_thumb(Main *bmain, Scene *scene, bScreen *screen, BlendThumbnail **thumb_pt)
+static ImBuf *blend_file_thumb(const bContext *C, Scene *scene, bScreen *screen, BlendThumbnail **thumb_pt)
 {
        /* will be scaled down, but gives some nice oversampling */
        ImBuf *ibuf;
@@ -1084,19 +1151,21 @@ static ImBuf *blend_file_thumb(Main *bmain, Scene *scene, bScreen *screen, Blend
        }
 
        /* gets scaled to BLEN_THUMB_SIZE */
+       Depsgraph *depsgraph = CTX_data_depsgraph(C);
+
        if (scene->camera) {
                ibuf = ED_view3d_draw_offscreen_imbuf_simple(
-                       bmain, scene, scene->camera,
+                       depsgraph, scene, OB_SOLID, scene->camera,
                        BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
-                       IB_rect, V3D_OFSDRAW_NONE, OB_SOLID, R_ALPHAPREMUL, 0, NULL,
-                       NULL, NULL, err_out);
+                       IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL,
+                       NULL, err_out);
        }
        else {
                ibuf = ED_view3d_draw_offscreen_imbuf(
-                       bmain, scene, v3d, ar,
+                       depsgraph, scene, OB_SOLID, v3d, ar,
                        BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
                        IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL,
-                       NULL, NULL, err_out);
+                       NULL, err_out);
        }
 
        if (ibuf) {
@@ -1187,10 +1256,10 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo
 
        /* blend file thumbnail */
        /* save before exit_editmode, otherwise derivedmeshes for shared data corrupt #27765) */
-       /* Main now can store a .blend thumbnail, usefull for background mode or thumbnail customization. */
+       /* Main now can store a .blend thumbnail, useful for background mode or thumbnail customization. */
        main_thumb = thumb = bmain->blen_thumb;
        if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) {
-               ibuf_thumb = blend_file_thumb(bmain, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
+               ibuf_thumb = blend_file_thumb(C, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
        }
 
        /* operator now handles overwrite checks */
@@ -1226,7 +1295,6 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo
                }
 
                SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
-               SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_AUTOPLAY, G_FILE_AUTOPLAY);
 
                /* prevent background mode scripts from clobbering history */
                if (do_history) {
@@ -1335,7 +1403,7 @@ void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *UNUSED(w
        }
        else {
                /*  save as regular blend file */
-               int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY);
+               int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY);
 
                ED_editors_flush_edits(C, false);
 
@@ -1451,7 +1519,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
        BLI_callback_exec(bmain, NULL, BLI_CB_EVT_SAVE_PRE);
 
        /* check current window and close it if temp */
-       if (win && win->screen->temp)
+       if (win && WM_window_is_temp_screen(win))
                wm_window_close(C, wm, win);
 
        /* update keymaps in user preferences */
@@ -1459,12 +1527,12 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op)
 
        BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_STARTUP_FILE, NULL);
 
-       printf("trying to save homefile at %s ", filepath);
+       printf("Writing homefile: '%s' ", filepath);
 
        ED_editors_flush_edits(C, false);
 
        /*  force save as regular blend file */
-       fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY);
+       fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY);
 
        if (BLO_write_file(bmain, filepath, fileflags | G_FILE_USERPREFS, op->reports, NULL) == 0) {
                printf("fail\n");
@@ -1484,7 +1552,7 @@ void WM_OT_save_homefile(wmOperatorType *ot)
 {
        ot->name = "Save Startup File";
        ot->idname = "WM_OT_save_homefile";
-       ot->description = "Make the current file the default .blend file, includes preferences";
+       ot->description = "Make the current file the default .blend file";
 
        ot->invoke = WM_operator_confirm;
        ot->exec = wm_homefile_write_exec;
@@ -1538,6 +1606,7 @@ static int wm_userpref_write_exec(bContext *C, wmOperator *op)
        char filepath[FILE_MAX];
        const char *cfgdir;
        bool ok = true;
+       bool use_template_userpref = wm_app_template_has_userpref(U.app_template);
 
        /* update keymaps in user preferences */
        WM_keyconfig_update(wm);
@@ -1545,9 +1614,9 @@ static int wm_userpref_write_exec(bContext *C, wmOperator *op)
        if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL))) {
                bool ok_write;
                BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL);
-               printf("trying to save userpref at %s ", filepath);
 
-               if (U.app_template[0]) {
+               printf("Writing userprefs: '%s' ", filepath);
+               if (use_template_userpref) {
                        ok_write = BKE_blendfile_userdef_write_app_template(filepath, op->reports);
                }
                else {
@@ -1566,11 +1635,12 @@ static int wm_userpref_write_exec(bContext *C, wmOperator *op)
                BKE_report(op->reports, RPT_ERROR, "Unable to create userpref path");
        }
 
-       if (U.app_template[0]) {
+       if (use_template_userpref) {
                if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, U.app_template))) {
                        /* Also save app-template prefs */
                        BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL);
-                       printf("trying to save app-template userpref at %s ", filepath);
+
+                       printf("Writing userprefs app-template: '%s' ", filepath);
                        if (BKE_blendfile_userdef_write(filepath, op->reports) != 0) {
                                printf("ok\n");
                        }
@@ -1590,7 +1660,7 @@ static int wm_userpref_write_exec(bContext *C, wmOperator *op)
 
 void WM_OT_save_userpref(wmOperatorType *ot)
 {
-       ot->name = "Save User Settings";
+       ot->name = "Save Preferences";
        ot->idname = "WM_OT_save_userpref";
        ot->description = "Save user preferences separately, overrides startup file preferences";
 
@@ -1659,8 +1729,9 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op)
                RNA_property_string_get(op->ptr, prop_app_template, app_template_buf);
                app_template = app_template_buf;
 
-               /* Always load preferences when switching templates. */
-               use_userdef = true;
+               /* Always load preferences when switching templates with own preferences. */
+               use_userdef = wm_app_template_has_userpref(app_template) ||
+                             wm_app_template_has_userpref(U.app_template);
 
                /* Turn override off, since we're explicitly loading a different app-template. */
                WM_init_state_app_template_set(NULL);
@@ -1677,6 +1748,22 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
+static int wm_homefile_read_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
+{
+       /* Draw menu which includes default startup and application templates. */
+       uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("New File"), ICON_FILE_NEW);
+       uiLayout *layout = UI_popup_menu_layout(pup);
+
+       MenuType *mt = WM_menutype_find("TOPBAR_MT_file_new", false);
+       if (mt) {
+               UI_menutype_draw(C, mt, layout);
+       }
+
+       UI_popup_menu_end(C, pup);
+
+       return OPERATOR_INTERFACE;
+}
+
 void WM_OT_read_homefile(wmOperatorType *ot)
 {
        PropertyRNA *prop;
@@ -1684,7 +1771,7 @@ void WM_OT_read_homefile(wmOperatorType *ot)
        ot->idname = "WM_OT_read_homefile";
        ot->description = "Open the default file (doesn't save the current file)";
 
-       ot->invoke = WM_operator_confirm;
+       ot->invoke = wm_homefile_read_invoke;
        ot->exec = wm_homefile_read_exec;
 
        prop = RNA_def_string_file_path(ot->srna, "filepath", NULL,
@@ -2259,3 +2346,121 @@ void WM_OT_save_mainfile(wmOperatorType *ot)
 }
 
 /** \} */
+
+/** \name Auto-execution of scripts warning popup
+ *
+ * \{ */
+
+static void wm_block_autorun_warning_ignore(bContext *C, void *arg_block, void *UNUSED(arg))
+{
+       wmWindow *win = CTX_wm_window(C);
+       UI_popup_block_close(C, win, arg_block);
+}
+
+static void wm_block_autorun_warning_allow(bContext *C, void *arg_block, void *UNUSED(arg))
+{
+       PointerRNA props_ptr;
+       wmWindow *win = CTX_wm_window(C);
+
+       UI_popup_block_close(C, win, arg_block);
+
+       /* Save user preferences for permanent execution. */
+       if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
+               WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, NULL);
+       }
+
+       /* Load file again with scripts enabled. */
+       wmOperatorType *ot = WM_operatortype_find("WM_OT_revert_mainfile", false);
+
+       WM_operator_properties_create_ptr(&props_ptr, ot);
+       RNA_boolean_set(&props_ptr, "use_scripts", true);
+       WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &props_ptr);
+       WM_operator_properties_free(&props_ptr);
+}
+
+/* Build the autorun warning dialog UI */
+static uiBlock *block_create_autorun_warning(struct bContext *C, struct ARegion *ar, void *UNUSED(arg1))
+{
+       uiStyle *style = UI_style_get();
+       uiBlock *block = UI_block_begin(C, ar, "autorun_warning_popup", UI_EMBOSS);
+
+       UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT);
+       UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
+       UI_block_emboss_set(block, UI_EMBOSS);
+
+       uiLayout *layout = UI_block_layout(
+               block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 10, 2, U.widget_unit * 24, U.widget_unit * 6, 0, style);
+
+       /* Text and some vertical space */
+       uiLayout *col = uiLayoutColumn(layout, true);
+       uiItemL(col, IFACE_("For security reasons, automatic execution of Python scripts in this file was disabled:"), ICON_ERROR);
+       uiLayout *sub = uiLayoutRow(col, true);
+       uiLayoutSetRedAlert(sub, true);
+       uiItemL(sub, G.autoexec_fail, ICON_BLANK1);
+       uiItemL(col, IFACE_("This may lead to unexpected behavior."), ICON_BLANK1);
+
+       uiItemS(layout);
+
+       PointerRNA userpref_ptr;
+       RNA_pointer_create(NULL, &RNA_UserPreferencesSystem, &U, &userpref_ptr);
+       uiItemR(layout, &userpref_ptr, "use_scripts_auto_execute", 0, IFACE_("Permanently allow execution of scripts"), ICON_NONE);
+
+       uiItemS(layout);
+
+       /* Buttons */
+       uiBut *but;
+       uiLayout *split = uiLayoutSplit(layout, 0.0f, true);
+       col = uiLayoutColumn(split, false);
+
+       /* Allow reload if we have a saved file. */
+       if (G.relbase_valid) {
+               but = uiDefIconTextBut(
+                       block, UI_BTYPE_BUT, 0, ICON_NONE, IFACE_("Allow Execution"), 0, 0, 50, UI_UNIT_Y,
+                       NULL, 0, 0, 0, 0, TIP_("Reload file with execution of Python scripts enabled"));
+               UI_but_func_set(but, wm_block_autorun_warning_allow, block, NULL);
+       }
+       else {
+               uiItemS(col);
+       }
+
+       /* empty space between buttons */
+       col = uiLayoutColumn(split, false);
+       uiItemS(col);
+
+       col = uiLayoutColumn(split, 1);
+       but = uiDefIconTextBut(
+               block, UI_BTYPE_BUT, 0, ICON_NONE, IFACE_("Ignore"), 0, 0, 50, UI_UNIT_Y,
+               NULL, 0, 0, 0, 0, TIP_("Continue using file without Python scripts"));
+       UI_but_func_set(but, wm_block_autorun_warning_ignore, block, NULL);
+
+       UI_block_bounds_set_centered(block, 10);
+
+       return block;
+}
+
+void wm_test_autorun_warning(bContext *C)
+{
+       /* Test if any auto-execution of scripts failed. */
+       if ((G.f & G_SCRIPT_AUTOEXEC_FAIL) == 0) {
+               return;
+       }
+
+       /* Only show the warning once. */
+       if (G.f & G_SCRIPT_AUTOEXEC_FAIL_QUIET) {
+               return;
+       }
+
+       G.f |= G_SCRIPT_AUTOEXEC_FAIL_QUIET;
+
+       wmWindowManager *wm = CTX_wm_manager(C);
+       wmWindow *win = (wm->winactive) ? wm->winactive : wm->windows.first;
+
+       if (win) {
+               wmWindow *prevwin = CTX_wm_window(C);
+               CTX_wm_window_set(C, win);
+               UI_popup_block_invoke(C, block_create_autorun_warning, NULL);
+               CTX_wm_window_set(C, prevwin);
+       }
+}
+
+/** \} */