Blender V5.0
wm_files.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
12/* Placed up here because of crappy WINSOCK stuff. */
13#include <cerrno>
14#include <cstddef>
15#include <cstring>
16#include <fcntl.h> /* For open flags (#O_BINARY, #O_RDONLY). */
17
18#ifdef WIN32
19/* Need to include windows.h so _WIN32_IE is defined. */
20# include <windows.h>
21# ifndef _WIN32_IE
22/* Minimal requirements for SHGetSpecialFolderPath on MINGW MSVC has this defined already. */
23# define _WIN32_IE 0x0400
24# endif
25/* For #SHGetSpecialFolderPath, has to be done before `BLI_winstuff.h`
26 * because 'near' is disabled through `BLI_windstuff.h`. */
27# include "BLI_winstuff.h"
28# include <shlobj.h>
29#endif
30
31#include <fmt/format.h>
32
34#include "MEM_guardedalloc.h"
35
36#include "BLI_fileops.h"
37#include "BLI_filereader.h"
38#include "BLI_linklist.h"
39#include "BLI_listbase.h"
40#include "BLI_math_base.h"
41#include "BLI_math_time.h"
42#include "BLI_memory_cache.hh"
43#include "BLI_string.h"
44#include "BLI_string_utf8.h"
45#include "BLI_system.h"
46#include "BLI_threads.h"
47#include "BLI_time.h"
48#include "BLI_timer.h"
49#include "BLI_utildefines.h"
50#include BLI_SYSTEM_PID_H
51
52#include "BLO_readfile.hh"
53#include "BLT_translation.hh"
54
55#include "BLF_api.hh"
56
57#include "DNA_scene_types.h"
58#include "DNA_screen_types.h"
59#include "DNA_space_types.h"
60#include "DNA_userdef_types.h"
62#include "DNA_workspace_types.h"
63
64#include "AS_asset_library.hh"
65
66#ifndef WITH_CYCLES
67# include "BKE_addon.h"
68#endif
69#include "BKE_appdir.hh"
70#include "BKE_autoexec.hh"
71#include "BKE_blender.hh"
72#include "BKE_blender_version.h"
73#include "BKE_blendfile.hh"
74#include "BKE_callbacks.hh"
75#include "BKE_context.hh"
76#include "BKE_global.hh"
77#include "BKE_idprop.hh"
78#include "BKE_lib_id.hh"
79#include "BKE_lib_override.hh"
80#include "BKE_lib_remap.hh"
81#include "BKE_library.hh"
82#include "BKE_main.hh"
83#include "BKE_main_namemap.hh"
84#include "BKE_node.hh"
85#include "BKE_packedFile.hh"
86#include "BKE_report.hh"
87#include "BKE_scene.hh"
88#include "BKE_screen.hh"
89#include "BKE_sound.h"
90#include "BKE_undo_system.hh"
91#include "BKE_workspace.hh"
92
93#include "BLO_writefile.hh"
94
95#include "RNA_access.hh"
96#include "RNA_define.hh"
97
98#include "IMB_imbuf.hh"
99#include "IMB_imbuf_types.hh"
100#include "IMB_metadata.hh"
101#include "IMB_thumbs.hh"
102
103#include "ED_asset.hh"
104#include "ED_datafiles.h"
105#include "ED_fileselect.hh"
106#include "ED_image.hh"
107#include "ED_outliner.hh"
108#include "ED_render.hh"
109#include "ED_screen.hh"
110#include "ED_undo.hh"
111#include "ED_util.hh"
112#include "ED_view3d.hh"
113#include "ED_view3d_offscreen.hh"
114
115#include "NOD_composite.hh"
116
117#include "GHOST_C-api.h"
118#include "GHOST_Path-api.hh"
119
120#include "GPU_context.hh"
121
122#include "SEQ_sequencer.hh"
123
124#include "UI_interface.hh"
125#include "UI_interface_layout.hh"
126#include "UI_resources.hh"
127#include "UI_view2d.hh"
128
129/* Only to report a missing engine. */
130#include "RE_engine.h"
131
132#ifdef WITH_PYTHON
133# include "BPY_extern_python.hh"
134# include "BPY_extern_run.hh"
135#endif
136
137#include "DEG_depsgraph.hh"
138
139#include "WM_api.hh"
140#include "WM_keymap.hh"
141#include "WM_message.hh"
142#include "WM_toolsystem.hh"
143#include "WM_types.hh"
144
145#include "wm.hh"
146#include "wm_event_system.hh"
147#include "wm_files.hh"
148#include "wm_window.hh"
149
150#include "CLG_log.h"
151
152static RecentFile *wm_file_history_find(const char *filepath);
153static void wm_history_file_free(RecentFile *recent);
154static void wm_history_files_free();
155static void wm_history_file_update();
156static void wm_history_file_write();
157
159
160static CLG_LogRef LOG = {"blend"};
161
171#define USE_THUMBNAIL_FAST_DOWNSCALE
172
173/* -------------------------------------------------------------------- */
176
178{
179 wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
180 if (wm->file_saved) {
181 wm->file_saved = 0;
182 /* Notifier that data changed, for save-over warning or header. */
184 }
185}
186
192
194
195/* -------------------------------------------------------------------- */
198
206
212 Main *bmain,
213 const bool is_read_homefile)
214{
215 using namespace blender;
216 BLI_assert(BLI_listbase_count_at_most(&bmain->wm, 2) <= 1);
217 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
219 wm_setup_data->is_read_homefile = is_read_homefile;
220 /* This info is not always known yet when this function is called. */
221 wm_setup_data->is_factory_startup = false;
222
223 if (wm == nullptr) {
224 return wm_setup_data;
225 }
226
227 /* First wrap up running stuff.
228 *
229 * Code copied from `wm_init_exit.cc`. */
231
232 wmWindow *active_win = CTX_wm_window(C);
233 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
234 CTX_wm_window_set(C, win); /* Needed by operator close callbacks. */
235 WM_event_remove_handlers(C, &win->handlers);
236 WM_event_remove_handlers(C, &win->modalhandlers);
238 }
239 /* Reset active window. */
240 CTX_wm_window_set(C, active_win);
241
242 /* NOTE(@ideasman42): Clear the message bus so it's always cleared on file load.
243 * Otherwise it's cleared when "Load UI" is set (see #USER_FILENOUI and #wm_close_and_free).
244 * However it's _not_ cleared when the UI is kept. This complicates use from add-ons
245 * which can re-register subscribers on file-load. To support this use case,
246 * it's best to have predictable behavior - always clear. */
247 if (wm->runtime->message_bus != nullptr) {
248 WM_msgbus_destroy(wm->runtime->message_bus);
249 wm->runtime->message_bus = nullptr;
250 }
251
252 /* XXX Hack! We have to clear context popup-region here, because removing all
253 * #wmWindow::modalhandlers above frees the active menu (at least, in the 'startup splash' case),
254 * causing use-after-free error in later handling of the button callbacks in UI code
255 * (see #ui_apply_but_funcs_after()).
256 * Tried solving this by always nullptr-ing context's menu when setting wm/win/etc.,
257 * but it broke popups refreshing (see #47632),
258 * so for now just handling this specific case here. */
259 CTX_wm_region_popup_set(C, nullptr);
260
261 ED_editors_exit(bmain, true);
262
263 /* Asset loading is done by the UI/editors and they keep pointers into it. So make sure to clear
264 * it after UI/editors. */
267
268 /* NOTE: `wm_setup_data->old_wm` cannot be set here, as this pointer may be swapped with the
269 * newly read one in `setup_app_data` process (See #swap_wm_data_for_blendfile). */
270
271 return wm_setup_data;
272}
273
276 wmWindow *oldwin,
277 wmWindow *win)
278{
279 win->ghostwin = oldwin->ghostwin;
280 win->gpuctx = oldwin->gpuctx;
281 win->active = oldwin->active;
282 if (win->active) {
283 wm->runtime->winactive = win;
284 }
285 if (oldwm->runtime->windrawable == oldwin) {
286 oldwm->runtime->windrawable = nullptr;
287 wm->runtime->windrawable = win;
288 }
289
290 /* File loading in background mode still calls this. */
291 if (!G.background) {
292 /* Pointer back. */
293 GHOST_SetWindowUserData(static_cast<GHOST_WindowHandle>(win->ghostwin), win);
294 }
295
296 oldwin->ghostwin = nullptr;
297 oldwin->gpuctx = nullptr;
298
299 win->eventstate = oldwin->eventstate;
301 oldwin->eventstate = nullptr;
302 oldwin->event_last_handled = nullptr;
303
304 /* Ensure proper screen re-scaling. */
305 win->sizex = oldwin->sizex;
306 win->sizey = oldwin->sizey;
307 win->posx = oldwin->posx;
308 win->posy = oldwin->posy;
309}
310
321 Main *bmain,
322 BlendFileReadWMSetupData *wm_setup_data,
324 const bool load_ui)
325{
326 /* This data is not needed here, besides detecting that old WM has been kept (in caller code).
327 * Since `old_wm` is kept, do not free it, just clear the pointer as clean-up. */
328 wm_setup_data->old_wm = nullptr;
329
330 if (!load_ui) {
331 /* When loading without UI (i.e. keeping existing UI), no matching needed.
332 *
333 * The other UI data (workspaces, layouts, screens) has also been re-used from old Main, and
334 * newly read one from file has already been discarded in #setup_app_data. */
335 return;
336 }
337
338 /* Old WM is being reused, but other UI data (workspaces, layouts, screens) comes from the new
339 * file, so the WM needs to be updated to use these. */
340 bScreen *screen = CTX_wm_screen(C);
341 if (screen != nullptr) {
342 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
343 WorkSpace *workspace;
344
345 WorkSpaceLayout *layout_ref = BKE_workspace_layout_find_global(bmain, screen, &workspace);
346 BKE_workspace_active_set(win->workspace_hook, workspace);
347 win->scene = CTX_data_scene(C);
348
349 /* All windows get active screen from file. */
350 if (screen->winid == 0) {
351 WM_window_set_active_screen(win, workspace, screen);
352 }
353 else {
354#if 0
355 /* NOTE(@ideasman42): The screen referenced from the window has been freed,
356 * see: #107525. */
358#endif
360 bmain, workspace, layout_ref, win);
361
362 WM_window_set_active_layout(win, workspace, layout_new);
363 }
364
365 bScreen *win_screen = WM_window_get_active_screen(win);
366 win_screen->winid = win->winid;
367 }
368 }
369}
370
372 Main * /*bmain*/,
373 BlendFileReadWMSetupData *wm_setup_data,
375{
376 wmWindowManager *old_wm = wm_setup_data->old_wm;
377
378 wm->op_undo_depth = old_wm->op_undo_depth;
379
380 /* Move existing key configurations into the new WM. */
381 wm->runtime->keyconfigs = old_wm->runtime->keyconfigs;
382 wm->runtime->addonconf = old_wm->runtime->addonconf;
383 wm->runtime->defaultconf = old_wm->runtime->defaultconf;
384 wm->runtime->userconf = old_wm->runtime->userconf;
385
386 BLI_listbase_clear(&old_wm->runtime->keyconfigs);
387 old_wm->runtime->addonconf = nullptr;
388 old_wm->runtime->defaultconf = nullptr;
389 old_wm->runtime->userconf = nullptr;
390
391 /* Ensure new keymaps are made, and space types are set. */
392 wm->init_flag = 0;
393 wm->runtime->winactive = nullptr;
394
395 /* Clearing drawable of old WM before deleting any context to avoid clearing the wrong wm. */
397
398 bool has_match = false;
399 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
400 LISTBASE_FOREACH (wmWindow *, old_win, &old_wm->windows) {
401 if (old_win->winid == win->winid) {
402 has_match = true;
403
404 wm_file_read_setup_wm_substitute_old_window(old_wm, wm, old_win, win);
405 }
406 }
407 }
408 /* Ensure that at least one window is kept open so we don't lose the context, see #42303. */
409 if (!has_match) {
411 wm,
412 static_cast<wmWindow *>(old_wm->windows.first),
413 static_cast<wmWindow *>(wm->windows.first));
414 }
415
416 wm_setup_data->old_wm = nullptr;
417 wm_close_and_free(C, old_wm);
418 /* Don't handle user counts as this is only ever called once #G_MAIN has already been freed via
419 * #BKE_main_free so any access to ID's referenced by the window-manager (from ID properties)
420 * will crash. See: #100703. */
421 BKE_libblock_free_data(&old_wm->id, false);
423 MEM_freeN(old_wm);
424}
425
433 Main *bmain,
434 BlendFileReadWMSetupData *wm_setup_data)
435{
436 BLI_assert(BLI_listbase_count_at_most(&bmain->wm, 2) <= 1);
437 BLI_assert(wm_setup_data != nullptr);
438 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
439
440 /* If reading factory startup file, and there was no previous WM, clear the size of the windows
441 * in newly read WM so that they get resized to occupy the whole available space on current
442 * monitor.
443 */
444 if (wm_setup_data->is_read_homefile && wm_setup_data->is_factory_startup &&
445 wm_setup_data->old_wm == nullptr)
446 {
448 }
449
450 if (wm == nullptr) {
451 /* Add a default WM in case none exists in newly read main (should only happen when opening
452 * an old pre-2.5 .blend file at startup). */
453 wm_add_default(bmain, C);
454 }
455 else if (wm_setup_data->old_wm != nullptr) {
456 if (wm_setup_data->old_wm == wm) {
457 /* Old WM was kept, update it with new workspaces/layouts/screens read from file.
458 *
459 * Happens when not loading UI, or when the newly read file has no WM (pre-2.5 files). */
461 C, bmain, wm_setup_data, wm, (G.fileflags & G_FILE_NO_UI) == 0);
462 }
463 else {
464 /* Using new WM from read file, try to keep current GHOST windows, transfer keymaps, etc.,
465 * from old WM.
466 *
467 * Also takes care of clearing old WM data (temporarily stored in `wm_setup_data->old_wm`).
468 */
469 wm_file_read_setup_wm_use_new(C, bmain, wm_setup_data, wm);
470 }
471 }
472 /* Else just using the new WM read from file, nothing to do. */
473 BLI_assert(wm_setup_data->old_wm == nullptr);
474 MEM_delete(wm_setup_data);
475
476 /* UI Updates. */
477 /* Flag local View3D's to check and exit if they are empty. */
478 LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
479 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
480 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
481 if (sl->spacetype == SPACE_VIEW3D) {
482 View3D *v3d = reinterpret_cast<View3D *>(sl);
483 if (v3d->localvd) {
485 }
486 }
487 }
488 }
489 }
490}
491
493
494/* -------------------------------------------------------------------- */
497
499{
500 /* Check if GPU backend is already set from the command line arguments. The command line
501 * arguments have higher priority than user preferences. */
503 return;
504 }
505
507}
508
512static void wm_init_userdef(Main *bmain)
513{
514 /* Not versioning, just avoid errors. */
515#ifndef WITH_CYCLES
516 BKE_addon_remove_safe(&U.addons, "cycles");
517#endif
518
520
521 /* Needed so loading a file from the command line respects user-pref #26156. */
522 SET_FLAG_FROM_TEST(G.fileflags, U.flag & USER_FILENOUI, G_FILE_NO_UI);
523
524 /* Set the python auto-execute setting from user prefs. */
525 /* Enabled by default, unless explicitly enabled in the command line which overrides. */
526 if ((G.f & G_FLAG_SCRIPT_OVERRIDE_PREF) == 0) {
528 }
529
530 /* Only reset "offline mode" if they weren't passes via command line arguments. */
531 if ((G.f & G_FLAG_INTERNET_OVERRIDE_PREF_ANY) == 0) {
533 }
534
535 const int64_t cache_limit = int64_t(U.memcachelimit) * 1024 * 1024;
536 MEM_CacheLimiter_set_maximum(cache_limit);
538
539 BKE_sound_init(bmain);
540
541 /* Update the temporary directory from the preferences or fall back to the system default. */
542 BKE_tempdir_init(U.tempdir);
543
544 /* Update input device preference. */
546
548
551}
552
553/* Return codes. */
554#define BKE_READ_EXOTIC_FAIL_PATH -3 /* File format is not supported. */
555#define BKE_READ_EXOTIC_FAIL_FORMAT -2 /* File format is not supported. */
556#define BKE_READ_EXOTIC_FAIL_OPEN -1 /* Can't open the file. */
557#define BKE_READ_EXOTIC_OK_BLEND 0 /* `.blend` file. */
558#if 0
559# define BKE_READ_EXOTIC_OK_OTHER 1 /* Other supported formats. */
560#endif
561
563
564/* -------------------------------------------------------------------- */
570
571/* Intended to check for non-blender formats but for now it only reads blends. */
572static int wm_read_exotic(const char *filepath)
573{
574 /* Make sure we're not trying to read a directory. */
575
576 int filepath_len = strlen(filepath);
577 if (filepath_len > 0 && ELEM(filepath[filepath_len - 1], '/', '\\')) {
579 }
580
581 /* Open the file. */
582 const int filedes = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
583 if (filedes == -1) {
585 }
586
587 FileReader *rawfile = BLI_filereader_new_file(filedes);
588 if (rawfile == nullptr) {
590 }
591
592 /* Read the header (7 bytes are enough to identify all known types). */
593 char header[7];
594 if (rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) {
595 rawfile->close(rawfile);
597 }
598 rawfile->seek(rawfile, 0, SEEK_SET);
599
600 /* Check for uncompressed `.blend`. */
601 if (STREQLEN(header, "BLENDER", 7)) {
602 rawfile->close(rawfile);
604 }
605
606 /* Check for compressed `.blend`. */
607 FileReader *compressed_file = nullptr;
608 if (BLI_file_magic_is_gzip(header)) {
609 /* In earlier versions of Blender (before 3.0), compressed files used `Gzip` instead of `Zstd`.
610 * While these files will no longer be written, there still needs to be reading support. */
611 compressed_file = BLI_filereader_new_gzip(rawfile);
612 }
613 else if (BLI_file_magic_is_zstd(header)) {
614 compressed_file = BLI_filereader_new_zstd(rawfile);
615 }
616
617 /* If a compression signature matches,
618 * try decompressing the start and check if it's a `.blend`. */
619 if (compressed_file != nullptr) {
620 size_t len = compressed_file->read(compressed_file, header, sizeof(header));
621 compressed_file->close(compressed_file);
622 if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) {
624 }
625 }
626 else {
627 rawfile->close(rawfile);
628 }
629
630 /* Add check for future file formats here. */
631
633}
634
636
637/* -------------------------------------------------------------------- */
640
641void WM_file_autoexec_init(const char *filepath)
642{
644 return;
645 }
646
647 if (G.f & G_FLAG_SCRIPT_AUTOEXEC) {
648 char dirpath[FILE_MAX];
649 BLI_path_split_dir_part(filepath, dirpath, sizeof(dirpath));
650 if (BKE_autoexec_match(dirpath)) {
652 }
653 }
654}
655
657{
658 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
659 ReportList *reports = &wm->runtime->reports;
660 bool found = false;
661 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
662 if (scene->r.engine[0] &&
663 BLI_findstring(&R_engines, scene->r.engine, offsetof(RenderEngineType, idname)) == nullptr)
664 {
665 BKE_reportf(reports,
666 RPT_ERROR,
667 "Engine '%s' not available for scene '%s' (an add-on may need to be installed "
668 "or enabled)",
669 scene->r.engine,
670 scene->id.name + 2);
671 found = true;
672 }
673 }
674
675 if (found) {
676 if (!G.background) {
678 }
679 }
680}
681
688static void wm_file_read_pre(bool use_data, bool /*use_userdef*/)
689{
690 if (use_data) {
692 }
693
694 /* Always do this as both startup and preferences may have loaded in many font's
695 * at a different zoom level to the file being loaded. */
697
699}
700
717
723 const char *filepath,
725{
727
728 const bool use_data = params->use_data;
729 const bool use_userdef = params->use_userdef;
730 const bool is_startup_file = params->is_startup_file;
731 const bool is_factory_startup = params->is_factory_startup;
732 const bool reset_app_template = params->reset_app_template;
733
734 bool addons_loaded = false;
735
736 if (use_data) {
737 if (!G.background) {
738 /* Remove windows which failed to be added via #WM_check. */
740 }
741 CTX_wm_window_set(C, static_cast<wmWindow *>(wm->windows.first));
742 }
743
744#ifdef WITH_PYTHON
745 if (is_startup_file) {
746 /* The following block handles data & preferences being reloaded
747 * which requires resetting some internal variables. */
748 if (!params->is_first_time) {
750 bool reset_all = use_userdef;
751 if (use_userdef || reset_app_template) {
752 /* Only run when we have a template path found. */
754 const char *imports[] = {"bl_app_template_utils", nullptr};
755 BPY_run_string_eval(C, imports, "bl_app_template_utils.reset()");
756 reset_all = true;
757 }
758 }
759 if (reset_all) {
760 const char *imports[] = {"bpy", "addon_utils", nullptr};
762 C,
763 imports,
764 /* Refresh scripts as the preferences may have changed the user-scripts path.
765 *
766 * This is needed when loading settings from the previous version,
767 * otherwise the script path stored in the preferences would be ignored. */
768 "bpy.utils.refresh_script_paths()\n"
769 /* Sync add-ons, these may have changed from the defaults. */
770 "addon_utils.reset_all()");
771 }
772 if (use_data) {
774 }
775 addons_loaded = true;
776 }
777 }
778 else {
779 /* Run any texts that were loaded in and flagged as modules. */
780 if (use_data) {
782 }
783 addons_loaded = true;
784 }
785#else
786 UNUSED_VARS(is_startup_file, reset_app_template);
787#endif /* WITH_PYTHON */
788
789 Main *bmain = CTX_data_main(C);
790
791 if (use_userdef) {
792 if (is_factory_startup) {
794 }
795 }
796
797 if (is_factory_startup && BLT_translate_new_dataname()) {
798 /* Translate workspace names. */
799 LISTBASE_FOREACH_MUTABLE (WorkSpace *, workspace, &bmain->workspaces) {
801 *bmain, workspace->id, CTX_DATA_(BLT_I18NCONTEXT_ID_WORKSPACE, workspace->id.name + 2));
802 }
803 }
804
805 if (use_data) {
806 /* Important to do before nulling the context. */
808
809 /* Load-post must run before evaluating drivers & depsgraph, see: #109720.
810 * On failure, the caller handles #BKE_CB_EVT_LOAD_POST_FAIL. */
811 if (params->success) {
813 }
814
815 if (is_factory_startup) {
817 }
818 }
819
820 if (use_data) {
822
823 /* After load post, so for example the driver namespace can be filled
824 * before evaluating the depsgraph. */
825 if (!G.background || (G.fileflags & G_BACKGROUND_NO_DEPSGRAPH) == 0) {
827 }
828
830
831 /* Add-ons are disabled when loading the startup file, so the Render Layer node in compositor
832 * node trees might be wrong due to missing render engines that are available as add-ons, like
833 * Cycles. So we need to update compositor node trees after reading the file when add-ons are
834 * now loaded. */
835 if (is_startup_file) {
836 FOREACH_NODETREE_BEGIN (bmain, node_tree, owner_id) {
837 if (node_tree->type == NTREE_COMPOSIT) {
839 }
840 }
842 }
843
844#if 1
846 /* Clear static filtered asset tree caches. */
848#else
850#endif
851 }
852
853 /* Report any errors.
854 * Currently disabled if add-ons aren't yet loaded. */
855 if (addons_loaded) {
856 wm_file_read_report(bmain, static_cast<wmWindow *>(wm->windows.first));
857 }
858
859 if (use_data) {
860 if (!G.background) {
861 if (wm->runtime->undo_stack == nullptr) {
862 wm->runtime->undo_stack = BKE_undosys_stack_create();
863 }
864 else {
865 BKE_undosys_stack_clear(wm->runtime->undo_stack);
866 }
867 BKE_undosys_stack_init_from_main(wm->runtime->undo_stack, bmain);
868 BKE_undosys_stack_init_from_context(wm->runtime->undo_stack, C);
869 }
870 }
871
872 if (use_data) {
873 if (!G.background) {
874 /* In background mode this makes it hard to load
875 * a blend file and do anything since the screen
876 * won't be set to a valid value again. */
877 CTX_wm_window_set(C, nullptr); /* Exits queues. */
878
879 /* Ensure auto-run action is not used from a previous blend file load. */
880 wm_test_autorun_revert_action_set(nullptr, nullptr);
881
882 /* Ensure tools are registered. */
884 }
885 }
886}
887
888static void wm_read_callback_pre_wrapper(bContext *C, const char *filepath)
889{
890 /* NOTE: either #BKE_CB_EVT_LOAD_POST or #BKE_CB_EVT_LOAD_POST_FAIL must run.
891 * Runs at the end of this function, don't return beforehand. */
893}
894
895static void wm_read_callback_post_wrapper(bContext *C, const char *filepath, const bool success)
896{
897 Main *bmain = CTX_data_main(C);
898 /* Temporarily set the window context as this was once supported, see: #107759.
899 * If the window is already set, don't change it. */
900 bool has_window = CTX_wm_window(C) != nullptr;
901 if (!has_window) {
902 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
903 wmWindow *win = static_cast<wmWindow *>(wm->windows.first);
904 CTX_wm_window_set(C, win);
905 }
906
907 /* On success: #BKE_CB_EVT_LOAD_POST runs from #wm_file_read_post. */
908 if (success == false) {
910 }
911
912 /* This function should leave the window null when the function entered. */
913 if (!has_window) {
914 CTX_wm_window_set(C, nullptr);
915 }
916}
917
919
920/* -------------------------------------------------------------------- */
923
925{
926 double duration_whole_minutes, duration_whole_seconds;
927 double duration_libraries_minutes, duration_libraries_seconds;
928 double duration_lib_override_minutes, duration_lib_override_seconds;
929 double duration_lib_override_resync_minutes, duration_lib_override_resync_seconds;
930 double duration_lib_override_recursive_resync_minutes,
931 duration_lib_override_recursive_resync_seconds;
932
934 nullptr,
935 nullptr,
936 &duration_whole_minutes,
937 &duration_whole_seconds,
938 nullptr);
940 nullptr,
941 nullptr,
942 &duration_libraries_minutes,
943 &duration_libraries_seconds,
944 nullptr);
946 nullptr,
947 nullptr,
948 &duration_lib_override_minutes,
949 &duration_lib_override_seconds,
950 nullptr);
952 nullptr,
953 nullptr,
954 &duration_lib_override_resync_minutes,
955 &duration_lib_override_resync_seconds,
956 nullptr);
958 nullptr,
959 nullptr,
960 &duration_lib_override_recursive_resync_minutes,
961 &duration_lib_override_recursive_resync_seconds,
962 nullptr);
963
964 CLOG_INFO(
965 &LOG, "Blender file read in %.0fm%.2fs", duration_whole_minutes, duration_whole_seconds);
966 CLOG_INFO(&LOG,
967 " * Loading libraries: %.0fm%.2fs",
968 duration_libraries_minutes,
969 duration_libraries_seconds);
970 CLOG_INFO(&LOG,
971 " * Applying overrides: %.0fm%.2fs",
972 duration_lib_override_minutes,
973 duration_lib_override_seconds);
974 CLOG_INFO(&LOG,
975 " * Resyncing overrides: %.0fm%.2fs (%d root overrides), including recursive "
976 "resyncs: %.0fm%.2fs)",
977 duration_lib_override_resync_minutes,
978 duration_lib_override_resync_seconds,
979 bf_reports->count.resynced_lib_overrides,
980 duration_lib_override_recursive_resync_minutes,
981 duration_lib_override_recursive_resync_seconds);
982
983 if (bf_reports->resynced_lib_overrides_libraries_count != 0) {
984 for (LinkNode *node_lib = bf_reports->resynced_lib_overrides_libraries; node_lib != nullptr;
985 node_lib = node_lib->next)
986 {
987 Library *library = static_cast<Library *>(node_lib->link);
988 BKE_reportf(bf_reports->reports,
989 RPT_INFO,
990 "Library \"%s\" needs overrides resync",
991 library->filepath);
992 }
993 }
994
995 if (bf_reports->count.missing_libraries != 0 || bf_reports->count.missing_linked_id != 0) {
996 BKE_reportf(bf_reports->reports,
998 "%d libraries and %d linked data-blocks are missing (including %d ObjectData), "
999 "please check the Info and Outliner editors for details",
1000 bf_reports->count.missing_libraries,
1001 bf_reports->count.missing_linked_id,
1002 bf_reports->count.missing_obdata);
1003 }
1004 else {
1005 if (bf_reports->count.missing_obdata != 0) {
1006 CLOG_WARN(&LOG,
1007 "%d local ObjectData are reported to be missing, this should never happen",
1008 bf_reports->count.missing_obdata);
1009 }
1010 }
1011
1012 if (bf_reports->resynced_lib_overrides_libraries_count != 0) {
1013 BKE_reportf(bf_reports->reports,
1015 "%d libraries have overrides needing resync (auto resynced in %.0fm%.2fs), "
1016 "please check the Info editor for details",
1018 duration_lib_override_recursive_resync_minutes,
1019 duration_lib_override_recursive_resync_seconds);
1020 }
1021
1022 if (bf_reports->count.proxies_to_lib_overrides_success != 0 ||
1024 {
1025 BKE_reportf(bf_reports->reports,
1027 "Proxies have been removed from Blender (%d proxies were automatically converted "
1028 "to library overrides, %d proxies could not be converted and were cleared). "
1029 "Consider re-saving any library .blend file with the newest Blender version",
1032 }
1033
1034 if (bf_reports->count.sequence_strips_skipped != 0) {
1035 BKE_reportf(bf_reports->reports,
1036 RPT_ERROR,
1037 "%d sequence strips were not read because they were in a channel larger than %d",
1038 bf_reports->count.sequence_strips_skipped,
1040 }
1041
1043 bf_reports->resynced_lib_overrides_libraries = nullptr;
1044
1045 if (bf_reports->pre_animato_file_loaded) {
1046 BKE_report(
1047 bf_reports->reports,
1049 "Loaded a pre-2.50 blend file, animation data has not been loaded. Open & save the file "
1050 "with Blender v4.5 to convert animation data.");
1051 }
1052}
1053
1055 const char *filepath,
1056 const bool use_scripts_autoexec_check,
1057 ReportList *reports)
1058{
1059 /* Assume automated tasks with background, don't write recent file list. */
1060 const bool do_history_file_update = (G.background == false) &&
1061 (CTX_wm_manager(C)->op_undo_depth == 0);
1062 bool success = false;
1063
1064 const bool use_data = true;
1065 const bool use_userdef = false;
1066
1067 /* NOTE: a matching #wm_read_callback_post_wrapper must be called. */
1069
1070 Main *bmain = CTX_data_main(C);
1071
1072 /* So we can get the error message. */
1073 errno = 0;
1074
1075 WM_cursor_wait(true);
1076
1077 /* First try to append data from exotic file formats. */
1078 /* It throws error box when file doesn't exist and returns -1. */
1079 /* NOTE(ton): it should set some error message somewhere. */
1080 const int retval = wm_read_exotic(filepath);
1081
1082 /* We didn't succeed, now try to read Blender file. */
1083 if (retval == BKE_READ_EXOTIC_OK_BLEND) {
1085 params.is_startup = false;
1086 /* Loading preferences when the user intended to load a regular file is a security
1087 * risk, because the excluded path list is also loaded. Further it's just confusing
1088 * if a user loads a file and various preferences change. */
1089 params.skip_flags = BLO_READ_SKIP_USERDEF;
1090
1091 BlendFileReadReport bf_reports{};
1092 bf_reports.reports = reports;
1093 bf_reports.duration.whole = BLI_time_now_seconds();
1094 BlendFileData *bfd = BKE_blendfile_read(filepath, &params, &bf_reports);
1095 if (bfd != nullptr) {
1096 wm_file_read_pre(use_data, use_userdef);
1097
1098 /* Close any user-loaded fonts. */
1100
1101 /* Put WM into a stable state for post-readfile processes (kill jobs, removes event handlers,
1102 * message bus, and so on). */
1103 BlendFileReadWMSetupData *wm_setup_data = wm_file_read_setup_wm_init(C, bmain, false);
1104
1105 /* This flag is initialized by the operator but overwritten on read.
1106 * need to re-enable it here else drivers and registered scripts won't work. */
1107 const int G_f_orig = G.f;
1108
1109 /* Frees the current main and replaces it with the new one read from file. */
1111 C, bfd, &params, wm_setup_data, &bf_reports, false, nullptr);
1112 bmain = CTX_data_main(C);
1113
1114 /* Finalize handling of WM, using the read WM and/or the current WM depending on things like
1115 * whether the UI is loaded from the .blend file or not, etc. */
1116 wm_file_read_setup_wm_finalize(C, bmain, wm_setup_data);
1117
1118 if (G.f != G_f_orig) {
1119 const int flags_keep = G_FLAG_ALL_RUNTIME;
1121 G.f = (G.f & ~flags_keep) | (G_f_orig & flags_keep);
1122 }
1123
1124 /* Set by the `use_scripts` property on file load.
1125 * If this was not set, then it should be calculated based on the file-path.
1126 * Note that this uses `bmain->filepath` and not `filepath`, necessary when
1127 * recovering the last session, where the file-path can be #BLENDER_QUIT_FILE. */
1128 if (use_scripts_autoexec_check) {
1130 }
1131
1132 WM_check(C); /* Opens window(s), checks keymaps. */
1133
1134 if (do_history_file_update) {
1136 }
1137
1138 wmFileReadPost_Params read_file_post_params{};
1139 read_file_post_params.use_data = use_data;
1140 read_file_post_params.use_userdef = use_userdef;
1141 read_file_post_params.is_startup_file = false;
1142 read_file_post_params.is_factory_startup = false;
1143 read_file_post_params.reset_app_template = false;
1144 read_file_post_params.success = true;
1145 read_file_post_params.is_alloc = false;
1146 wm_file_read_post(C, filepath, &read_file_post_params);
1147
1148 bf_reports.duration.whole = BLI_time_now_seconds() - bf_reports.duration.whole;
1149 file_read_reports_finalize(&bf_reports);
1150
1151 success = true;
1152 }
1153 }
1154#if 0
1155 else if (retval == BKE_READ_EXOTIC_OK_OTHER) {
1156 BKE_undo_write(C, "Import file");
1157 }
1158#endif
1159 else if (retval == BKE_READ_EXOTIC_FAIL_OPEN) {
1160 BKE_reportf(reports,
1161 RPT_ERROR,
1162 "Cannot read file \"%s\": %s",
1163 filepath,
1164 errno ? strerror(errno) : RPT_("unable to open the file"));
1165 }
1166 else if (retval == BKE_READ_EXOTIC_FAIL_FORMAT) {
1167 BKE_reportf(reports, RPT_ERROR, "File format is not supported in file \"%s\"", filepath);
1168 }
1169 else if (retval == BKE_READ_EXOTIC_FAIL_PATH) {
1170 BKE_reportf(reports, RPT_ERROR, "File path \"%s\" invalid", filepath);
1171 }
1172 else {
1173 BKE_reportf(reports, RPT_ERROR, "Unknown error loading \"%s\"", filepath);
1174 BLI_assert_msg(0, "invalid 'retval'");
1175 }
1176
1177 /* NOTE: even if the file fails to load, keep the file in the "Recent Files" list.
1178 * This is done because failure to load could be caused by the file-system being
1179 * temporarily offline, see: #127825. */
1180
1181 WM_cursor_wait(false);
1182
1183 wm_read_callback_post_wrapper(C, filepath, success);
1184
1186
1187 return success;
1188}
1189
1190static struct {
1192 bool override;
1194
1196{
1197 if (app_template) {
1199 wm_init_state_app_template.override = true;
1200 }
1201 else {
1202 wm_init_state_app_template.app_template[0] = '\0';
1203 wm_init_state_app_template.override = false;
1204 }
1205}
1206
1208{
1209 return wm_init_state_app_template.override ? wm_init_state_app_template.app_template : nullptr;
1210}
1211
1213
1214/* -------------------------------------------------------------------- */
1217
1219 const wmHomeFileRead_Params *params_homefile,
1220 ReportList *reports,
1221 wmFileReadPost_Params **r_params_file_read_post)
1222{
1223 /* NOTE: unlike #WM_file_read, don't set the wait cursor when reading the home-file.
1224 * While technically both are reading a file and could use the wait cursor,
1225 * avoid doing so for the following reasons.
1226 *
1227 * - When loading blend with a file (command line or external file browser)
1228 * the home-file is read before the file being loaded.
1229 * Toggling the wait cursor twice causes the cursor to flicker which looks like a glitch.
1230 * - In practice it's not that useful as users tend not to set scenes with slow loading times
1231 * as their startup.
1232 */
1233
1234/* UNUSED, keep as this may be needed later & the comment below isn't self evident. */
1235#if 0
1236 /* Context does not always have valid main pointer here. */
1237 Main *bmain = G_MAIN;
1238#endif
1239 bool success = false;
1240
1241 /* May be enabled, when the user configuration doesn't exist. */
1242 const bool use_data = params_homefile->use_data;
1243 const bool use_userdef = params_homefile->use_userdef;
1244 bool use_factory_settings = params_homefile->use_factory_settings;
1245 /* Currently this only impacts preferences as it doesn't make much sense to keep the default
1246 * startup open in the case the app-template doesn't happen to define its own startup.
1247 * Unlike preferences where we might want to only reset the app-template part of the preferences
1248 * so as not to reset the preferences for all other Blender instances, see: #96427. */
1249 const bool use_factory_settings_app_template_only =
1251 const bool use_empty_data = params_homefile->use_empty_data;
1252 const char *filepath_startup_override = params_homefile->filepath_startup_override;
1253 const char *app_template_override = params_homefile->app_template_override;
1254
1255 bool filepath_startup_is_factory = true;
1256 char filepath_startup[FILE_MAX];
1257 char filepath_userdef[FILE_MAX];
1258
1259 /* When 'app_template' is set:
1260 * `{BLENDER_USER_CONFIG}/{app_template}`. */
1261 char app_template_system[FILE_MAX];
1262 /* When 'app_template' is set:
1263 * `{BLENDER_SYSTEM_SCRIPTS}/startup/bl_app_templates_system/{app_template}`. */
1264 char app_template_config[FILE_MAX];
1265
1266 eBLOReadSkip skip_flags = eBLOReadSkip(0);
1267
1268 if (use_data == false) {
1269 skip_flags |= BLO_READ_SKIP_DATA;
1270 }
1271 if (use_userdef == false) {
1272 skip_flags |= BLO_READ_SKIP_USERDEF;
1273 }
1274
1275 /* True if we load startup.blend from memory
1276 * or use app-template startup.blend which the user hasn't saved. */
1277 bool is_factory_startup = true;
1278
1279 const char *app_template = nullptr;
1280 bool update_defaults = false;
1281
1282 /* Current Main is not always available in context here. */
1283 Main *bmain = G_MAIN;
1284
1285 if (filepath_startup_override != nullptr) {
1286 /* Pass. */
1287 }
1288 else if (app_template_override) {
1289 /* This may be clearing the current template by setting to an empty string. */
1290 app_template = app_template_override;
1291 }
1292 else if (!use_factory_settings && U.app_template[0]) {
1293 app_template = U.app_template;
1294 }
1295
1296 const bool reset_app_template = ((!app_template && U.app_template[0]) ||
1297 (app_template && !STREQ(app_template, U.app_template)));
1298
1299 /* Options exclude each other. */
1300 BLI_assert((use_factory_settings && filepath_startup_override) == 0);
1301
1302 if ((G.f & G_FLAG_SCRIPT_OVERRIDE_PREF) == 0) {
1304 }
1305
1306 if (use_data) {
1307 if (reset_app_template) {
1308 /* Always load UI when switching to another template. */
1309 G.fileflags &= ~G_FILE_NO_UI;
1310 }
1311 }
1312
1313 if (use_userdef || reset_app_template) {
1314#ifdef WITH_PYTHON
1315 /* This only runs once Blender has already started. */
1316 if (!params_homefile->is_first_time) {
1318 /* This is restored by 'wm_file_read_post', disable before loading any preferences
1319 * so an add-on can read their own preferences when un-registering,
1320 * and use new preferences if/when re-registering, see #67577.
1321 *
1322 * Note that this fits into 'wm_file_read_pre' function but gets messy
1323 * since we need to know if 'reset_app_template' is true. */
1324 const char *imports[] = {"addon_utils", nullptr};
1325 BPY_run_string_eval(C, imports, "addon_utils.disable_all()");
1326 }
1327#endif /* WITH_PYTHON */
1328 }
1329
1330 if (use_data) {
1331 /* NOTE: a matching #wm_read_callback_post_wrapper must be called.
1332 * This runs from #wm_homefile_read_post. */
1334 }
1335
1336 /* For regular file loading this only runs after the file is successfully read.
1337 * In the case of the startup file, the in-memory startup file is used as a fallback
1338 * so we know this will work if all else fails. */
1339 wm_file_read_pre(use_data, use_userdef);
1340
1341 BlendFileReadWMSetupData *wm_setup_data = nullptr;
1342 if (use_data) {
1343 /* Put WM into a stable state for post-readfile processes (kill jobs, removes event handlers,
1344 * message bus, and so on). */
1345 wm_setup_data = wm_file_read_setup_wm_init(C, bmain, true);
1346 }
1347
1348 filepath_startup[0] = '\0';
1349 filepath_userdef[0] = '\0';
1350 app_template_system[0] = '\0';
1351 app_template_config[0] = '\0';
1352
1353 const std::optional<std::string> cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, nullptr);
1354 if (!use_factory_settings) {
1355 if (cfgdir.has_value()) {
1357 filepath_startup, sizeof(filepath_startup), cfgdir->c_str(), BLENDER_STARTUP_FILE);
1358 filepath_startup_is_factory = false;
1359 if (use_userdef) {
1361 filepath_userdef, sizeof(filepath_startup), cfgdir->c_str(), BLENDER_USERPREF_FILE);
1362 }
1363 }
1364 else {
1365 use_factory_settings = true;
1366 }
1367
1368 if (filepath_startup_override) {
1369 STRNCPY(filepath_startup, filepath_startup_override);
1370 filepath_startup_is_factory = false;
1371 }
1372 }
1373
1374 /* Load preferences before `startup.blend`. */
1375 if (use_userdef) {
1376 if (use_factory_settings_app_template_only) {
1377 /* Use the current preferences as-is (only load in the app_template preferences). */
1378 skip_flags |= BLO_READ_SKIP_USERDEF;
1379 }
1380 else if (!use_factory_settings && BLI_exists(filepath_userdef)) {
1381 UserDef *userdef = BKE_blendfile_userdef_read(filepath_userdef, nullptr);
1382 if (userdef != nullptr) {
1383 CLOG_INFO(&LOG, "Read prefs: \"%s\"", filepath_userdef);
1384
1386 userdef = nullptr;
1387
1388 skip_flags |= BLO_READ_SKIP_USERDEF;
1389 }
1390 }
1391 }
1392
1393 if ((app_template != nullptr) && (app_template[0] != '\0')) {
1395 app_template, app_template_system, sizeof(app_template_system)))
1396 {
1397 /* Can safely continue with code below, just warn it's not found. */
1398 BKE_reportf(reports, RPT_WARNING, "Application Template \"%s\" not found", app_template);
1399 }
1400
1401 /* Insert template name into startup file. */
1402
1403 /* Note that the path is being set even when `use_factory_settings == true`
1404 * this is done so we can load a templates factory-settings. */
1405 if (!use_factory_settings && cfgdir.has_value()) {
1407 app_template_config, sizeof(app_template_config), cfgdir->c_str(), app_template);
1409 filepath_startup, sizeof(filepath_startup), app_template_config, BLENDER_STARTUP_FILE);
1410 filepath_startup_is_factory = false;
1411 if (BLI_access(filepath_startup, R_OK) != 0) {
1412 filepath_startup[0] = '\0';
1413 }
1414 }
1415 else {
1416 filepath_startup[0] = '\0';
1417 }
1418
1419 if (filepath_startup[0] == '\0') {
1421 filepath_startup, sizeof(filepath_startup), app_template_system, BLENDER_STARTUP_FILE);
1422 filepath_startup_is_factory = true;
1423
1424 /* Update defaults only for system templates. */
1425 update_defaults = true;
1426 }
1427 }
1428
1429 if (!use_factory_settings || (filepath_startup[0] != '\0')) {
1430 if (BLI_access(filepath_startup, R_OK) == 0) {
1432 params.is_startup = true;
1433 params.is_factory_settings = use_factory_settings;
1434 params.skip_flags = skip_flags | BLO_READ_SKIP_USERDEF;
1435 BlendFileReadReport bf_reports{};
1436 bf_reports.reports = reports;
1437 BlendFileData *bfd = BKE_blendfile_read(filepath_startup, &params, &bf_reports);
1438
1439 if (bfd != nullptr) {
1440 CLOG_INFO(&LOG, "Read startup: \"%s\"", filepath_startup);
1441
1442 /* Frees the current main and replaces it with the new one read from file. */
1444 bfd,
1445 &params,
1446 wm_setup_data,
1447 &bf_reports,
1448 update_defaults && use_data,
1449 app_template);
1450 success = true;
1451 bmain = CTX_data_main(C);
1452 }
1453 }
1454 if (success) {
1455 is_factory_startup = filepath_startup_is_factory;
1456 }
1457 }
1458
1459 if (use_userdef) {
1460 if ((skip_flags & BLO_READ_SKIP_USERDEF) == 0) {
1461 UserDef *userdef_default = BKE_blendfile_userdef_from_defaults();
1463 skip_flags |= BLO_READ_SKIP_USERDEF;
1464 }
1465 }
1466
1467 if (success == false && filepath_startup_override && reports) {
1468 /* We can not return from here because wm is already reset. */
1469 BKE_reportf(reports, RPT_ERROR, "Could not read \"%s\"", filepath_startup_override);
1470 }
1471
1472 bool loaded_factory_settings = false;
1473 if (success == false) {
1474 BlendFileReadParams read_file_params{};
1475 read_file_params.is_startup = true;
1476 read_file_params.is_factory_settings = use_factory_settings;
1477 read_file_params.skip_flags = skip_flags;
1479 datatoc_startup_blend, datatoc_startup_blend_size, &read_file_params, nullptr);
1480 if (bfd != nullptr) {
1481 BlendFileReadReport read_report{};
1482 /* Frees the current main and replaces it with the new one read from file. */
1484 C, bfd, &read_file_params, wm_setup_data, &read_report, true, nullptr);
1485 success = true;
1486 loaded_factory_settings = true;
1487 bmain = CTX_data_main(C);
1488 }
1489 }
1490
1491 if (use_empty_data) {
1493 }
1494
1495 /* Load template preferences,
1496 * unlike regular preferences we only use some of the settings,
1497 * see: #BKE_blender_userdef_set_app_template. */
1498 if (app_template_system[0] != '\0') {
1499 char temp_path[FILE_MAX];
1500 temp_path[0] = '\0';
1501 if (!use_factory_settings) {
1502 BLI_path_join(temp_path, sizeof(temp_path), app_template_config, BLENDER_USERPREF_FILE);
1503 if (BLI_access(temp_path, R_OK) != 0) {
1504 temp_path[0] = '\0';
1505 }
1506 }
1507
1508 if (temp_path[0] == '\0') {
1509 BLI_path_join(temp_path, sizeof(temp_path), app_template_system, BLENDER_USERPREF_FILE);
1510 }
1511
1512 if (use_userdef) {
1513 UserDef *userdef_template = nullptr;
1514 /* Just avoids missing file warning. */
1515 if (BLI_exists(temp_path)) {
1516 userdef_template = BKE_blendfile_userdef_read(temp_path, nullptr);
1517 if (userdef_template) {
1518 CLOG_INFO(&LOG, "Read prefs from app-template: \"%s\"", temp_path);
1519 }
1520 }
1521 if (userdef_template == nullptr) {
1522 /* We need to have preferences load to overwrite preferences from previous template. */
1523 userdef_template = BKE_blendfile_userdef_from_defaults();
1524 }
1525 if (userdef_template) {
1527 userdef_template = nullptr;
1528 }
1529 }
1530 }
1531
1532 if (app_template_override) {
1533 STRNCPY(U.app_template, app_template_override);
1534 }
1535
1536 if (use_userdef) {
1537 /* Check userdef before open window, keymaps etc. */
1538 wm_init_userdef(bmain);
1539 }
1540
1541 if (use_data) {
1542 /* Finalize handling of WM, using the read WM and/or the current WM depending on things like
1543 * whether the UI is loaded from the .blend file or not, etc. */
1544 wm_setup_data->is_factory_startup = loaded_factory_settings;
1545 wm_file_read_setup_wm_finalize(C, bmain, wm_setup_data);
1546 }
1547
1548 if (use_userdef) {
1549 /* Clear keymaps because the current default keymap may have been initialized
1550 * from user preferences, which have been reset. */
1551 LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
1552 if (wm->runtime->defaultconf) {
1553 wm->runtime->defaultconf->flag &= ~KEYCONF_INIT_DEFAULT;
1554 }
1555 }
1556 }
1557
1558 if (use_data) {
1559 WM_check(C); /* Opens window(s), checks keymaps. */
1560
1561 bmain->filepath[0] = '\0';
1562 }
1563
1564 {
1565 wmFileReadPost_Params params_file_read_post{};
1566 params_file_read_post.use_data = use_data;
1567 params_file_read_post.use_userdef = use_userdef;
1568 params_file_read_post.is_startup_file = true;
1569 params_file_read_post.is_factory_startup = is_factory_startup;
1570 params_file_read_post.is_first_time = params_homefile->is_first_time;
1571
1572 params_file_read_post.reset_app_template = reset_app_template;
1573
1574 params_file_read_post.success = success;
1575 params_file_read_post.is_alloc = false;
1576
1577 if (r_params_file_read_post == nullptr) {
1578 wm_homefile_read_post(C, &params_file_read_post);
1579 }
1580 else {
1581 params_file_read_post.is_alloc = true;
1582 *r_params_file_read_post = MEM_mallocN<wmFileReadPost_Params>(__func__);
1583 **r_params_file_read_post = params_file_read_post;
1584
1585 /* Match #wm_file_read_post which leaves the window cleared too. */
1586 CTX_wm_window_set(C, nullptr);
1587 }
1588 }
1589}
1590
1592 const wmHomeFileRead_Params *params_homefile,
1593 ReportList *reports)
1594{
1595 wm_homefile_read_ex(C, params_homefile, reports, nullptr);
1596}
1597
1598void wm_homefile_read_post(bContext *C, const wmFileReadPost_Params *params_file_read_post)
1599{
1600 const char *filepath = "";
1601 wm_file_read_post(C, filepath, params_file_read_post);
1602
1603 if (params_file_read_post->use_data) {
1604 wm_read_callback_post_wrapper(C, filepath, params_file_read_post->success);
1605 }
1606
1607 if (params_file_read_post->is_alloc) {
1608 MEM_freeN(params_file_read_post);
1609 }
1610}
1611
1613
1614/* -------------------------------------------------------------------- */
1617
1619{
1620 const std::optional<std::string> cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, nullptr);
1621 if (!cfgdir.has_value()) {
1622 return;
1623 }
1624
1625 char filepath[FILE_MAX];
1626 LinkNode *l;
1627 int num;
1628
1629 BLI_path_join(filepath, sizeof(filepath), cfgdir->c_str(), BLENDER_HISTORY_FILE);
1630
1631 LinkNode *lines = BLI_file_read_as_lines(filepath);
1632
1634
1635 /* Read list of recent opened files from #BLENDER_HISTORY_FILE to memory. */
1636 for (l = lines, num = 0; l && (num < U.recent_files); l = l->next) {
1637 const char *line = static_cast<const char *>(l->link);
1638 /* Don't check if files exist, causes slow startup for remote/external drives. */
1639 if (line[0]) {
1640 RecentFile *recent = MEM_mallocN<RecentFile>("RecentFile");
1641 BLI_addtail(&(G.recent_files), recent);
1642 recent->filepath = BLI_strdup(line);
1643 num++;
1644 }
1645 }
1646
1647 BLI_file_free_lines(lines);
1648}
1649
1650static RecentFile *wm_history_file_new(const char *filepath)
1651{
1652 RecentFile *recent = MEM_mallocN<RecentFile>("RecentFile");
1653 recent->filepath = BLI_strdup(filepath);
1654 return recent;
1655}
1656
1658{
1659 BLI_assert(BLI_findindex(&G.recent_files, recent) != -1);
1660 MEM_freeN(recent->filepath);
1661 BLI_freelinkN(&G.recent_files, recent);
1662}
1663
1665{
1666 LISTBASE_FOREACH_MUTABLE (RecentFile *, recent, &G.recent_files) {
1667 wm_history_file_free(recent);
1668 }
1669}
1670
1671static RecentFile *wm_file_history_find(const char *filepath)
1672{
1673 return static_cast<RecentFile *>(
1674 BLI_findstring_ptr(&G.recent_files, filepath, offsetof(RecentFile, filepath)));
1675}
1676
1682{
1683 char filepath[FILE_MAX];
1684 FILE *fp;
1685
1686 /* Will be nullptr in background mode. */
1687 const std::optional<std::string> user_config_dir = BKE_appdir_folder_id_create(
1688 BLENDER_USER_CONFIG, nullptr);
1689 if (!user_config_dir.has_value()) {
1690 return;
1691 }
1692
1693 BLI_path_join(filepath, sizeof(filepath), user_config_dir->c_str(), BLENDER_HISTORY_FILE);
1694
1695 fp = BLI_fopen(filepath, "w");
1696 if (fp) {
1697 LISTBASE_FOREACH (RecentFile *, recent, &G.recent_files) {
1698 fprintf(fp, "%s\n", recent->filepath);
1699 }
1700 fclose(fp);
1701 }
1702}
1703
1708{
1709 RecentFile *recent;
1710 const char *blendfile_path = BKE_main_blendfile_path_from_global();
1711
1712 /* No write history for recovered startup files. */
1713 if (blendfile_path[0] == '\0') {
1714 return;
1715 }
1716
1717 recent = static_cast<RecentFile *>(G.recent_files.first);
1718 /* Refresh #BLENDER_HISTORY_FILE of recent opened files, when current file was changed. */
1719 if (!(recent) || (BLI_path_cmp(recent->filepath, blendfile_path) != 0)) {
1720
1721 recent = wm_file_history_find(blendfile_path);
1722 if (recent) {
1723 BLI_remlink(&G.recent_files, recent);
1724 }
1725 else {
1726 RecentFile *recent_next;
1727 for (recent = static_cast<RecentFile *>(BLI_findlink(&G.recent_files, U.recent_files - 1));
1728 recent;
1729 recent = recent_next)
1730 {
1731 recent_next = recent->next;
1732 wm_history_file_free(recent);
1733 }
1734 recent = wm_history_file_new(blendfile_path);
1735 }
1736
1737 /* Add current file to the beginning of list. */
1738 BLI_addhead(&(G.recent_files), recent);
1739
1740 /* Write current file to #BLENDER_HISTORY_FILE. */
1742
1743 /* Also update most recent files on system. */
1744 GHOST_addToSystemRecentFiles(blendfile_path);
1745 }
1746}
1747
1749
1750/* -------------------------------------------------------------------- */
1770
1771#ifdef USE_THUMBNAIL_FAST_DOWNSCALE
1772static uint8_t *blend_file_thumb_fast_downscale(const uint8_t *src_rect,
1773 const int src_size[2],
1774 const int dst_size[2])
1775{
1776 /* NOTE: this is a faster alternative to #IMBScaleFilter::Box which is
1777 * especially slow in debug builds, normally debug performance isn't a
1778 * consideration however it's slow enough to get in the way of development.
1779 * In release builds this gives ~1.4x speedup. */
1780
1781 /* Scaling using a box-filter where each box uses an integer-rounded region.
1782 * Accept a slightly lower quality scale as this is only for thumbnails.
1783 * In practice the result is visually indistinguishable.
1784 *
1785 * Technically the color accumulation *could* overflow (creating some invalid pixels),
1786 * however this would require the source image to be larger than
1787 * 65,535 pixels squared (when scaling down to 256x256).
1788 * As the source input is a screenshot or a small camera render created for the thumbnail,
1789 * this isn't a concern. */
1790
1791 BLI_assert(dst_size[0] <= src_size[0] && dst_size[1] <= src_size[1]);
1792 uint8_t *dst_rect = MEM_malloc_arrayN<uint8_t>(size_t(4 * dst_size[0] * dst_size[1]), __func__);
1793
1794 /* A row, the width of the destination to accumulate pixel values into
1795 * before writing into the image. */
1796 uint32_t *accum_row = MEM_calloc_arrayN<uint32_t>(size_t(dst_size[0] * 4), __func__);
1797
1798# ifndef NDEBUG
1799 /* Assert that samples are calculated correctly. */
1800 uint64_t sample_count_all = 0;
1801# endif
1802
1803 const uint32_t src_size_x = src_size[0];
1804 const uint32_t src_size_y = src_size[1];
1805
1806 const uint32_t dst_size_x = dst_size[0];
1807 const uint32_t dst_size_y = dst_size[1];
1808 const uint8_t *src_px = src_rect;
1809
1810 uint32_t src_y = 0;
1811 for (uint32_t dst_y = 0; dst_y < dst_size_y; dst_y++) {
1812 const uint32_t src_y_beg = src_y;
1813 const uint32_t src_y_end = ((dst_y + 1) * src_size_y) / dst_size_y;
1814 for (; src_y < src_y_end; src_y++) {
1815 uint32_t *accum = accum_row;
1816 uint32_t src_x = 0;
1817 for (uint32_t dst_x = 0; dst_x < dst_size_x; dst_x++, accum += 4) {
1818 const uint32_t src_x_end = ((dst_x + 1) * src_size_x) / dst_size_x;
1819 for (; src_x < src_x_end; src_x++) {
1820 accum[0] += uint32_t(src_px[0]);
1821 accum[1] += uint32_t(src_px[1]);
1822 accum[2] += uint32_t(src_px[2]);
1823 accum[3] += uint32_t(src_px[3]);
1824 src_px += 4;
1825 }
1826 BLI_assert(src_x == src_x_end);
1827 }
1828 BLI_assert(accum == accum_row + (4 * dst_size[0]));
1829 }
1830
1831 uint32_t *accum = accum_row;
1832 uint8_t *dst_px = dst_rect + ((dst_y * dst_size_x) * 4);
1833 uint32_t src_x_beg = 0;
1834 const uint32_t span_y = src_y_end - src_y_beg;
1835 for (uint32_t dst_x = 0; dst_x < dst_size_x; dst_x++) {
1836 const uint32_t src_x_end = ((dst_x + 1) * src_size_x) / dst_size_x;
1837 const uint32_t span_x = src_x_end - src_x_beg;
1838
1839 const uint32_t sample_count = span_x * span_y;
1840 dst_px[0] = uint8_t(accum[0] / sample_count);
1841 dst_px[1] = uint8_t(accum[1] / sample_count);
1842 dst_px[2] = uint8_t(accum[2] / sample_count);
1843 dst_px[3] = uint8_t(accum[3] / sample_count);
1844 accum[0] = accum[1] = accum[2] = accum[3] = 0;
1845 accum += 4;
1846 dst_px += 4;
1847
1848 src_x_beg = src_x_end;
1849# ifndef NDEBUG
1850 sample_count_all += sample_count;
1851# endif
1852 }
1853 }
1854 BLI_assert(src_px == src_rect + (sizeof(uint8_t[4]) * src_size[0] * src_size[1]));
1855 BLI_assert(sample_count_all == size_t(src_size[0]) * size_t(src_size[1]));
1856
1857 MEM_freeN(accum_row);
1858 return dst_rect;
1859}
1860#endif /* USE_THUMBNAIL_FAST_DOWNSCALE */
1861
1862static blender::int2 blend_file_thumb_clamp_size(const int size[2], const int limit)
1863{
1865 if (size[0] > size[1]) {
1866 result.x = limit;
1867 result.y = max_ii(1, int((float(size[1]) / float(size[0])) * limit));
1868 }
1869 else {
1870 result.x = max_ii(1, int((float(size[0]) / float(size[1])) * limit));
1871 result.y = limit;
1872 }
1873 return result;
1874}
1875
1880{
1881 *r_thumb = nullptr;
1882
1883 wmWindow *win = CTX_wm_window(C);
1884 if (G.background || (win == nullptr)) {
1885 return nullptr;
1886 }
1887
1888 /* The window to capture should be a main window (without parent). */
1889 while (win->parent) {
1890 win = win->parent;
1891 }
1892
1894 int win_size[2];
1895 /* NOTE: always read from front-buffer as drawing a window can cause problems while saving,
1896 * even if this means the thumbnail from the screen-shot fails to be created, see: #98462. */
1897 ImBuf *ibuf = nullptr;
1898
1899 if (uint8_t *buffer = WM_window_pixels_read_from_frontbuffer(wm, win, win_size)) {
1900 const blender::int2 thumb_size_2x = blend_file_thumb_clamp_size(win_size, BLEN_THUMB_SIZE * 2);
1901 const blender::int2 thumb_size = blend_file_thumb_clamp_size(win_size, BLEN_THUMB_SIZE);
1902
1903#ifdef USE_THUMBNAIL_FAST_DOWNSCALE
1904 if ((thumb_size_2x[0] <= win_size[0]) && (thumb_size_2x[1] <= win_size[1])) {
1905 uint8_t *rect_2x = blend_file_thumb_fast_downscale(buffer, win_size, thumb_size_2x);
1906 uint8_t *rect = blend_file_thumb_fast_downscale(rect_2x, thumb_size_2x, thumb_size);
1907
1908 MEM_freeN(buffer);
1909 ibuf = IMB_allocFromBufferOwn(rect_2x, nullptr, thumb_size_2x.x, thumb_size_2x.y, 24);
1910
1911 BlendThumbnail *thumb = BKE_main_thumbnail_from_buffer(nullptr, rect, thumb_size);
1912 MEM_freeN(rect);
1913 *r_thumb = thumb;
1914 }
1915 else
1916#endif /* USE_THUMBNAIL_FAST_DOWNSCALE */
1917 {
1918 ibuf = IMB_allocFromBufferOwn(buffer, nullptr, win_size[0], win_size[1], 24);
1919 BLI_assert(ibuf != nullptr); /* Never expected to fail. */
1920
1921 /* File-system thumbnail image can be 256x256. */
1922 IMB_scale(ibuf, thumb_size_2x.x, thumb_size_2x.y, IMBScaleFilter::Box, false);
1923
1924 /* Thumbnail inside blend should be 128x128. */
1925 ImBuf *thumb_ibuf = IMB_dupImBuf(ibuf);
1926 IMB_scale(thumb_ibuf, thumb_size.x, thumb_size.y, IMBScaleFilter::Box, false);
1927
1928 BlendThumbnail *thumb = BKE_main_thumbnail_from_imbuf(nullptr, thumb_ibuf);
1929 IMB_freeImBuf(thumb_ibuf);
1930 *r_thumb = thumb;
1931 }
1932 }
1933
1934 if (ibuf) {
1935 /* Save metadata for quick access. */
1936 char version_str[10];
1937 SNPRINTF(version_str, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100);
1939 IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_str);
1940 }
1941
1942 /* Must be freed by caller. */
1943 return ibuf;
1944}
1945
1952 Scene *scene,
1953 bScreen *screen,
1954 BlendThumbnail **r_thumb)
1955{
1956 *r_thumb = nullptr;
1957
1958 /* Scene can be nullptr if running a script at startup and calling the save operator. */
1959 if (G.background || scene == nullptr) {
1960 return nullptr;
1961 }
1962
1963 /* Will be scaled down, but gives some nice oversampling. */
1964 ImBuf *ibuf;
1965 BlendThumbnail *thumb;
1967 const float pixelsize_old = U.pixelsize;
1968 wmWindow *windrawable_old = wm->runtime->windrawable;
1969 char err_out[256] = "unknown";
1970
1971 /* Screen if no camera found. */
1972 ScrArea *area = nullptr;
1973 ARegion *region = nullptr;
1974 View3D *v3d = nullptr;
1975
1976 if (screen != nullptr) {
1977 area = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0);
1978 if (area) {
1979 v3d = static_cast<View3D *>(area->spacedata.first);
1981 }
1982 }
1983
1984 if (scene->camera == nullptr && v3d == nullptr) {
1985 return nullptr;
1986 }
1987
1989
1990 /* Note that with scaling, this ends up being 0.5,
1991 * as it's a thumbnail, we don't need object centers and friends to be 1:1 size. */
1992 U.pixelsize = 1.0f;
1993
1994 if (scene->camera) {
1996 scene,
1997 (v3d) ? &v3d->shading : nullptr,
1998 (v3d) ? eDrawType(v3d->shading.type) : OB_SOLID,
1999 scene->camera,
2006 nullptr,
2007 nullptr,
2008 nullptr,
2009 err_out);
2010 }
2011 else {
2013 scene,
2014 OB_SOLID,
2015 v3d,
2016 region,
2021 nullptr,
2022 true,
2023 nullptr,
2024 nullptr,
2025 err_out);
2026 }
2027
2028 U.pixelsize = pixelsize_old;
2029
2030 /* Reset to old drawable. */
2031 if (windrawable_old) {
2032 wm_window_make_drawable(wm, windrawable_old);
2033 }
2034 else {
2036 }
2037
2038 if (ibuf) {
2039 /* Dirty oversampling. */
2040 ImBuf *thumb_ibuf;
2041 thumb_ibuf = IMB_dupImBuf(ibuf);
2042
2043 /* Save metadata for quick access. */
2044 char version_str[10];
2045 SNPRINTF(version_str, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100);
2047 IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_str);
2048
2049 /* BLEN_THUMB_SIZE is size of thumbnail inside blend file: 128x128. */
2051 thumb = BKE_main_thumbnail_from_imbuf(nullptr, thumb_ibuf);
2052 IMB_freeImBuf(thumb_ibuf);
2053 /* Thumbnail saved to file-system should be 256x256. */
2054 IMB_scale(ibuf,
2058 false);
2059 }
2060 else {
2061 /* '*r_thumb' needs to stay nullptr to prevent a bad thumbnail from being handled. */
2062 CLOG_WARN(&LOG, "failed to create thumbnail: %s", err_out);
2063 thumb = nullptr;
2064 }
2065
2066 /* Must be freed by caller. */
2067 *r_thumb = thumb;
2068
2069 return ibuf;
2070}
2071
2073
2074/* -------------------------------------------------------------------- */
2077
2079{
2080 char filepath[FILE_MAX];
2081
2083 BLI_path_extension_replace(filepath, sizeof(filepath), "_crash.blend");
2085 const bool success = BLO_write_file(G_MAIN, filepath, G.fileflags, &params, nullptr);
2086 printf("%s: \"%s\"\n", success ? "written" : "failed", filepath);
2087 return success;
2088}
2089
2095 const char *filepath,
2096 ReportList *reports)
2097{
2098 const int filepath_len = strlen(filepath);
2099 if (filepath_len == 0) {
2100 BKE_report(reports, RPT_ERROR, "Path is empty, cannot save");
2101 return false;
2102 }
2103
2104 if (filepath_len >= FILE_MAX) {
2105 BKE_report(reports, RPT_ERROR, "Path too long, cannot save");
2106 return false;
2107 }
2108
2109 if (bmain->is_asset_edit_file &&
2111 {
2112 BKE_report(reports, RPT_ERROR, "Cannot overwrite files that are managed by the asset system");
2113 return false;
2114 }
2115
2116 LISTBASE_FOREACH (Library *, li, &bmain->libraries) {
2117 if (BLI_path_cmp(li->runtime->filepath_abs, filepath) == 0) {
2118 BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath);
2119 return false;
2120 }
2121 }
2122
2123 return true;
2124}
2125
2130 const char *filepath,
2131 int fileflags,
2132 eBLO_WritePathRemap remap_mode,
2133 bool use_save_as_copy,
2134 ReportList *reports)
2135{
2136 Main *bmain = CTX_data_main(C);
2137 BlendThumbnail *thumb = nullptr, *main_thumb = nullptr;
2138 ImBuf *ibuf_thumb = nullptr;
2139
2140 /* NOTE: used to replace the file extension (to ensure `.blend`),
2141 * no need to now because the operator ensures,
2142 * its handy for scripts to save to a predefined name without blender editing it. */
2143
2144 if (!wm_file_write_check_with_report_on_failure(bmain, filepath, reports)) {
2145 return false;
2146 }
2147
2148 /* Call pre-save callbacks before writing preview,
2149 * that way you can generate custom file thumbnail. */
2150
2151 /* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
2152 * Runs at the end of this function, don't return beforehand. */
2154
2155 /* Check if file write permission is OK. */
2156 if (const int st_mode = BLI_exists(filepath)) {
2157 bool ok = true;
2158
2159 if (!BLI_file_is_writable(filepath)) {
2161 reports, RPT_ERROR, "Cannot save blend file, path \"%s\" is not writable", filepath);
2162 ok = false;
2163 }
2164 else if (S_ISDIR(st_mode)) {
2165 /* While the UI mostly prevents this, it's possible to save to an existing
2166 * directory from Python because the path is used unmodified.
2167 * Besides it being unlikely the user wants to replace a directory,
2168 * the file versioning logic (to create `*.blend1` files)
2169 * would rename the directory with a `1` suffix, see #134101. */
2171 reports, RPT_ERROR, "Cannot save blend file, path \"%s\" is a directory", filepath);
2172 ok = false;
2173 }
2174
2175 if (!ok) {
2177 return false;
2178 }
2179 }
2180
2182
2183 /* Enforce full override check/generation on file save. */
2185
2186 /* NOTE: Ideally we would call `WM_redraw_windows` here to remove any open menus.
2187 * But we can crash if saving from a script, see #92704 & #97627.
2188 * Just checking `!G.background && BLI_thread_is_main()` is not sufficient to fix this.
2189 * Additionally some EGL configurations don't support reading the front-buffer
2190 * immediately after drawing, see: #98462. In that case off-screen drawing is necessary. */
2191
2192 /* Don't forget not to return without! */
2193 WM_cursor_wait(true);
2194
2195 if (U.file_preview_type != USER_FILE_PREVIEW_NONE) {
2196 /* Blend file thumbnail.
2197 *
2198 * - Save before exiting edit-mode, otherwise evaluated-mesh for shared data gets corrupted.
2199 * See #27765.
2200 * - Main can store a `.blend` thumbnail,
2201 * useful for background-mode or thumbnail customization.
2202 */
2203 main_thumb = thumb = bmain->blen_thumb;
2204 if (thumb != nullptr) {
2205 /* In case we are given a valid thumbnail data, just generate image from it. */
2206 ibuf_thumb = BKE_main_thumbnail_to_imbuf(nullptr, thumb);
2207 }
2208 else if (BLI_thread_is_main()) {
2209 int file_preview_type = U.file_preview_type;
2210
2211 if (file_preview_type == USER_FILE_PREVIEW_AUTO) {
2212 Scene *scene = CTX_data_scene(C);
2213 bScreen *screen = CTX_wm_screen(C);
2214 bool do_render = (scene != nullptr && scene->camera != nullptr && screen != nullptr &&
2215 (BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0) != nullptr));
2216 file_preview_type = do_render ? USER_FILE_PREVIEW_CAMERA : USER_FILE_PREVIEW_SCREENSHOT;
2217 }
2218
2219 switch (file_preview_type) {
2221 ibuf_thumb = blend_file_thumb_from_screenshot(C, &thumb);
2222 break;
2223 }
2225 ibuf_thumb = blend_file_thumb_from_camera(
2226 C, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
2227 break;
2228 }
2229 default:
2231 }
2232 }
2233 }
2234
2235 /* Operator now handles overwrite checks. */
2236
2237 if (G.fileflags & G_FILE_AUTOPACK) {
2238 BKE_packedfile_pack_all(bmain, reports, false);
2239 }
2240
2242
2243 /* XXX(ton): temp solution to solve bug, real fix coming. */
2244 bmain->recovered = false;
2245
2246 BlendFileWriteParams blend_write_params{};
2247 blend_write_params.remap_mode = remap_mode;
2248 blend_write_params.use_save_versions = true;
2249 blend_write_params.use_save_as_copy = use_save_as_copy;
2250 blend_write_params.thumb = thumb;
2251
2252 const bool success = BLO_write_file(bmain, filepath, fileflags, &blend_write_params, reports);
2253
2254 if (success) {
2255 const bool do_history_file_update = (G.background == false) &&
2256 (CTX_wm_manager(C)->op_undo_depth == 0);
2257
2258 if (use_save_as_copy == false) {
2259 STRNCPY(bmain->filepath, filepath); /* Is guaranteed current file. */
2260 }
2261
2262 SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
2263
2264 /* Prevent background mode scripts from clobbering history. */
2265 if (do_history_file_update) {
2267 }
2268
2269 /* Run this function after because the file can't be written before the blend is. */
2270 if (ibuf_thumb) {
2271 IMB_thumb_delete(filepath, THB_FAIL); /* Without this a failed thumb overrides. */
2272 ibuf_thumb = IMB_thumb_create(filepath, THB_LARGE, THB_SOURCE_BLEND, ibuf_thumb);
2273 }
2274 }
2275
2277 bmain, success ? BKE_CB_EVT_SAVE_POST : BKE_CB_EVT_SAVE_POST_FAIL, filepath);
2278
2279 if (ibuf_thumb) {
2280 IMB_freeImBuf(ibuf_thumb);
2281 }
2282 if (thumb && thumb != main_thumb) {
2283 MEM_freeN(thumb);
2284 }
2285
2286 WM_cursor_wait(false);
2287
2288 return success;
2289}
2290
2292
2293/* -------------------------------------------------------------------- */
2296
2297static void wm_autosave_location(char filepath[FILE_MAX])
2298{
2299 const int pid = abs(getpid());
2300 char filename[1024];
2301
2302 /* Normally there is no need to check for this to be nullptr,
2303 * however this runs on exit when it may be cleared. */
2304 Main *bmain = G_MAIN;
2305 const char *blendfile_path = bmain ? BKE_main_blendfile_path(bmain) : nullptr;
2306
2307 if (blendfile_path && (blendfile_path[0] != '\0')) {
2308 const char *basename = BLI_path_basename(blendfile_path);
2309 int len = strlen(basename) - 6;
2310 SNPRINTF(filename, "%.*s_%d_autosave.blend", len, basename, pid);
2311 }
2312 else {
2313 SNPRINTF(filename, "%d_autosave.blend", pid);
2314 }
2315
2316 const char *tempdir_base = BKE_tempdir_base();
2317 BLI_path_join(filepath, FILE_MAX, tempdir_base, filename);
2318}
2319
2321{
2322 if (wm->file_saved) {
2323 /* When file is already saved, skip creating an auto-save file, see: #146003 */
2324 return true;
2325 }
2326
2327 char filepath[FILE_MAX];
2328 wm_autosave_location(filepath);
2329
2330 /* Technically, we could always just save here, but that would cause performance regressions
2331 * compared to when the #MemFile undo step was used for saving undo-steps. So for now just skip
2332 * auto-save when we are in a mode where auto-save wouldn't have worked previously anyway. This
2333 * check can be removed once the performance regressions have been solved. */
2334 if (ED_undosys_stack_memfile_get_if_active(wm->runtime->undo_stack) != nullptr) {
2335 WM_autosave_write(wm, bmain);
2336 return true;
2337 }
2338 if ((U.uiflag & USER_GLOBALUNDO) == 0) {
2339 WM_autosave_write(wm, bmain);
2340 return true;
2341 }
2342 /* Can't auto-save with MemFile right now, try again later. */
2343 return false;
2344}
2345
2347{
2348 return wm->autosave_scheduled;
2349}
2350
2352{
2354
2355 char filepath[FILE_MAX];
2356 wm_autosave_location(filepath);
2357 /* Save as regular blend file with recovery information and always compress them, see: !132685.
2358 */
2359 const int fileflags = G.fileflags | G_FILE_RECOVER_WRITE | G_FILE_COMPRESS;
2360
2361 /* Error reporting into console. */
2363 BLO_write_file(bmain, filepath, fileflags, &params, nullptr);
2364
2365 /* Restart auto-save timer. */
2368
2369 wm->autosave_scheduled = false;
2370}
2371
2372static void wm_autosave_timer_begin_ex(wmWindowManager *wm, double timestep)
2373{
2375
2376 if (U.flag & USER_AUTOSAVE) {
2377 wm->autosavetimer = WM_event_timer_add(wm, nullptr, TIMERAUTOSAVE, timestep);
2378 }
2379}
2380
2385
2387{
2388 if (wm->autosavetimer) {
2389 WM_event_timer_remove(wm, nullptr, wm->autosavetimer);
2390 wm->autosavetimer = nullptr;
2391 }
2392}
2393
2398
2400{
2402
2403 /* If a modal operator is running, don't autosave because we might not be in
2404 * a valid state to save. But try again in 10ms. */
2405 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
2406 LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
2407 if (handler_base->type == WM_HANDLER_TYPE_OP) {
2408 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
2409 if (handler->op) {
2411 return;
2412 }
2413 }
2414 }
2415 }
2416
2417 wm->autosave_scheduled = false;
2418 if (!wm_autosave_write_try(bmain, wm)) {
2419 wm->autosave_scheduled = true;
2420 }
2421 /* Restart the timer after file write, just in case file write takes a long time. */
2423}
2424
2426{
2427 char filepath[FILE_MAX];
2428
2429 wm_autosave_location(filepath);
2430
2431 if (BLI_exists(filepath)) {
2432 char filepath_quit[FILE_MAX];
2433 BLI_path_join(filepath_quit, sizeof(filepath_quit), BKE_tempdir_base(), BLENDER_QUIT_FILE);
2434
2435 /* For global undo; remove temporarily saved file, otherwise rename. */
2436 if (U.uiflag & USER_GLOBALUNDO) {
2437 BLI_delete(filepath, false, false);
2438 }
2439 else {
2440 BLI_rename_overwrite(filepath, filepath_quit);
2441 }
2442 }
2443}
2444
2446
2447/* -------------------------------------------------------------------- */
2450
2453{
2454 PropertyRNA *prop;
2455
2456 /* So it's possible to reset app-template settings without resetting other defaults. */
2457 prop = RNA_def_boolean(ot->srna,
2458 "use_factory_startup_app_template_only",
2459 false,
2460 "Factory Startup App-Template Only",
2461 "");
2463}
2464
2466
2467/* -------------------------------------------------------------------- */
2474
2475void wm_open_init_load_ui(wmOperator *op, bool use_prefs)
2476{
2477 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "load_ui");
2478 if (!RNA_property_is_set(op->ptr, prop)) {
2479 bool value = use_prefs ? ((U.flag & USER_FILENOUI) == 0) : ((G.fileflags & G_FILE_NO_UI) == 0);
2480
2481 RNA_property_boolean_set(op->ptr, prop, value);
2482 }
2483}
2484
2485bool wm_open_init_use_scripts(wmOperator *op, bool use_prefs)
2486{
2487 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
2488 bool use_scripts_autoexec_check = false;
2489 if (!RNA_property_is_set(op->ptr, prop)) {
2490 /* Use #G_FLAG_SCRIPT_AUTOEXEC rather than the userpref because this means if
2491 * the flag has been disabled from the command line, then opening
2492 * from the menu won't enable this setting. */
2493 bool value = use_prefs ? ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) :
2494 ((G.f & G_FLAG_SCRIPT_AUTOEXEC) != 0);
2495
2496 RNA_property_boolean_set(op->ptr, prop, value);
2497 use_scripts_autoexec_check = true;
2498 }
2499 return use_scripts_autoexec_check;
2500}
2501
2503
2504/* -------------------------------------------------------------------- */
2507
2513{
2514 Main *bmain = CTX_data_main(C);
2516 wmWindow *win = CTX_wm_window(C);
2517 char filepath[FILE_MAX];
2518 int fileflags;
2519
2520 const char *app_template = U.app_template[0] ? U.app_template : nullptr;
2521 const std::optional<std::string> cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG,
2522 app_template);
2523 if (!cfgdir.has_value()) {
2524 BKE_report(op->reports, RPT_ERROR, "Unable to create user config path");
2525 return OPERATOR_CANCELLED;
2526 }
2527
2528 /* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
2529 * Runs at the end of this function, don't return beforehand. */
2532
2533 /* Check current window and close it if temp. */
2534 if (win && WM_window_is_temp_screen(win)) {
2535 wm_window_close(C, wm, win);
2536 }
2537
2538 /* Update keymaps in user preferences. */
2540
2541 BLI_path_join(filepath, sizeof(filepath), cfgdir->c_str(), BLENDER_STARTUP_FILE);
2542
2543 CLOG_INFO_NOCHECK(&LOG, "Writing startup file: \"%s\" ", filepath);
2544
2546
2547 /* Force save as regular blend file. */
2548 fileflags = G.fileflags & ~G_FILE_COMPRESS;
2549
2550 BlendFileWriteParams blend_write_params{};
2551 /* Make all paths absolute when saving the startup file.
2552 * On load the `G.main->filepath` will be empty so the paths
2553 * won't have a base for resolving the relative paths. */
2554 blend_write_params.remap_mode = BLO_WRITE_PATH_REMAP_ABSOLUTE;
2555 /* Don't apply any path changes to the current blend file. */
2556 blend_write_params.use_save_as_copy = true;
2557
2558 const bool success = BLO_write_file(
2559 bmain, filepath, fileflags, &blend_write_params, op->reports);
2560
2562
2563 if (success) {
2564 BKE_report(op->reports, RPT_INFO, "Startup file saved");
2565 return OPERATOR_FINISHED;
2566 }
2567 CLOG_WARN(&LOG, "Failed to write startup file");
2568 return OPERATOR_CANCELLED;
2569}
2570
2572 wmOperator *op,
2573 const wmEvent * /*event*/)
2574{
2575 if (!U.app_template[0]) {
2577 op,
2578 IFACE_("Overwrite Startup File"),
2579 IFACE_("Blender will start next time as it is now."),
2580 IFACE_("Overwrite"),
2582 false);
2583 }
2584
2585 /* A different message if this is overriding a specific template startup file. */
2586 char display_name[FILE_MAX];
2587 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
2588 std::string message = fmt::format(
2589 fmt::runtime(IFACE_("Template \"{}\" will start next time as it is now.")),
2590 IFACE_(display_name));
2592 op,
2593 IFACE_("Overwrite Template Startup File"),
2594 message.c_str(),
2595 IFACE_("Overwrite"),
2597 false);
2598}
2599
2601{
2602 ot->name = "Save Startup File";
2603 ot->idname = "WM_OT_save_homefile";
2604 ot->description = "Make the current file the default startup file";
2605
2606 ot->invoke = wm_homefile_write_invoke;
2607 ot->exec = wm_homefile_write_exec;
2608}
2609
2611
2612/* -------------------------------------------------------------------- */
2615
2616/* Only save the prefs block. operator entry. */
2618{
2620
2621 /* Update keymaps in user preferences. */
2623
2624 const bool success = BKE_blendfile_userdef_write_all(op->reports);
2625
2626 return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2627}
2628
2630{
2631 ot->name = "Save Preferences";
2632 ot->idname = "WM_OT_save_userpref";
2633 ot->description = "Make the current preferences default";
2634
2635 ot->invoke = WM_operator_confirm;
2636 ot->exec = wm_userpref_write_exec;
2637}
2638
2640
2641/* -------------------------------------------------------------------- */
2644
2648static void wm_userpref_read_exceptions(UserDef *userdef_curr, const UserDef *userdef_prev)
2649{
2650#define USERDEF_RESTORE(member) \
2651 { \
2652 userdef_curr->member = userdef_prev->member; \
2653 } \
2654 ((void)0)
2655
2656 /* Current visible preferences category. */
2657 USERDEF_RESTORE(space_data.section_active);
2658
2659#undef USERDEF_RESTORE
2660}
2661
2663 Main *bmain,
2664 PointerRNA *ptr_a,
2665 PointerRNA *ptr_b)
2666{
2668 PropertyRNA *iterprop = RNA_struct_iterator_property(ptr_a->type);
2669 BLI_assert(ptr_a->type == ptr_b->type);
2670 RNA_property_collection_begin(ptr_a, iterprop, &iter);
2671 for (; iter.valid; RNA_property_collection_next(&iter)) {
2672 PropertyRNA *prop = static_cast<PropertyRNA *>(iter.ptr.data);
2673 if (STREQ(RNA_property_identifier(prop), "rna_type")) {
2674 continue;
2675 }
2676 switch (RNA_property_type(prop)) {
2677 case PROP_POINTER: {
2678 PointerRNA ptr_sub_a = RNA_property_pointer_get(ptr_a, prop);
2679 PointerRNA ptr_sub_b = RNA_property_pointer_get(ptr_b, prop);
2680 rna_struct_update_when_changed(C, bmain, &ptr_sub_a, &ptr_sub_b);
2681 break;
2682 }
2683 case PROP_COLLECTION:
2684 /* Don't handle collections. */
2685 break;
2686 default: {
2687 if (!RNA_property_equals(bmain, ptr_a, ptr_b, prop, RNA_EQ_STRICT)) {
2688 RNA_property_update(C, ptr_b, prop);
2689 }
2690 }
2691 }
2692 }
2694}
2695
2697 Main *bmain,
2698 UserDef *userdef_prev,
2699 UserDef *userdef_curr)
2700{
2701 PointerRNA ptr_a = RNA_pointer_create_discrete(nullptr, &RNA_Preferences, userdef_prev);
2702 PointerRNA ptr_b = RNA_pointer_create_discrete(nullptr, &RNA_Preferences, userdef_curr);
2703 const bool is_dirty = userdef_curr->runtime.is_dirty;
2704
2705 rna_struct_update_when_changed(C, bmain, &ptr_a, &ptr_b);
2706
2709
2710 userdef_curr->runtime.is_dirty = is_dirty;
2711}
2712
2714{
2715 Main *bmain = CTX_data_main(C);
2716 const bool use_data = false;
2717 const bool use_userdef = true;
2718 const bool use_factory_settings = STREQ(op->type->idname, "WM_OT_read_factory_userpref");
2719 const bool use_factory_settings_app_template_only =
2720 (use_factory_settings && RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only"));
2721
2723
2724 UserDef U_backup = blender::dna::shallow_copy(U);
2725
2726 wmHomeFileRead_Params read_homefile_params{};
2727 read_homefile_params.use_data = use_data;
2728 read_homefile_params.use_userdef = use_userdef;
2729 read_homefile_params.use_factory_settings = use_factory_settings;
2730 read_homefile_params.use_factory_settings_app_template_only =
2731 use_factory_settings_app_template_only;
2732 read_homefile_params.use_empty_data = false;
2733 read_homefile_params.filepath_startup_override = nullptr;
2735 wm_homefile_read(C, &read_homefile_params, op->reports);
2736
2737 wm_userpref_read_exceptions(&U, &U_backup);
2738 SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT);
2739
2740 wm_userpref_update_when_changed(C, bmain, &U_backup, &U);
2741
2742 if (use_factory_settings) {
2743 U.runtime.is_dirty = true;
2744 }
2745
2747
2748 /* Needed to recalculate UI scaling values (eg, #UserDef.inv_scale_factor). */
2749 wm_window_clear_drawable(static_cast<wmWindowManager *>(bmain->wm.first));
2750
2752
2753 return OPERATOR_FINISHED;
2754}
2755
2757{
2758 ot->name = "Load Preferences";
2759 ot->idname = "WM_OT_read_userpref";
2760 ot->description = "Load last saved preferences";
2761
2762 ot->invoke = WM_operator_confirm;
2763 ot->exec = wm_userpref_read_exec;
2764}
2765
2767 wmOperator *op,
2768 const wmEvent * /*event*/)
2769{
2770 std::string title;
2771
2772 const bool template_only = U.app_template[0] &&
2773 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only");
2774
2775 if (template_only) {
2776 char display_name[FILE_MAX];
2777 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
2778 title = fmt::format(fmt::runtime(IFACE_("Load Factory \"{}\" Preferences.")),
2779 IFACE_(display_name));
2780 }
2781 else {
2782 title = IFACE_("Load Factory Blender Preferences");
2783 }
2784
2786 C,
2787 op,
2788 title.c_str(),
2789 IFACE_("To make changes to Preferences permanent, use \"Save Preferences\""),
2790 IFACE_("Load"),
2792 false);
2793}
2794
2796{
2797 ot->name = "Load Factory Preferences";
2798 ot->idname = "WM_OT_read_factory_userpref";
2799 ot->description =
2800 "Load factory default preferences. "
2801 "To make changes to preferences permanent, use \"Save Preferences\"";
2802
2803 ot->invoke = wm_userpref_read_invoke;
2804 ot->exec = wm_userpref_read_exec;
2805
2807}
2808
2810
2811/* -------------------------------------------------------------------- */
2814
2821
2823{
2824 ot->name = "Reload History File";
2825 ot->idname = "WM_OT_read_history";
2826 ot->description = "Reloads history and bookmarks";
2827
2828 ot->invoke = WM_operator_confirm;
2830
2831 /* This operator is only used for loading settings from a previous blender install. */
2832 ot->flag = OPTYPE_INTERNAL;
2833}
2834
2836
2837/* -------------------------------------------------------------------- */
2842
2844{
2845 const bool use_factory_startup_and_userdef = STREQ(op->type->idname,
2846 "WM_OT_read_factory_settings");
2847 const bool use_factory_settings = use_factory_startup_and_userdef ||
2848 RNA_boolean_get(op->ptr, "use_factory_startup");
2849 const bool use_factory_settings_app_template_only =
2850 (use_factory_startup_and_userdef &&
2851 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only"));
2852
2853 bool use_userdef = false;
2854 char filepath_buf[FILE_MAX];
2855 const char *filepath = nullptr;
2856 UserDef U_backup = blender::dna::shallow_copy(U);
2857
2858 if (!use_factory_settings) {
2859 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath");
2860
2861 /* This can be used when loading of a start-up file should only change
2862 * the scene content but keep the blender UI as it is. */
2863 wm_open_init_load_ui(op, true);
2864 SET_FLAG_FROM_TEST(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
2865
2866 if (RNA_property_is_set(op->ptr, prop)) {
2867 RNA_property_string_get(op->ptr, prop, filepath_buf);
2868 filepath = filepath_buf;
2869 if (BLI_access(filepath, R_OK)) {
2871 op->reports, RPT_ERROR, "Cannot read alternative start-up file: \"%s\"", filepath);
2872 return OPERATOR_CANCELLED;
2873 }
2874 }
2875 }
2876 else {
2877 if (use_factory_startup_and_userdef) {
2878 /* Always load UI for factory settings (prefs will re-init). */
2879 G.fileflags &= ~G_FILE_NO_UI;
2880 /* Always load preferences with factory settings. */
2881 use_userdef = true;
2882 }
2883 }
2884
2885 /* Close any user-loaded fonts. */
2887
2888 char app_template_buf[sizeof(U.app_template)];
2889 const char *app_template;
2890 PropertyRNA *prop_app_template = RNA_struct_find_property(op->ptr, "app_template");
2891 const bool use_splash = !use_factory_settings && RNA_boolean_get(op->ptr, "use_splash");
2892 const bool use_empty_data = RNA_boolean_get(op->ptr, "use_empty");
2893
2894 if (prop_app_template && RNA_property_is_set(op->ptr, prop_app_template)) {
2895 RNA_property_string_get(op->ptr, prop_app_template, app_template_buf);
2896 app_template = app_template_buf;
2897
2898 if (!use_factory_settings) {
2899 /* Always load preferences when switching templates with own preferences. */
2902 }
2903
2904 /* Turn override off, since we're explicitly loading a different app-template. */
2906 }
2907 else {
2908 /* Normally nullptr, only set when overriding from the command-line. */
2910 }
2911
2912 if (use_userdef) {
2914 }
2915
2916 wmHomeFileRead_Params read_homefile_params{};
2917 read_homefile_params.use_data = true;
2918 read_homefile_params.use_userdef = use_userdef;
2919 read_homefile_params.use_factory_settings = use_factory_settings;
2920 read_homefile_params.use_factory_settings_app_template_only =
2921 use_factory_settings_app_template_only;
2922 read_homefile_params.use_empty_data = use_empty_data;
2923 read_homefile_params.filepath_startup_override = filepath;
2924 read_homefile_params.app_template_override = app_template;
2925 wm_homefile_read(C, &read_homefile_params, op->reports);
2926
2927 if (use_splash) {
2929 }
2930
2931 if (use_userdef) {
2932 wm_userpref_read_exceptions(&U, &U_backup);
2933 SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT);
2934
2935 if (use_factory_settings) {
2936 U.runtime.is_dirty = true;
2937 }
2938 }
2939
2940 if (use_userdef) {
2942 }
2943
2944 if (G.fileflags & G_FILE_NO_UI) {
2946 }
2947
2948 return OPERATOR_FINISHED;
2949}
2950
2952{
2954 "WM_OT_read_homefile",
2956 (IDProperty *)user_data,
2957 nullptr);
2958}
2959
2969
2971{
2972 PropertyRNA *prop;
2973
2974 prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", "");
2976
2977 prop = RNA_def_boolean(
2978 ot->srna,
2979 "use_empty",
2980 false,
2981 "Empty",
2982 "After loading, remove everything except scenes, windows, and workspaces. This makes it "
2983 "possible to load the startup file with its scene configuration and window layout intact, "
2984 "but no objects, materials, animations, ...");
2986}
2987
2989{
2990 PropertyRNA *prop;
2991 ot->name = "Reload Start-Up File";
2992 ot->idname = "WM_OT_read_homefile";
2993 ot->description = "Open the default file";
2994
2995 ot->invoke = wm_homefile_read_invoke;
2996 ot->exec = wm_homefile_read_exec;
2997
2998 prop = RNA_def_string_file_path(ot->srna,
2999 "filepath",
3000 nullptr,
3001 FILE_MAX,
3002 "File Path",
3003 "Path to an alternative start-up file");
3005
3006 /* So scripts can use an alternative start-up file without the UI. */
3007 prop = RNA_def_boolean(
3008 ot->srna, "load_ui", true, "Load UI", "Load user interface setup from the .blend file");
3010
3011 /* So the splash can be kept open after loading a file (for templates). */
3012 prop = RNA_def_boolean(ot->srna, "use_splash", false, "Splash", "");
3014
3015 /* So scripts can load factory-startup without resetting preferences
3016 * (which has other implications such as reloading all add-ons).
3017 * Match naming for `--factory-startup` command line argument. */
3018 prop = RNA_def_boolean(ot->srna,
3019 "use_factory_startup",
3020 false,
3021 "Factory Startup",
3022 "Load the default ('factory startup') blend file. "
3023 "This is independent of the normal start-up file that the user can save");
3026
3028
3029 /* Omit poll to run in background mode. */
3030}
3031
3033 wmOperator *op,
3034 const wmEvent * /*event*/)
3035{
3037 CTX_wm_manager(C));
3038 std::string title;
3039 const bool template_only = U.app_template[0] &&
3040 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only");
3041
3042 if (template_only) {
3043 char display_name[FILE_MAX];
3044 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
3045 title = fmt::format(fmt::runtime(IFACE_("Load Factory \"{}\" Startup File and Preferences")),
3046 IFACE_(display_name));
3047 }
3048 else {
3049 title = IFACE_("Load Factory Default Startup File and Preferences");
3050 }
3051
3053 C,
3054 op,
3055 title.c_str(),
3056 unsaved ? IFACE_("To make changes to Preferences permanent, use \"Save Preferences\".\n"
3057 "Warning: Your file is unsaved! Proceeding will abandon your changes.") :
3058 IFACE_("To make changes to Preferences permanent, use \"Save Preferences\"."),
3059 IFACE_("Load"),
3061 false);
3062}
3063
3065{
3066 ot->name = "Load Factory Settings";
3067 ot->idname = "WM_OT_read_factory_settings";
3068 ot->description =
3069 "Load factory default startup file and preferences. "
3070 "To make changes permanent, use \"Save Startup File\" and \"Save Preferences\"";
3071
3073 ot->exec = wm_homefile_read_exec;
3074 /* Omit poll to run in background mode. */
3075
3077
3079}
3080
3082
3083/* -------------------------------------------------------------------- */
3086
3091 const char *filepath,
3092 const bool use_scripts_autoexec_check,
3093 ReportList *reports)
3094{
3095 /* XXX: wm in context is not set correctly after #WM_file_read -> crash. */
3096 /* Do it before for now, but is this correct with multiple windows? */
3098
3099 const bool success = WM_file_read(C, filepath, use_scripts_autoexec_check, reports);
3100
3101 return success;
3102}
3103
3104/* Generic operator state utilities. */
3105
3106static void create_operator_state(wmOperatorType *ot, int first_state)
3107{
3108 PropertyRNA *prop = RNA_def_int(
3109 ot->srna, "state", first_state, INT32_MIN, INT32_MAX, "State", "", INT32_MIN, INT32_MAX);
3112}
3113
3115{
3116 return RNA_int_get(op->ptr, "state");
3117}
3118
3120{
3121 RNA_int_set(op->ptr, "state", state);
3122}
3123
3128
3130 wmOperator *op,
3131 OperatorDispatchTarget *targets)
3132{
3133 int state = get_operator_state(op);
3134 for (int i = 0; targets[i].run; i++) {
3135 OperatorDispatchTarget target = targets[i];
3136 if (target.state == state) {
3137 return target.run(C, op);
3138 }
3139 }
3141 return OPERATOR_CANCELLED;
3142}
3143
3145
3146/* -------------------------------------------------------------------- */
3149
3150enum {
3154};
3155
3157
3159{
3161 "WM_OT_open_mainfile",
3163 (IDProperty *)user_data,
3164 nullptr);
3165}
3166
3181
3183{
3185
3186 Main *bmain = CTX_data_main(C);
3187 const char *blendfile_path = BKE_main_blendfile_path(bmain);
3188
3189 if (CTX_wm_window(C) == nullptr) {
3190 /* In rare cases this could happen, when trying to invoke in background
3191 * mode on load for example. Don't use poll for this because exec()
3192 * can still run without a window. */
3193 BKE_report(op->reports, RPT_ERROR, "Context window not set");
3194 return OPERATOR_CANCELLED;
3195 }
3196
3197 /* If possible, get the name of the most recently used `.blend` file. */
3198 if (G.recent_files.first) {
3199 RecentFile *recent = static_cast<RecentFile *>(G.recent_files.first);
3200 blendfile_path = recent->filepath;
3201 }
3202
3203 RNA_string_set(op->ptr, "filepath", blendfile_path);
3204 wm_open_init_load_ui(op, true);
3205 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3206 UNUSED_VARS(use_scripts_autoexec_check); /* The user can set this in the UI. */
3207 op->customdata = nullptr;
3208
3210
3212}
3213
3215{
3216 char filepath[FILE_MAX];
3217 bool success;
3218
3219 RNA_string_get(op->ptr, "filepath", filepath);
3220 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3221
3222 /* For file opening, also print in console for warnings, not only errors. */
3224
3225 /* Re-use last loaded setting so we can reload a file without changing. */
3226 wm_open_init_load_ui(op, false);
3227 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, false);
3228
3229 SET_FLAG_FROM_TEST(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
3231 success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, op->reports);
3232
3233 if (success) {
3234 if (G.fileflags & G_FILE_NO_UI) {
3236 }
3238 return OPERATOR_FINISHED;
3239 }
3240 return OPERATOR_CANCELLED;
3241}
3242
3249
3254
3256 wmOperator *op,
3257 const wmEvent * /*event*/)
3258{
3259 return wm_open_mainfile_dispatch(C, op);
3260}
3261
3266
3268 wmOperatorType * /*ot*/,
3269 PointerRNA *ptr)
3270{
3271 if (!RNA_struct_property_is_set(ptr, "filepath")) {
3272 return "";
3273 }
3274
3275 char filepath[FILE_MAX];
3276 RNA_string_get(ptr, "filepath", filepath);
3277
3278 BLI_stat_t stats;
3279 if (BLI_stat(filepath, &stats) == -1) {
3280 return fmt::format("{}\n\n{}", filepath, TIP_("File Not Found"));
3281 }
3282
3283 /* Date. */
3284 char date_str[FILELIST_DIRENTRY_DATE_LEN];
3285 char time_str[FILELIST_DIRENTRY_TIME_LEN];
3286 bool is_today, is_yesterday;
3288 nullptr, int64_t(stats.st_mtime), false, time_str, date_str, &is_today, &is_yesterday);
3289 if (is_today || is_yesterday) {
3290 STRNCPY(date_str, is_today ? TIP_("Today") : TIP_("Yesterday"));
3291 }
3292
3293 /* Size. */
3294 char size_str[FILELIST_DIRENTRY_SIZE_LEN];
3295 BLI_filelist_entry_size_to_string(nullptr, uint64_t(stats.st_size), false, size_str);
3296
3297 return fmt::format("{}\n\n{}: {} {}\n{}: {}",
3298 filepath,
3299 TIP_("Modified"),
3300 date_str,
3301 time_str,
3302 TIP_("Size"),
3303 size_str);
3304}
3305
3306/* Currently fits in a pointer. */
3309};
3310BLI_STATIC_ASSERT(sizeof(FileRuntime) <= sizeof(void *), "Struct must not exceed pointer size");
3311
3313{
3314 FileRuntime *file_info = (FileRuntime *)&op->customdata;
3315 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
3316 bool is_untrusted = false;
3317 char filepath[FILE_MAX];
3318 char *lslash;
3319
3320 RNA_string_get(op->ptr, "filepath", filepath);
3321
3322 /* Get the directory. */
3323 lslash = (char *)BLI_path_slash_rfind(filepath);
3324 if (lslash) {
3325 *(lslash + 1) = '\0';
3326 }
3327
3328 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
3329 if (BKE_autoexec_match(filepath) == true) {
3330 RNA_property_boolean_set(op->ptr, prop, false);
3331 is_untrusted = true;
3332 }
3333 }
3334
3335 if (file_info) {
3336 file_info->is_untrusted = is_untrusted;
3337 }
3338
3339 return is_untrusted;
3340}
3341
3342static void wm_open_mainfile_ui(bContext * /*C*/, wmOperator *op)
3343{
3344 FileRuntime *file_info = (FileRuntime *)&op->customdata;
3345 uiLayout *layout = op->layout;
3346 const char *autoexec_text;
3347
3348 layout->prop(op->ptr, "load_ui", UI_ITEM_NONE, std::nullopt, ICON_NONE);
3349
3350 uiLayout *col = &layout->column(false);
3351 if (file_info->is_untrusted) {
3352 autoexec_text = IFACE_("Trusted Source [Untrusted Path]");
3353 col->active_set(false);
3354 col->enabled_set(false);
3355 }
3356 else {
3357 autoexec_text = IFACE_("Trusted Source");
3358 }
3359
3360 col->prop(op->ptr, "use_scripts", UI_ITEM_NONE, autoexec_text, ICON_NONE);
3361}
3362
3364{
3365 RNA_def_boolean(ot->srna,
3366 "use_scripts",
3367 false,
3368 "Trusted Source",
3369 "Allow .blend file to execute scripts automatically, default available from "
3370 "system preferences");
3371}
3372
3374{
3375 ot->name = "Open";
3376 ot->idname = "WM_OT_open_mainfile";
3377 ot->description = "Open a Blender file";
3378 ot->get_description = wm_open_mainfile_get_description;
3379
3380 ot->invoke = wm_open_mainfile_invoke;
3381 ot->exec = wm_open_mainfile_exec;
3382 ot->check = wm_open_mainfile_check;
3383 ot->ui = wm_open_mainfile_ui;
3384 /* Omit window poll so this can work in background mode. */
3385
3393
3395 ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file");
3396
3398
3400 ot->srna, "display_file_selector", true, "Display File Selector", "");
3402
3404}
3405
3407
3408/* -------------------------------------------------------------------- */
3411
3413 wmOperator *op,
3414 const wmEvent * /*event*/)
3415{
3416 std::string message = IFACE_("Any unsaved changes will be lost.");
3418 message += "\n";
3419 message += IFACE_("Warning: There are unsaved external image(s).");
3420 }
3421
3423 op,
3424 IFACE_("Revert to the Saved File"),
3425 message.c_str(),
3426 IFACE_("Revert"),
3428 false);
3429}
3430
3432{
3433 Main *bmain = CTX_data_main(C);
3434 bool success;
3435 char filepath[FILE_MAX];
3436
3437 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, false);
3438
3440
3441 STRNCPY(filepath, BKE_main_blendfile_path(bmain));
3442 success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, op->reports);
3443
3444 if (success) {
3445 return OPERATOR_FINISHED;
3446 }
3447 return OPERATOR_CANCELLED;
3448}
3449
3451{
3452 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3453 return (blendfile_path[0] != '\0');
3454}
3455
3457{
3458 ot->name = "Revert";
3459 ot->idname = "WM_OT_revert_mainfile";
3460 ot->description = "Reload the saved file";
3461
3462 ot->invoke = wm_revert_mainfile_invoke;
3465
3467}
3468
3470
3471/* -------------------------------------------------------------------- */
3474
3476 const bool use_scripts_autoexec_check,
3477 ReportList *reports)
3478{
3479 char filepath[FILE_MAX];
3480 BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE);
3481 G.fileflags |= G_FILE_RECOVER_READ;
3482 const bool success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, reports);
3483 G.fileflags &= ~G_FILE_RECOVER_READ;
3484 return success;
3485}
3486
3488 wmOperator *op,
3489 const bool use_scripts_autoexec_check)
3490{
3492 if (WM_file_recover_last_session(C, use_scripts_autoexec_check, op->reports)) {
3493 if (!G.background) {
3494 wmOperatorType *ot = op->type;
3495 PointerRNA *props_ptr = MEM_new<PointerRNA>(__func__);
3497 RNA_boolean_set(props_ptr, "use_scripts", true);
3499 }
3500 return OPERATOR_FINISHED;
3501 }
3502 return OPERATOR_CANCELLED;
3503}
3504
3506{
3507 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3508 return wm_recover_last_session_impl(C, op, use_scripts_autoexec_check);
3509}
3510
3512{
3514 "WM_OT_recover_last_session",
3516 (IDProperty *)user_data,
3517 nullptr);
3518}
3519
3521 wmOperator *op,
3522 const wmEvent * /*event*/)
3523{
3524 /* Keep the current setting instead of using the preferences since a file selector
3525 * doesn't give us the option to change the setting. */
3526 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, false);
3527
3530 {
3531 return OPERATOR_INTERFACE;
3532 }
3533 return wm_recover_last_session_impl(C, op, use_scripts_autoexec_check);
3534}
3535
3537{
3538 ot->name = "Recover Last Session";
3539 ot->idname = "WM_OT_recover_last_session";
3540 ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")";
3541
3544
3546}
3547
3549
3550/* -------------------------------------------------------------------- */
3553
3555{
3556 char filepath[FILE_MAX];
3557 bool success;
3558
3559 RNA_string_get(op->ptr, "filepath", filepath);
3560 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3561
3562 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3564
3565 G.fileflags |= G_FILE_RECOVER_READ;
3566
3567 success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, op->reports);
3568
3569 G.fileflags &= ~G_FILE_RECOVER_READ;
3570
3571 if (success) {
3572 if (!G.background) {
3573 wmOperatorType *ot = op->type;
3574 PointerRNA *props_ptr = MEM_new<PointerRNA>(__func__);
3576 RNA_boolean_set(props_ptr, "use_scripts", true);
3578 }
3579 return OPERATOR_FINISHED;
3580 }
3581 return OPERATOR_CANCELLED;
3582}
3583
3585 wmOperator *op,
3586 const wmEvent * /*event*/)
3587{
3588 char filepath[FILE_MAX];
3589
3590 wm_autosave_location(filepath);
3591 RNA_string_set(op->ptr, "filepath", filepath);
3592 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3593 UNUSED_VARS(use_scripts_autoexec_check); /* The user can set this in the UI. */
3595
3597}
3598
3600{
3601 ot->name = "Recover Auto Save";
3602 ot->idname = "WM_OT_recover_auto_save";
3603 ot->description = "Open an automatically saved file to recover it";
3604
3607
3615
3617}
3618
3620
3621/* -------------------------------------------------------------------- */
3626
3627static void wm_filepath_default(const Main *bmain, char *filepath)
3628{
3629 if (bmain->filepath[0] == '\0') {
3630 char filename_untitled[FILE_MAXFILE];
3631 /* While a filename need not be UTF8, at this point the constructed name should be UTF8. */
3632 SNPRINTF_UTF8(filename_untitled, "%s.blend", DATA_("Untitled"));
3633 BLI_path_filename_ensure(filepath, FILE_MAX, filename_untitled);
3634 }
3635}
3636
3638{
3639 PropertyRNA *prop;
3640
3641 prop = RNA_struct_find_property(op->ptr, "compress");
3642 if (!RNA_property_is_set(op->ptr, prop)) {
3643 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3644 if (blendfile_path[0] != '\0') { /* Keep flag for existing file. */
3645 RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0);
3646 }
3647 else { /* Use userdef for new file. */
3648 RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0);
3649 }
3650 }
3651}
3652
3654{
3655 Main *bmain = CTX_data_main(C);
3656 PropertyRNA *prop;
3657 char filepath[FILE_MAX];
3658
3659 prop = RNA_struct_find_property(op->ptr, "filepath");
3660 if (!RNA_property_is_set(op->ptr, prop)) {
3661 const char *blendfile_path = BKE_main_blendfile_path(bmain);
3662 /* If not saved before, get the name of the most recently used `.blend` file. */
3663 if ((blendfile_path[0] == '\0') && G.recent_files.first) {
3664 RecentFile *recent = static_cast<RecentFile *>(G.recent_files.first);
3665 STRNCPY(filepath, recent->filepath);
3666 }
3667 else {
3668 STRNCPY(filepath, blendfile_path);
3669 }
3670
3671 /* For convenience when using "Save As" on asset system files:
3672 * Replace `.asset.blend` extension with just `.blend`.
3673 * Asset system files must not be overridden (except by the asset system),
3674 * there are further checks to prevent this entirely. */
3675 if (bmain->is_asset_edit_file &&
3677 {
3678 filepath[strlen(filepath) - strlen(BLENDER_ASSET_FILE_SUFFIX)] = '\0';
3679 BLI_path_extension_ensure(filepath, FILE_MAX, ".blend");
3680 }
3681
3682 wm_filepath_default(bmain, filepath);
3683 RNA_property_string_set(op->ptr, prop, filepath);
3684 }
3685}
3686
3688 wmOperator *op,
3689 const wmEvent * /*event*/)
3690{
3691
3693 save_set_filepath(C, op);
3694
3695 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
3696 if (!RNA_property_is_set(op->ptr, prop)) {
3697 RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_RELPATHS));
3698 }
3699
3701
3703}
3704
3705/* Function used for #WM_OT_save_mainfile too. */
3707{
3708 Main *bmain = CTX_data_main(C);
3709 char filepath[FILE_MAX];
3710 const bool is_save_as = (op->type->invoke == wm_save_as_mainfile_invoke);
3711 const bool use_save_as_copy = is_save_as && RNA_boolean_get(op->ptr, "copy");
3712
3713 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "incremental");
3714 const bool is_incremental = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
3715
3716 /* We could expose all options to the users however in most cases remapping
3717 * existing relative paths is a good default.
3718 * Users can manually make their paths relative & absolute if they wish. */
3719 const eBLO_WritePathRemap remap_mode = RNA_boolean_get(op->ptr, "relative_remap") ?
3723
3724 const bool is_filepath_set = RNA_struct_property_is_set(op->ptr, "filepath");
3725 if (is_filepath_set) {
3726 RNA_string_get(op->ptr, "filepath", filepath);
3727 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3728 }
3729 else {
3730 STRNCPY(filepath, BKE_main_blendfile_path(bmain));
3731 }
3732
3733 if (filepath[0] == '\0') {
3734 BKE_report(op->reports,
3735 RPT_ERROR,
3736 "Unable to save an unsaved file with an empty or unset \"filepath\" property");
3737 return OPERATOR_CANCELLED;
3738 }
3739
3740 if ((is_save_as == false) && is_incremental) {
3741 char head[FILE_MAXFILE], tail[FILE_MAXFILE];
3742 ushort digits;
3743 int num = BLI_path_sequence_decode(filepath, head, sizeof(head), tail, sizeof(tail), &digits);
3744 /* Numbers greater than INT_MAX return 0, resulting in always appending "1" to the name. */
3745 if (num == 0 && digits == 0) {
3746 /* This does nothing if there are no numbers at the end of the head. */
3748 }
3749
3750 const int tries_limit = 1000;
3751 int tries = 0;
3752 bool in_use = true;
3753 do {
3754 num++;
3755 tries++;
3756 BLI_path_sequence_encode(filepath, sizeof(filepath), head, tail, digits, num);
3757 in_use = BLI_exists(filepath);
3758 } while (in_use && tries < tries_limit && num < INT_MAX);
3759 if (in_use) {
3760 BKE_report(op->reports, RPT_ERROR, "Unable to find an available incremented file name");
3761 return OPERATOR_CANCELLED;
3762 }
3763 }
3764
3765 const int fileflags_orig = G.fileflags;
3766 int fileflags = G.fileflags;
3767
3768 /* Set compression flag. */
3769 SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "compress"), G_FILE_COMPRESS);
3770
3771 const bool success = wm_file_write(
3772 C, filepath, fileflags, remap_mode, use_save_as_copy, op->reports);
3773
3774 if ((op->flag & OP_IS_INVOKE) == 0) {
3775 /* OP_IS_INVOKE is set when the operator is called from the GUI.
3776 * If it is not set, the operator is called from a script and
3777 * shouldn't influence G.fileflags. */
3778 G.fileflags = fileflags_orig;
3779 }
3780
3781 if (success == false) {
3782 return OPERATOR_CANCELLED;
3783 }
3784
3785 const char *filename = BLI_path_basename(filepath);
3786
3787 if (is_incremental) {
3788 BKE_reportf(op->reports, RPT_INFO, "Saved incremental as \"%s\"", filename);
3789 }
3790 else if (is_save_as) {
3791 /* use_save_as_copy depends upon is_save_as. */
3792 if (use_save_as_copy) {
3793 BKE_reportf(op->reports, RPT_INFO, "Saved copy as \"%s\"", filename);
3794 }
3795 else {
3796 BKE_reportf(op->reports, RPT_INFO, "Saved as \"%s\"", filename);
3797 }
3798 }
3799 else {
3800 BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", filename);
3801 }
3802
3803 if (!use_save_as_copy) {
3804 /* If saved file is the active one, there are technically no more compatibility issues, the
3805 * file on disk now matches the currently opened data version-wise. */
3806 bmain->has_forward_compatibility_issues = false;
3808
3809 /* If saved file is the active one, notify WM so that saved status and window title can be
3810 * updated. */
3813 /* Restart auto-save timer to avoid unnecessary unexpected freezing (because of auto-save)
3814 * when often saving manually. */
3817 wm->autosave_scheduled = false;
3818 }
3819 }
3820
3821 if (!is_save_as && RNA_boolean_get(op->ptr, "exit")) {
3823 }
3824
3825 return OPERATOR_FINISHED;
3826}
3827
3829{
3830 char filepath[FILE_MAX];
3831 RNA_string_get(op->ptr, "filepath", filepath);
3832 if (!BKE_blendfile_extension_check(filepath)) {
3833 /* NOTE(@ideasman42): some users would prefer #BLI_path_extension_replace(),
3834 * we have had some nitpicking bug reports about this.
3835 * Always adding the extension as users may use '.' as part of the file-name. */
3836 BLI_path_extension_ensure(filepath, FILE_MAX, ".blend");
3837 RNA_string_set(op->ptr, "filepath", filepath);
3838 return true;
3839 }
3840 return false;
3841}
3842
3844{
3845 if (RNA_boolean_get(ptr, "copy")) {
3846 return CTX_IFACE_(ot->translation_context, "Save Copy");
3847 }
3848 return "";
3849}
3850
3852 wmOperatorType * /*ot*/,
3853 PointerRNA *ptr)
3854{
3855 if (RNA_boolean_get(ptr, "copy")) {
3856 return BLI_strdup(TIP_(
3857 "Save the current file in the desired location but do not make the saved file active"));
3858 }
3859 return "";
3860}
3861
3863{
3864 PropertyRNA *prop;
3865
3866 ot->name = "Save As";
3867 ot->idname = "WM_OT_save_as_mainfile";
3868 ot->description = "Save the current file in the desired location";
3869
3872 ot->get_name = wm_save_as_mainfile_get_name;
3873 ot->get_description = wm_save_as_mainfile_get_description;
3874 ot->check = wm_save_mainfile_check;
3875 /* Omit window poll so this can work in background mode. */
3876
3880 FILE_SAVE,
3884 RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
3885 RNA_def_boolean(ot->srna,
3886 "relative_remap",
3887 true,
3888 "Remap Relative",
3889 "Remap relative paths when saving to a different directory");
3890 prop = RNA_def_boolean(
3891 ot->srna,
3892 "copy",
3893 false,
3894 "Save Copy",
3895 "Save a copy of the actual working state but does not make saved file active");
3897}
3898
3900 wmOperator *op,
3901 const wmEvent * /*event*/)
3902{
3904
3905 /* Cancel if no active window. */
3906 if (CTX_wm_window(C) == nullptr) {
3907 return OPERATOR_CANCELLED;
3908 }
3909
3911 save_set_filepath(C, op);
3912
3913 /* If we're saving for the first time and prefer relative paths -
3914 * any existing paths will be absolute,
3915 * enable the option to remap paths to avoid confusion, see: #37240. */
3916 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3917 if ((blendfile_path[0] == '\0') && (U.flag & USER_RELPATHS)) {
3918 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
3919 if (!RNA_property_is_set(op->ptr, prop)) {
3920 RNA_property_boolean_set(op->ptr, prop, true);
3921 }
3922 }
3923
3924 if (blendfile_path[0] != '\0') {
3928 }
3929 else {
3931 }
3932 }
3933 else {
3936 }
3937
3938 return ret;
3939}
3940
3942 wmOperatorType * /*ot*/,
3943 PointerRNA *ptr)
3944{
3945 if (RNA_boolean_get(ptr, "incremental")) {
3946 return TIP_(
3947 "Save the current Blender file with a numerically incremented name that does not "
3948 "overwrite any existing files");
3949 }
3950 return "";
3951}
3952
3954{
3955 ot->name = "Save Blender File";
3956 ot->idname = "WM_OT_save_mainfile";
3957 ot->description = "Save the current Blender file";
3958
3959 ot->invoke = wm_save_mainfile_invoke;
3961 ot->check = wm_save_mainfile_check;
3962 ot->get_description = wm_save_mainfile_get_description;
3963 /* Omit window poll so this can work in background mode. */
3964
3965 PropertyRNA *prop;
3969 FILE_SAVE,
3973 RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
3974 RNA_def_boolean(ot->srna,
3975 "relative_remap",
3976 false,
3977 "Remap Relative",
3978 "Remap relative paths when saving to a different directory");
3979
3980 prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "Exit Blender after saving");
3982
3983 prop = RNA_def_boolean(ot->srna,
3984 "incremental",
3985 false,
3986 "Incremental",
3987 "Save the current Blender file with a numerically incremented name that "
3988 "does not overwrite any existing files");
3990}
3991
3993
3994/* -------------------------------------------------------------------- */
3997
3999
4001 {CLEAR_RECENT_ALL, "ALL", 0, "All Items", ""},
4002 {CLEAR_RECENT_MISSING, "MISSING", 0, "Items Not Found", ""},
4003 {0, nullptr, 0, nullptr, nullptr},
4004};
4005
4007 wmOperator *op,
4008 const wmEvent *event)
4009{
4011 C, op, event, IFACE_("Clear Recent Files List"), IFACE_("Remove"));
4012}
4013
4015{
4016 ClearRecentInclude include = static_cast<ClearRecentInclude>(RNA_enum_get(op->ptr, "remove"));
4017
4018 if (include == CLEAR_RECENT_ALL) {
4020 }
4021 else if (include == CLEAR_RECENT_MISSING) {
4022 LISTBASE_FOREACH_MUTABLE (RecentFile *, recent, &G.recent_files) {
4023 if (!BLI_exists(recent->filepath)) {
4024 wm_history_file_free(recent);
4025 }
4026 }
4027 }
4028
4030
4031 return OPERATOR_FINISHED;
4032}
4033
4035{
4036 uiLayout *layout = op->layout;
4037 layout->use_property_split_set(true);
4038 layout->use_property_decorate_set(false);
4039
4040 layout->separator();
4041 layout->prop(op->ptr, "remove", UI_ITEM_R_TOGGLE, std::nullopt, ICON_NONE);
4042 layout->separator();
4043}
4044
4046{
4047 ot->name = "Clear Recent Files List";
4048 ot->idname = "WM_OT_clear_recent_files";
4049 ot->description = "Clear the recent files list";
4050
4054
4055 /* flags */
4056 ot->flag = OPTYPE_REGISTER;
4057
4058 /* props */
4059 ot->prop = RNA_def_enum(
4060 ot->srna, "remove", prop_clear_recent_types, CLEAR_RECENT_ALL, "Remove", "");
4061}
4062
4064
4065/* -------------------------------------------------------------------- */
4068
4069static void wm_block_autorun_warning_ignore(bContext *C, void *arg_block, void * /*arg*/)
4070{
4071 wmWindow *win = CTX_wm_window(C);
4072 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4073
4074 /* Free the data as it's no longer needed. */
4075 wm_test_autorun_revert_action_set(nullptr, nullptr);
4076}
4077
4079{
4080 wmWindow *win = CTX_wm_window(C);
4081
4082 UI_popup_block_close(C, win, block);
4083
4084 /* Save user preferences for permanent execution. */
4085 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
4087 C, "WM_OT_save_userpref", blender::wm::OpCallContext::ExecDefault, nullptr, nullptr);
4088 }
4089
4090 /* Load file again with scripts enabled.
4091 * The reload is necessary to allow scripts to run when the files loads. */
4093}
4094
4096{
4097 wmWindow *win = CTX_wm_window(C);
4098 Main *bmain = CTX_data_main(C);
4099
4100 UI_popup_block_close(C, win, block);
4101
4102 /* Save user preferences for permanent execution. */
4103 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
4105 C, "WM_OT_save_userpref", blender::wm::OpCallContext::ExecDefault, nullptr, nullptr);
4106 }
4107
4108 /* Force a full refresh, but without reloading the file. */
4109 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
4111 }
4112}
4113
4114/* Build the auto-run warning dialog UI. */
4115static uiBlock *block_create_autorun_warning(bContext *C, ARegion *region, void * /*arg1*/)
4116{
4117 const char *blendfile_path = BKE_main_blendfile_path_from_global();
4119
4120 uiBlock *block = UI_block_begin(
4121 C, region, "autorun_warning_popup", blender::ui::EmbossType::Emboss);
4126
4127 const char *title = RPT_(
4128 "For security reasons, automatic execution of Python scripts "
4129 "in this file was disabled:");
4130 const char *message = RPT_("This may lead to unexpected behavior");
4131 const char *checkbox_text = RPT_("Permanently allow execution of scripts");
4132
4133 /* Measure strings to find the longest. */
4134 const uiStyle *style = UI_style_get_dpi();
4135 UI_fontstyle_set(&style->widget);
4136 int text_width = int(BLF_width(style->widget.uifont_id, title, BLF_DRAW_STR_DUMMY_MAX));
4137 text_width = std::max(text_width,
4138 int(BLF_width(style->widget.uifont_id, message, BLF_DRAW_STR_DUMMY_MAX)));
4139 text_width = std::max(
4140 text_width,
4141 int(BLF_width(style->widget.uifont_id, checkbox_text, BLF_DRAW_STR_DUMMY_MAX) +
4142 (UI_SCALE_FAC * 25.0f)));
4143
4144 const int dialog_width = std::max(int(400.0f * UI_SCALE_FAC),
4145 text_width + int(style->columnspace * 2.5));
4146 const short icon_size = 40 * UI_SCALE_FAC;
4147 uiLayout *layout = uiItemsAlertBox(
4148 block, style, dialog_width + icon_size, ALERT_ICON_ERROR, icon_size);
4149
4150 /* Title and explanation text. */
4151 uiLayout *col = &layout->column(true);
4152 uiItemL_ex(col, title, ICON_NONE, true, false);
4153 uiItemL_ex(col, G.autoexec_fail, ICON_NONE, false, true);
4154 col->label(message, ICON_NONE);
4155
4156 layout->separator();
4157
4158 PointerRNA pref_ptr = RNA_pointer_create_discrete(nullptr, &RNA_PreferencesFilePaths, &U);
4159 layout->prop(&pref_ptr, "use_scripts_auto_execute", UI_ITEM_NONE, checkbox_text, ICON_NONE);
4160
4161 layout->separator(2.0f);
4162
4163 /* Buttons. */
4164 uiBut *but;
4165 uiLayout *split = &layout->split(0.0f, true);
4166 split->scale_y_set(1.2f);
4167
4168 /* Empty space. */
4169 col = &split->column(false);
4170 col->separator();
4171
4172 col = &split->column(false);
4173
4174 /* Allow reload if we have a saved file.
4175 * Otherwise just enable scripts and reset the depsgraphs. */
4176 if ((blendfile_path[0] != '\0') && wm->file_saved) {
4177 but = uiDefIconTextBut(block,
4179 0,
4180 ICON_NONE,
4181 IFACE_("Allow Execution"),
4182 0,
4183 0,
4184 50,
4185 UI_UNIT_Y,
4186 nullptr,
4187 TIP_("Reload file with execution of Python scripts enabled"));
4189 but, [block](bContext &C) { wm_block_autorun_warning_reload_with_scripts(&C, block); });
4190 }
4191 else {
4192 but = uiDefIconTextBut(block,
4194 0,
4195 ICON_NONE,
4196 IFACE_("Allow Execution"),
4197 0,
4198 0,
4199 50,
4200 UI_UNIT_Y,
4201 nullptr,
4202 TIP_("Enable scripts"));
4203 UI_but_func_set(but,
4204 [block](bContext &C) { wm_block_autorun_warning_enable_scripts(&C, block); });
4205 }
4207
4208 col = &split->column(false);
4209 but = uiDefIconTextBut(block,
4211 0,
4212 ICON_NONE,
4213 IFACE_("Ignore"),
4214 0,
4215 0,
4216 50,
4217 UI_UNIT_Y,
4218 nullptr,
4219 TIP_("Continue using file without Python scripts"));
4223
4225
4226 return block;
4227}
4228
4236static struct {
4240
4253
4255{
4258
4259 /* Use regular revert. */
4260 if (ot == nullptr) {
4261 ot = WM_operatortype_find("WM_OT_revert_mainfile", false);
4262 ptr = MEM_new<PointerRNA>(__func__);
4264 RNA_boolean_set(ptr, "use_scripts", true);
4265
4266 /* Set state, so it's freed correctly. */
4268 }
4269
4271 wm_test_autorun_revert_action_set(nullptr, nullptr);
4272}
4273
4275{
4276 /* Test if any auto-execution of scripts failed. */
4277 if ((G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL) == 0) {
4278 return;
4279 }
4280
4281 /* Only show the warning once. */
4283 return;
4284 }
4285
4287
4289 wmWindow *win = (wm->runtime->winactive) ? wm->runtime->winactive :
4290 static_cast<wmWindow *>(wm->windows.first);
4291
4292 if (win) {
4293 /* We want this warning on the Main window, not a child window even if active. See #118765. */
4294 if (win->parent) {
4295 win = win->parent;
4296 }
4297
4298 wmWindow *prevwin = CTX_wm_window(C);
4299 CTX_wm_window_set(C, win);
4301 CTX_wm_window_set(C, prevwin);
4302 }
4303}
4304
4306{
4307 if (!G_MAIN->is_read_invalid) {
4308 return;
4309 }
4310
4311 G_MAIN->is_read_invalid = false;
4312
4314 wmWindow *win = (wm->runtime->winactive) ? wm->runtime->winactive :
4315 static_cast<wmWindow *>(wm->windows.first);
4316
4317 if (win) {
4318 /* We want this warning on the Main window, not a child window even if active. See #118765. */
4319 if (win->parent) {
4320 win = win->parent;
4321 }
4322
4323 wmWindow *prevwin = CTX_wm_window(C);
4324 CTX_wm_window_set(C, win);
4325 UI_alert(C,
4326 RPT_("Unable to Load File"),
4327 RPT_("The file specified is not a valid Blend document."),
4329 false);
4330
4331 CTX_wm_window_set(C, prevwin);
4332 }
4333}
4334
4336
4337/* -------------------------------------------------------------------- */
4340
4341static void free_post_file_close_action(void *arg)
4342{
4343 wmGenericCallback *action = (wmGenericCallback *)arg;
4345}
4346
4347static void wm_free_operator_properties_callback(void *user_data)
4348{
4349 IDProperty *properties = (IDProperty *)user_data;
4350 IDP_FreeProperty(properties);
4351}
4352
4353static const char *save_file_overwrite_dialog_name = "save_file_overwrite_popup";
4354
4355static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bmain)
4356{
4357 uiLayout *layout = &parent_layout->column(true);
4358 /* Trick to make both lines of text below close enough to look like they are part of a same
4359 * block. */
4360 layout->scale_y_set(0.70f);
4361
4363 char writer_ver_str[16];
4364 char current_ver_str[16];
4365 if (bmain->versionfile == BLENDER_VERSION) {
4367 writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile);
4369 current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION);
4370 }
4371 else {
4373 writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
4375 current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1);
4376 }
4377
4378 char message_line1[256];
4379 char message_line2[256];
4380 SNPRINTF(message_line1,
4381 RPT_("This file was saved by a newer version of Blender (%s)."),
4382 writer_ver_str);
4383 SNPRINTF(message_line2,
4384 RPT_("Saving it with this Blender (%s) may cause loss of data."),
4385 current_ver_str);
4386 layout->label(message_line1, ICON_NONE);
4387 layout->label(message_line2, ICON_NONE);
4388 }
4389
4390 if (bmain->is_asset_edit_file) {
4392 layout->separator(1.4f);
4393 }
4394
4395 layout->label(RPT_("This file is managed by the Blender asset system. It can only be"),
4396 ICON_NONE);
4397 layout->label(RPT_("saved as a new, regular file."), ICON_NONE);
4398 }
4399
4402 layout->separator(1.4f);
4403 }
4404 layout->label(
4405 RPT_("Displays, views or color spaces in this file were missing and have been changed."),
4406 ICON_NONE);
4407 layout->label(RPT_("Saving it with this OpenColorIO configuration may cause loss of data."),
4408 ICON_NONE);
4409 }
4410}
4411
4412static void save_file_overwrite_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
4413{
4414 wmWindow *win = CTX_wm_window(C);
4415 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4416}
4417
4419{
4420 uiBut *but = uiDefIconTextBut(
4421 block, ButType::But, 0, ICON_NONE, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, nullptr, "");
4422 UI_but_func_set(but, save_file_overwrite_cancel, block, post_action);
4424}
4425
4426static void save_file_overwrite_confirm(bContext *C, void *arg_block, void *arg_data)
4427{
4428 wmWindow *win = CTX_wm_window(C);
4429
4430 /* Re-use operator properties as defined for the initial "Save" operator,
4431 * which triggered this "Forward Compatibility" popup. */
4433 static_cast<wmGenericCallback *>(arg_data));
4434
4435 /* Needs to be done after stealing the callback data above, otherwise it would cause a
4436 * use-after-free. */
4437 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4438
4439 PointerRNA operator_propptr = {};
4440 PointerRNA *operator_propptr_p = &operator_propptr;
4441 IDProperty *operator_idproperties = static_cast<IDProperty *>(callback->user_data);
4442 WM_operator_properties_alloc(&operator_propptr_p, &operator_idproperties, "WM_OT_save_mainfile");
4443
4445 "WM_OT_save_mainfile",
4447 operator_propptr_p,
4448 nullptr);
4449
4450 WM_generic_callback_free(callback);
4451}
4452
4454{
4455 uiBut *but = uiDefIconTextBut(
4456 block, ButType::But, 0, ICON_NONE, IFACE_("Overwrite"), 0, 0, 0, UI_UNIT_Y, nullptr, "");
4457 UI_but_func_set(but, save_file_overwrite_confirm, block, post_action);
4460}
4461
4462static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*arg_data*/)
4463{
4464 wmWindow *win = CTX_wm_window(C);
4465 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4466
4468 C, "WM_OT_save_as_mainfile", blender::wm::OpCallContext::InvokeDefault, nullptr, nullptr);
4469}
4470
4472{
4473 uiBut *but = uiDefIconTextBut(
4474 block, ButType::But, 0, ICON_NONE, IFACE_("Save As..."), 0, 0, 0, UI_UNIT_Y, nullptr, "");
4475 UI_but_func_set(but, save_file_overwrite_saveas, block, post_action);
4478}
4479
4481{
4482 wmGenericCallback *post_action = static_cast<wmGenericCallback *>(arg1);
4483 Main *bmain = CTX_data_main(C);
4484
4485 uiBlock *block = UI_block_begin(
4490
4491 uiLayout *layout = uiItemsAlertBox(block, 44, ALERT_ICON_WARNING);
4492
4493 /* Title. */
4495 if (bmain->is_asset_edit_file) {
4496 uiItemL_ex(layout,
4497 RPT_("Cannot overwrite asset system files. Save as new file"),
4498 ICON_NONE,
4499 true,
4500 false);
4501 uiItemL_ex(layout, RPT_("with an older Blender version?"), ICON_NONE, true, false);
4502 }
4503 else {
4504 uiItemL_ex(
4505 layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
4506 }
4507 }
4508 else if (bmain->is_asset_edit_file) {
4509 uiItemL_ex(layout,
4510 RPT_("Cannot overwrite asset system files. Save as new file?"),
4511 ICON_NONE,
4512 true,
4513 false);
4514 }
4515 else if (!bmain->colorspace.is_missing_opencolorio_config) {
4517 }
4518
4520 uiItemL_ex(layout,
4521 RPT_("Overwrite file with current OpenColorIO configuration?"),
4522 ICON_NONE,
4523 true,
4524 false);
4525 }
4526
4527 /* Filename. */
4528 const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
4529 char filename[FILE_MAX];
4530 if (blendfile_path[0] != '\0') {
4531 BLI_path_split_file_part(blendfile_path, filename, sizeof(filename));
4532 }
4533 else {
4534 /* While a filename need not be UTF8, at this point the constructed name should be UTF8. */
4535 SNPRINTF_UTF8(filename, "%s.blend", DATA_("Untitled"));
4536 /* Since this dialog should only be shown when re-saving an existing file, current filepath
4537 * should never be empty. */
4539 }
4540 layout->label(filename, ICON_NONE);
4541
4542 /* Detailed message info. */
4543 file_overwrite_detailed_info_show(layout, bmain);
4544
4545 layout->separator(4.0f);
4546
4547 /* Buttons. */
4548
4549 uiLayout *split = &layout->split(0.3f, true);
4550 split->scale_y_set(1.2f);
4551
4552 split->column(false);
4553 /* Asset files don't actually allow overriding. */
4554 const bool allow_overwrite = !bmain->is_asset_edit_file;
4555 if (allow_overwrite) {
4556 save_file_overwrite_confirm_button(block, post_action);
4557 }
4558
4559 uiLayout *split_right = &split->split(0.1f, true);
4560
4561 split_right->column(false);
4562 /* Empty space. */
4563
4564 split_right->column(false);
4565 save_file_overwrite_cancel_button(block, post_action);
4566
4567 split_right->column(false);
4568 save_file_overwrite_saveas_button(block, post_action);
4569
4571 return block;
4572}
4573
4586
4588
4589/* -------------------------------------------------------------------- */
4592
4594
4595static void wm_block_file_close_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
4596{
4597 wmWindow *win = CTX_wm_window(C);
4598 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4599}
4600
4601static void wm_block_file_close_discard(bContext *C, void *arg_block, void *arg_data)
4602{
4604
4605 /* Close the popup before executing the callback. Otherwise
4606 * the popup might be closed by the callback, which will lead
4607 * to a crash. */
4608 wmWindow *win = CTX_wm_window(C);
4609 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4610
4611 callback->exec(C, callback->user_data);
4612 WM_generic_callback_free(callback);
4613}
4614
4615static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_data)
4616{
4617 const Main *bmain = CTX_data_main(C);
4618 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
4620 bool execute_callback = true;
4621
4622 wmWindow *win = CTX_wm_window(C);
4623 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4624
4625 int modified_images_count = ED_image_save_all_modified_info(CTX_data_main(C), nullptr);
4626 if (modified_images_count > 0 && save_images_when_file_is_closed) {
4627 if (ED_image_should_save_modified(bmain)) {
4628 ReportList *reports = CTX_wm_reports(C);
4629 ED_image_save_all_modified(C, reports);
4631 }
4632 else {
4633 execute_callback = false;
4634 }
4635 }
4636
4637 bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0';
4638
4639 if (file_has_been_saved_before) {
4641 {
4642 /* Need to invoke to get the file-browser and choose where to save the new file.
4643 * This also makes it impossible to keep on going with current operation, which is why
4644 * callback cannot be executed anymore.
4645 *
4646 * This is the same situation as what happens when the file has never been saved before
4647 * (outer `else` statement, below). */
4649 "WM_OT_save_as_mainfile",
4651 nullptr,
4652 nullptr);
4653 execute_callback = false;
4654 }
4655 else {
4657 C, "WM_OT_save_mainfile", blender::wm::OpCallContext::ExecDefault, nullptr, nullptr);
4658 if (status & OPERATOR_CANCELLED) {
4659 execute_callback = false;
4660 }
4661 }
4662 }
4663 else {
4665 C, "WM_OT_save_mainfile", blender::wm::OpCallContext::InvokeDefault, nullptr, nullptr);
4666 execute_callback = false;
4667 }
4668
4669 if (execute_callback) {
4670 callback->exec(C, callback->user_data);
4671 }
4672 WM_generic_callback_free(callback);
4673}
4674
4676{
4677 uiBut *but = uiDefIconTextBut(
4678 block, ButType::But, 0, ICON_NONE, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, nullptr, "");
4679 UI_but_func_set(but, wm_block_file_close_cancel, block, post_action);
4681}
4682
4684{
4685 uiBut *but = uiDefIconTextBut(
4686 block, ButType::But, 0, ICON_NONE, IFACE_("Don't Save"), 0, 0, 0, UI_UNIT_Y, nullptr, "");
4687 UI_but_func_set(but, wm_block_file_close_discard, block, post_action);
4689}
4690
4692 wmGenericCallback *post_action,
4693 const bool needs_overwrite_confirm)
4694{
4695 uiBut *but = uiDefIconTextBut(
4696 block,
4698 0,
4699 ICON_NONE,
4700 /* Forward compatibility issues force using 'save as' operator instead of 'save' one. */
4701 needs_overwrite_confirm ? IFACE_("Save As...") : IFACE_("Save"),
4702 0,
4703 0,
4704 0,
4705 UI_UNIT_Y,
4706 nullptr,
4707 "");
4708 UI_but_func_set(but, wm_block_file_close_save, block, post_action);
4711}
4712
4713static const char *close_file_dialog_name = "file_close_popup";
4714
4715static void save_catalogs_when_file_is_closed_set_fn(bContext * /*C*/, void *arg1, void * /*arg2*/)
4716{
4717 char *save_catalogs_when_file_is_closed = static_cast<char *>(arg1);
4719 *save_catalogs_when_file_is_closed != 0);
4720}
4721
4723{
4724 using namespace blender;
4725 wmGenericCallback *post_action = (wmGenericCallback *)arg1;
4726 Main *bmain = CTX_data_main(C);
4727
4728 uiBlock *block = UI_block_begin(
4733
4734 uiLayout *layout = uiItemsAlertBox(
4736
4737 const bool needs_overwrite_confirm = BKE_main_needs_overwrite_confirm(bmain);
4738
4739 /* Title. */
4740 uiItemL_ex(layout, RPT_("Save changes before closing?"), ICON_NONE, true, false);
4741
4742 /* Filename. */
4743 const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
4744 char filename[FILE_MAX];
4745 if (blendfile_path[0] != '\0') {
4746 BLI_path_split_file_part(blendfile_path, filename, sizeof(filename));
4747 }
4748 else {
4749 /* While a filename need not be UTF8, at this point the constructed name should be UTF8. */
4750 SNPRINTF_UTF8(filename, "%s.blend", DATA_("Untitled"));
4751 }
4752 layout->label(filename, ICON_NONE);
4753
4754 /* Potential forward compatibility issues message. */
4755 if (needs_overwrite_confirm) {
4756 file_overwrite_detailed_info_show(layout, bmain);
4757 }
4758
4759 /* Image Saving Warnings. */
4760 ReportList reports;
4761 BKE_reports_init(&reports, RPT_STORE);
4762 uint modified_images_count = ED_image_save_all_modified_info(bmain, &reports);
4763
4764 LISTBASE_FOREACH (Report *, report, &reports.list) {
4765 uiLayout *row = &layout->column(false);
4766 row->scale_y_set(0.6f);
4767 row->separator();
4768
4769 /* Error messages created in ED_image_save_all_modified_info() can be long,
4770 * but are made to separate into two parts at first colon between text and paths.
4771 */
4772 char *message = BLI_strdupn(report->message, report->len);
4773 char *path_info = strstr(message, ": ");
4774 if (path_info) {
4775 /* Terminate message string at colon. */
4776 path_info[1] = '\0';
4777 /* Skip over the ": ". */
4778 path_info += 2;
4779 }
4780 uiItemL_ex(row, message, ICON_NONE, false, true);
4781 if (path_info) {
4782 uiItemL_ex(row, path_info, ICON_NONE, false, true);
4783 }
4784 MEM_freeN(message);
4785 }
4786
4787 /* Used to determine if extra separators are needed. */
4788 bool has_extra_checkboxes = false;
4789
4790 /* Modified Images Checkbox. */
4791 if (modified_images_count > 0) {
4792 char message[64];
4793 SNPRINTF(message, RPT_("Save %u modified image(s)"), modified_images_count);
4794 /* Only the first checkbox should get extra separation. */
4795 if (!has_extra_checkboxes) {
4796 layout->separator();
4797 }
4798 uiDefButBitC(block,
4800 1,
4801 0,
4802 message,
4803 0,
4804 0,
4805 0,
4806 UI_UNIT_Y,
4808 0,
4809 0,
4810 "");
4811 has_extra_checkboxes = true;
4812 }
4813
4815 static char save_catalogs_when_file_is_closed;
4816
4817 save_catalogs_when_file_is_closed = ed::asset::catalogs_get_save_catalogs_when_file_is_saved();
4818
4819 /* Only the first checkbox should get extra separation. */
4820 if (!has_extra_checkboxes) {
4821 layout->separator();
4822 }
4823 uiBut *but = uiDefButBitC(block,
4825 1,
4826 0,
4827 "Save modified asset catalogs",
4828 0,
4829 0,
4830 0,
4831 UI_UNIT_Y,
4832 &save_catalogs_when_file_is_closed,
4833 0,
4834 0,
4835 "");
4836 UI_but_func_set(but,
4838 &save_catalogs_when_file_is_closed,
4839 nullptr);
4840 has_extra_checkboxes = true;
4841 }
4842
4843 BKE_reports_free(&reports);
4844
4845 layout->separator(2.0f);
4846
4847 /* Buttons. */
4848#ifdef _WIN32
4849 const bool windows_layout = true;
4850#else
4851 const bool windows_layout = false;
4852#endif
4853
4854 if (windows_layout) {
4855 /* Windows standard layout. */
4856
4857 uiLayout *split = &layout->split(0.0f, true);
4858 split->scale_y_set(1.2f);
4859
4860 split->column(false);
4861 wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm);
4862
4863 split->column(false);
4864 wm_block_file_close_discard_button(block, post_action);
4865
4866 split->column(false);
4867 wm_block_file_close_cancel_button(block, post_action);
4868 }
4869 else {
4870 /* Non-Windows layout (macOS and Linux). */
4871
4872 uiLayout *split = &layout->split(0.3f, true);
4873 split->scale_y_set(1.2f);
4874
4875 split->column(false);
4876 wm_block_file_close_discard_button(block, post_action);
4877
4878 uiLayout *split_right = &split->split(0.1f, true);
4879
4880 split_right->column(false);
4881 /* Empty space. */
4882
4883 split_right->column(false);
4884 wm_block_file_close_cancel_button(block, post_action);
4885
4886 split_right->column(false);
4887 wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm);
4888 }
4889
4891 return block;
4892}
4893
4904
4906 wmOperator *op,
4907 wmGenericCallbackFn post_action_fn)
4908{
4909 if (U.uiflag & USER_SAVE_PROMPT &&
4911 {
4913 callback->exec = post_action_fn;
4914 callback->user_data = IDP_CopyProperty(op->properties);
4916 wm_close_file_dialog(C, callback);
4917 return true;
4918 }
4919
4920 return false;
4921}
4922
void AS_asset_libraries_exit()
bool AS_asset_library_has_any_unsaved_catalogs()
bool BKE_addon_remove_safe(struct ListBase *addon_list, const char *module)
Definition addon.cc:56
bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_maxncpy) ATTR_NONNULL(1)
Definition appdir.cc:1082
void BKE_tempdir_init(const char *userdir)
Definition appdir.cc:1200
#define BLENDER_USERPREF_FILE
bool BKE_appdir_app_template_has_userpref(const char *app_template) ATTR_NONNULL(1)
Definition appdir.cc:1096
#define BLENDER_HISTORY_FILE
std::optional< std::string > BKE_appdir_folder_id(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:721
#define BLENDER_STARTUP_FILE
#define BLENDER_QUIT_FILE
@ BLENDER_USER_CONFIG
const char * BKE_tempdir_base() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
Definition appdir.cc:1243
bool BKE_appdir_app_template_any()
Definition appdir.cc:1077
std::optional< std::string > BKE_appdir_folder_id_create(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:781
bool BKE_autoexec_match(const char *path)
Definition autoexec.cc:26
Blender util stuff.
void BKE_blender_userdef_app_template_data_set_and_free(UserDef *userdef)
Definition blender.cc:469
void BKE_blender_userdef_data_set_and_free(UserDef *userdef)
Definition blender.cc:288
#define BLENDER_FILE_SUBVERSION
#define BLENDER_VERSION
void BKE_blender_version_blendfile_string_from_values(char *str_buff, const size_t str_buff_maxncpy, const short file_version, const short file_subversion)
Definition blender.cc:156
#define BLENDER_FILE_VERSION
void BKE_blendfile_read_setup_readfile(bContext *C, BlendFileData *bfd, const BlendFileReadParams *params, BlendFileReadWMSetupData *wm_setup_data, BlendFileReadReport *reports, bool startup_update_defaults, const char *startup_app_template)
bool BKE_blendfile_userdef_write_all(ReportList *reports)
#define BLENDER_ASSET_FILE_SUFFIX
void BKE_blendfile_read_make_empty(bContext *C)
bool BKE_blendfile_extension_check(const char *str)
Definition blendfile.cc:86
UserDef * BKE_blendfile_userdef_from_defaults()
UserDef * BKE_blendfile_userdef_read(const char *filepath, ReportList *reports)
BlendFileData * BKE_blendfile_read(const char *filepath, const BlendFileReadParams *params, BlendFileReadReport *reports)
BlendFileData * BKE_blendfile_read_from_memory(const void *file_buf, int file_buf_size, const BlendFileReadParams *params, ReportList *reports)
void BKE_callback_exec_string(Main *bmain, eCbEvent evt, const char *str)
Definition callbacks.cc:63
void BKE_callback_exec_null(Main *bmain, eCbEvent evt)
Definition callbacks.cc:38
@ BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST
@ BKE_CB_EVT_LOAD_FACTORY_USERDEF_POST
@ BKE_CB_EVT_SAVE_PRE
@ BKE_CB_EVT_LOAD_POST_FAIL
@ BKE_CB_EVT_EXTENSION_REPOS_UPDATE_PRE
@ BKE_CB_EVT_VERSION_UPDATE
@ BKE_CB_EVT_SAVE_POST_FAIL
@ BKE_CB_EVT_SAVE_POST
@ BKE_CB_EVT_LOAD_POST
@ BKE_CB_EVT_LOAD_PRE
@ BKE_CB_EVT_EXTENSION_REPOS_UPDATE_POST
ReportList * CTX_wm_reports(const bContext *C)
bScreen * CTX_wm_screen(const bContext *C)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
bool CTX_py_init_get(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
void CTX_wm_window_set(bContext *C, wmWindow *win)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
wmMsgBus * CTX_wm_message_bus(const bContext *C)
void CTX_wm_region_popup_set(bContext *C, ARegion *region_popup)
@ G_FLAG_SCRIPT_OVERRIDE_PREF
@ G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET
@ G_FLAG_USERPREF_NO_SAVE_ON_EXIT
@ G_FLAG_SCRIPT_AUTOEXEC_FAIL
@ G_FLAG_SCRIPT_AUTOEXEC
@ G_FLAG_INTERNET_ALLOW
#define G_FLAG_ALL_READFILE
#define G_MAIN
#define G_FLAG_INTERNET_OVERRIDE_PREF_ANY
@ G_BACKGROUND_NO_DEPSGRAPH
@ G_FILE_RECOVER_READ
@ G_FILE_AUTOPACK
@ G_FILE_RECOVER_WRITE
@ G_FILE_NO_UI
@ G_FILE_COMPRESS
#define G_FLAG_ALL_RUNTIME
void IDP_FreeProperty(IDProperty *prop)
Definition idprop.cc:1251
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:863
void BKE_libblock_free_data_py(ID *id)
void BKE_libblock_free_data(ID *id, bool do_id_user) ATTR_NONNULL()
IDNewNameResult BKE_libblock_rename(Main &bmain, ID &id, blender::StringRefNull name, const IDNewNameMode mode=IDNewNameMode::RenameExistingNever)
Definition lib_id.cc:2370
void BKE_lib_override_library_main_operations_create(Main *bmain, bool force_auto, int *r_report_flags)
BlendThumbnail * BKE_main_thumbnail_from_imbuf(Main *bmain, ImBuf *img)
Definition main.cc:837
bool BKE_main_needs_overwrite_confirm(const Main *bmain)
Definition main.cc:480
#define BLEN_THUMB_SIZE
Definition BKE_main.hh:684
ImBuf * BKE_main_thumbnail_to_imbuf(Main *bmain, BlendThumbnail *data)
Definition main.cc:861
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:887
BlendThumbnail * BKE_main_thumbnail_from_buffer(Main *bmain, const uint8_t *rect, const int size[2])
Definition main.cc:815
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:892
bool BKE_main_namemap_validate(Main &bmain)
#define FOREACH_NODETREE_END
Definition BKE_node.hh:881
#define FOREACH_NODETREE_BEGIN(bmain, _nodetree, _id)
Definition BKE_node.hh:871
void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose)
@ RPT_STORE
Definition BKE_report.hh:56
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_reports_free(ReportList *reports)
Definition report.cc:97
void BKE_report_print_level_set(ReportList *reports, eReportType level)
Definition report.cc:265
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
void BKE_reports_init(ReportList *reports, int flag)
Definition report.cc:82
void BKE_scene_free_depsgraph_hash(Scene *scene)
Definition scene.cc:3311
ScrArea * BKE_screen_find_big_area(const bScreen *screen, int spacetype, short min)
Definition screen.cc:944
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:846
void BKE_sound_init(struct Main *bmain)
void BKE_undosys_stack_init_from_context(UndoStack *ustack, bContext *C)
void BKE_undosys_stack_clear(UndoStack *ustack)
void BKE_undosys_stack_init_from_main(UndoStack *ustack, Main *bmain)
UndoStack * BKE_undosys_stack_create()
void BKE_workspace_active_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace) SETTER_ATTRS
Definition workspace.cc:567
WorkSpaceLayout * BKE_workspace_layout_find_global(const Main *bmain, const bScreen *screen, WorkSpace **r_workspace) ATTR_NONNULL(1
void BLF_reset_fonts()
Definition blf.cc:83
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:440
float BLF_width(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:802
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:83
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:360
bool BLI_file_is_writable(const char *filepath) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition fileops_c.cc:291
#define O_BINARY
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
bool BLI_file_magic_is_gzip(const char header[4])
Definition fileops_c.cc:257
int BLI_stat(const char *path, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
struct LinkNode * BLI_file_read_as_lines(const char *filepath) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:563
void BLI_filelist_entry_size_to_string(const struct stat *st, uint64_t st_size_fallback, bool compact, char r_size[FILELIST_DIRENTRY_SIZE_LEN])
void BLI_file_free_lines(struct LinkNode *lines)
Definition storage.cc:609
bool BLI_file_magic_is_zstd(const char header[4])
Definition fileops_c.cc:264
struct stat BLI_stat_t
int BLI_access(const char *filepath, int mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int BLI_rename_overwrite(const char *from, const char *to) ATTR_NONNULL()
Definition fileops_c.cc:528
void BLI_filelist_entry_datetime_to_string(const struct stat *st, int64_t ts, bool compact, char r_time[FILELIST_DIRENTRY_TIME_LEN], char r_date[FILELIST_DIRENTRY_DATE_LEN], bool *r_is_today, bool *r_is_yesterday)
int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define FILELIST_DIRENTRY_SIZE_LEN
#define FILELIST_DIRENTRY_DATE_LEN
#define FILELIST_DIRENTRY_TIME_LEN
Wrapper for reading from various sources (e.g. raw files, compressed files, memory....
FileReader * BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
FileReader * BLI_filereader_new_file(int filedes) ATTR_WARN_UNUSED_RESULT
FileReader * BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
void * BLI_findstring(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:608
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count_at_most(const ListBase *listbase, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:511
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
void * BLI_findstring_ptr(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:651
MINLINE int max_ii(int a, int b)
void BLI_math_time_seconds_decompose(double seconds, double *r_days, double *r_hours, double *r_minutes, double *r_seconds, double *r_milliseconds)
Definition math_time.cc:12
ATTR_WARN_UNUSED_RESULT const size_t num
bool BLI_path_filename_ensure(char *filepath, size_t filepath_maxncpy, const char *filename) ATTR_NONNULL(1
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
void BLI_path_to_display_name(char *display_name, int display_name_maxncpy, const char *name) ATTR_NONNULL(1
#define FILE_MAXFILE
#define FILE_MAX
bool BLI_path_extension_replace(char *path, size_t path_maxncpy, const char *ext) ATTR_NONNULL(1
#define BLI_path_join(...)
void void void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy) ATTR_NONNULL(1
int BLI_path_canonicalize_native(char *path, int path_maxncpy)
bool BLI_path_extension_ensure(char *path, size_t path_maxncpy, const char *ext) ATTR_NONNULL(1
int BLI_path_sequence_decode(const char *path, char *head, size_t head_maxncpy, char *tail, size_t tail_maxncpy, unsigned short *r_digits_len)
Definition path_utils.cc:58
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
const char * BLI_path_slash_rfind(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define BLI_path_cmp
void BLI_path_sequence_encode(char *path, size_t path_maxncpy, const char *head, const char *tail, unsigned short numlen, int pic)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:604
int BLI_str_rstrip_digits(char *str) ATTR_NONNULL()
Definition string.cc:1012
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define SNPRINTF_UTF8(dst, format,...)
unsigned int uint
unsigned short ushort
int BLI_thread_is_main(void)
Definition threads.cc:179
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
void BLI_timer_on_file_load(void)
Definition BLI_timer.cc:144
#define UNUSED_VARS(...)
#define STREQLEN(a, b, n)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define STREQ(a, b)
Compatibility-like things for windows.
#define S_ISDIR(x)
external readfile function prototypes.
void BLO_sanitize_experimental_features_userpref_blend(UserDef *userdef)
eBLOReadSkip
@ BLO_READ_SKIP_DATA
@ BLO_READ_SKIP_USERDEF
external writefile.cc function prototypes.
bool BLO_write_file(Main *mainvar, const char *filepath, int write_flags, const BlendFileWriteParams *params, ReportList *reports)
eBLO_WritePathRemap
@ BLO_WRITE_PATH_REMAP_NONE
@ BLO_WRITE_PATH_REMAP_ABSOLUTE
@ BLO_WRITE_PATH_REMAP_RELATIVE
#define RPT_(msgid)
bool BLT_translate_new_dataname()
#define BLT_I18NCONTEXT_ID_WORKSPACE
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define CTX_DATA_(context, msgid)
#define IFACE_(msgid)
#define DATA_(msgid)
void BPY_python_reset(bContext *C)
bool BPY_run_string_eval(bContext *C, const char *imports[], const char *expr)
bool BPY_run_string_exec(bContext *C, const char *imports[], const char *expr)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
#define CLOG_INFO_NOCHECK(clg_ref, format,...)
Definition CLG_log.h:204
#define CLOG_INFO(clg_ref,...)
Definition CLG_log.h:190
@ NTREE_COMPOSIT
eDrawType
@ OB_SOLID
@ R_ALPHAPREMUL
@ RGN_TYPE_WINDOW
@ FILE_SORT_DEFAULT
@ FILE_SORT_TIME
@ FILE_BLENDER
@ FILE_TYPE_BLENDER
@ FILE_TYPE_FOLDER
@ SPACE_VIEW3D
@ FILE_VERTICALDISPLAY
@ FILE_DEFAULTDISPLAY
#define UI_SCALE_FAC
@ USER_SAVE_PROMPT
@ USER_GLOBALUNDO
@ USER_RELPATHS
@ USER_INTERNET_ALLOW
@ USER_FILENOUI
@ USER_FILECOMPRESS
@ USER_SCRIPT_AUTOEXEC_DISABLE
@ USER_AUTOSAVE
@ USER_FILE_PREVIEW_NONE
@ USER_FILE_PREVIEW_CAMERA
@ USER_FILE_PREVIEW_SCREENSHOT
@ USER_FILE_PREVIEW_AUTO
@ V3D_OFSDRAW_NONE
@ V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS
@ V3D_RUNTIME_LOCAL_MAYBE_EMPTY
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ KEYCONF_INIT_DEFAULT
int datatoc_startup_blend_size
const char datatoc_startup_blend[]
void ED_file_read_bookmarks()
int ED_image_save_all_modified_info(const Main *bmain, ReportList *reports)
bool ED_image_should_save_modified(const Main *bmain)
bool ED_image_save_all_modified(const bContext *C, ReportList *reports)
void ED_outliner_select_sync_from_all_tag(bContext *C)
void ED_preview_restart_queue_free()
void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen)
WorkSpaceLayout * ED_workspace_layout_duplicate(Main *bmain, WorkSpace *workspace, const WorkSpaceLayout *layout_old, wmWindow *win) ATTR_NONNULL()
MemFile * ED_undosys_stack_memfile_get_if_active(UndoStack *ustack)
void ED_editors_exit(Main *bmain, bool do_undo_system)
Definition ed_util.cc:224
bool ED_editors_flush_edits(Main *bmain)
Definition ed_util.cc:336
void ED_editors_init(bContext *C)
Definition ed_util.cc:100
void ED_view3d_local_collections_reset(const bContext *C, bool reset_all)
ImBuf * ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, Scene *scene, eDrawType drawtype, View3D *v3d, ARegion *region, int sizex, int sizey, eImBufFlags imbuf_flag, int alpha_mode, const char *viewname, bool restore_rv3d_mats, GPUOffScreen *ofs, GPUViewport *viewport, char err_out[256])
ImBuf * ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph, Scene *scene, View3DShading *shading_override, eDrawType drawtype, Object *camera, int width, int height, eImBufFlags imbuf_flags, eV3DOffscreenDrawFlag draw_flags, int alpha_mode, const char *viewname, GPUOffScreen *ofs, GPUViewport *viewport, char err_out[256])
GHOST C-API function and type declarations.
void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle, GHOST_TUserDataPtr user_data)
void GHOST_addToSystemRecentFiles(const char *filepath)
static void split(const char *text, const char *seps, char ***str, int *count)
bool GPU_backend_type_selection_is_overridden()
void GPU_backend_type_selection_set_override(GPUBackendType backend_type)
bool GPU_backend_type_selection_detect()
ImBuf * IMB_dupImBuf(const ImBuf *ibuf1)
ImBuf * IMB_allocFromBufferOwn(uint8_t *byte_buffer, float *float_buffer, unsigned int w, unsigned int h, unsigned int channels)
void IMB_freeImBuf(ImBuf *ibuf)
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:465
@ IB_byte_data
void IMB_metadata_set_field(IDProperty *metadata, const char *key, const char *value)
Definition metadata.cc:68
void IMB_metadata_ensure(IDProperty **metadata)
Definition metadata.cc:23
ImBuf * IMB_thumb_create(const char *filepath, ThumbSize size, ThumbSource source, ImBuf *img)
Definition thumbs.cc:490
void IMB_thumb_delete(const char *file_or_lib_path, ThumbSize size)
Definition thumbs.cc:520
@ THB_FAIL
Definition IMB_thumbs.hh:24
@ THB_LARGE
Definition IMB_thumbs.hh:23
@ THB_SOURCE_BLEND
Definition IMB_thumbs.hh:30
#define PREVIEW_RENDER_LARGE_HEIGHT
Definition IMB_thumbs.hh:41
void MEM_CacheLimiter_set_maximum(size_t m)
Read Guarded memory(de)allocation.
@ RNA_EQ_STRICT
@ PROP_POINTER
Definition RNA_types.hh:167
@ PROP_COLLECTION
Definition RNA_types.hh:168
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
void UI_alert(bContext *C, blender::StringRef title, blender::StringRef message, eAlertIcon icon, bool compact)
uiBut * uiDefIconTextBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, std::optional< blender::StringRef > tip)
#define UI_UNIT_Y
void UI_init_userdef()
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
@ UI_BLOCK_NUMSELECT
@ UI_BLOCK_LOOP
@ UI_BLOCK_KEEP_OPEN
@ UI_BLOCK_NO_WIN_CLIP
void UI_block_theme_style_set(uiBlock *block, char theme_style)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
@ UI_BUT_REDALERT
@ UI_BUT_ACTIVE_DEFAULT
void UI_popup_block_invoke(bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free)
const uiStyle * UI_style_get_dpi()
void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block)
bool UI_popup_block_name_exists(const bScreen *screen, blender::StringRef name)
void UI_fontstyle_set(const uiFontStyle *fs)
void UI_but_drawflag_disable(uiBut *but, int flag)
void UI_block_bounds_set_centered(uiBlock *block, int addval)
Definition interface.cc:679
@ UI_BLOCK_THEME_STYLE_POPUP
uiBut * uiDefButBitC(uiBlock *block, ButType type, int bit, int retval, blender::StringRef str, int x, int y, short width, short height, char *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BUT_TEXT_LEFT
void UI_but_flag_enable(uiBut *but, int flag)
@ ALERT_ICON_WARNING
@ ALERT_ICON_QUESTION
@ ALERT_ICON_ERROR
@ UI_ITEM_R_TOGGLE
uiLayout * uiItemsAlertBox(uiBlock *block, const uiStyle *style, const int dialog_width, const eAlertIcon icon, const int icon_size)
#define UI_ITEM_NONE
uiBut * uiItemL_ex(uiLayout *layout, blender::StringRef name, int icon, bool highlight, bool redalert)
void UI_view2d_zoom_cache_reset()
Definition view2d.cc:1041
@ WM_FILESEL_FILEPATH
Definition WM_api.hh:1124
@ FILE_OPENFILE
Definition WM_api.hh:1133
@ FILE_SAVE
Definition WM_api.hh:1134
#define ND_ASSET_LIST_READING
Definition WM_types.hh:550
#define NC_WINDOW
Definition WM_types.hh:375
#define ND_FILEREAD
Definition WM_types.hh:412
#define NC_WM
Definition WM_types.hh:374
#define ND_DATACHANGED
Definition WM_types.hh:414
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_ASSET
Definition WM_types.hh:404
#define ND_FILESAVE
Definition WM_types.hh:413
void(*)(bContext *C, void *user_data) wmGenericCallbackFn
Definition WM_types.hh:147
#define U
ATTR_WARN_UNUSED_RESULT const BMLoop * l
BPy_StructRNA * depsgraph
long long int int64_t
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
constexpr bool endswith(StringRef suffix) const
#define offsetof(t, d)
#define INT32_MAX
#define INT32_MIN
uint col
#define printf(...)
#define abs
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ListBase R_engines
#define LOG(level)
Definition log.h:97
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong state[N]
#define G(x, y, z)
void catalogs_set_save_catalogs_when_file_is_saved(bool should_save)
void pre_save_assets(Main *bmain)
void set_approximate_size_limit(int64_t limit_in_bytes)
constexpr int MAX_CHANNELS
VecBase< int32_t, 2 > int2
void ntreeCompositUpdateRLayers(bNodeTree *ntree)
const int status
return ret
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_collection_begin(PointerRNA *ptr, PropertyRNA *prop, CollectionPropertyIterator *iter)
PropertyType RNA_property_type(PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_collection_next(CollectionPropertyIterator *iter)
int RNA_int_get(PointerRNA *ptr, const char *name)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
std::string RNA_property_string_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_property_collection_end(CollectionPropertyIterator *iter)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_struct_iterator_property(StructRNA *type)
const char * RNA_property_identifier(const PropertyRNA *prop)
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
bool RNA_property_equals(Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, PropertyRNA *prop, eRNACompareMode mode)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_string_file_path(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
LinkNode * resynced_lib_overrides_libraries
double lib_overrides_recursive_resync
struct BlendFileReadReport::@077003321007012203371307366200326111253233132273 duration
int resynced_lib_overrides_libraries_count
struct BlendFileReadReport::@124362246150331065306145367225174223236260272332 count
wmWindowManager * old_wm
const BlendThumbnail * thumb
eBLO_WritePathRemap remap_mode
FileReaderSeekFn seek
FileReaderCloseFn close
FileReaderReadFn read
bool is_untrusted
Definition wm_files.cc:3308
IDProperty * metadata
char filepath[1024]
Definition DNA_ID.h:552
struct LinkNode * next
void * first
bool is_missing_opencolorio_config
Definition BKE_main.hh:157
ListBase scenes
Definition BKE_main.hh:278
ListBase wm
Definition BKE_main.hh:307
short subversionfile
Definition BKE_main.hh:182
bool is_asset_edit_file
Definition BKE_main.hh:199
bool has_forward_compatibility_issues
Definition BKE_main.hh:192
char filepath[1024]
Definition BKE_main.hh:179
bool recovered
Definition BKE_main.hh:206
BlendThumbnail * blen_thumb
Definition BKE_main.hh:259
ListBase libraries
Definition BKE_main.hh:279
ListBase screens
Definition BKE_main.hh:292
short versionfile
Definition BKE_main.hh:181
ListBase workspaces
Definition BKE_main.hh:315
MainColorspace colorspace
Definition BKE_main.hh:274
wmOperatorStatus(* run)(bContext *C, wmOperator *op)
Definition wm_files.cc:3126
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
RecentFile * next
Definition WM_types.hh:1450
char * filepath
Definition WM_types.hh:1451
ListBase list
Definition BKE_report.hh:75
struct Object * camera
ListBase spacedata
UserDef_Runtime runtime
View3D_Runtime runtime
struct View3D * localvd
View3DShading shading
Wrapper for bScreen.
void use_property_decorate_set(bool is_sep)
void scale_y_set(float scale)
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & split(float percentage, bool align)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
uiFontStyle widget
short columnspace
wmGenericCallbackFn exec
Definition WM_types.hh:150
wmGenericUserDataFreeFn free_user_data
Definition WM_types.hh:152
const char * app_template_override
Definition wm_files.hh:58
unsigned int use_factory_settings_app_template_only
Definition wm_files.hh:39
const char * filepath_startup_override
Definition wm_files.hh:53
unsigned int use_factory_settings
Definition wm_files.hh:37
unsigned int is_first_time
Definition wm_files.hh:49
unsigned int use_empty_data
Definition wm_files.hh:44
unsigned int use_data
Definition wm_files.hh:29
unsigned int use_userdef
Definition wm_files.hh:31
const char * idname
Definition WM_types.hh:1035
wmOperatorStatus(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1065
struct ReportList * reports
IDProperty * properties
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
WindowManagerRuntimeHandle * runtime
struct wmWindow * parent
struct wmEvent * eventstate
struct wmEvent * event_last_handled
i
Definition text_draw.cc:230
uint len
void wm_close_and_free(bContext *C, wmWindowManager *wm)
Definition wm.cc:552
void wm_clear_default_size(bContext *C)
Definition wm.cc:494
void WM_check(bContext *C)
Definition wm.cc:455
void WM_keyconfig_reload(bContext *C)
Definition wm.cc:406
void wm_add_default(Main *bmain, bContext *C)
Definition wm.cc:516
void WM_cursor_wait(bool val)
uint8_t * WM_window_pixels_read_from_frontbuffer(const wmWindowManager *wm, const wmWindow *win, int r_size[2])
Definition wm_draw.cc:1325
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_fileselect(bContext *C, wmOperator *op)
void wm_event_do_depsgraph(bContext *C, bool is_after_open_file)
void WM_main_add_notifier(uint type, void *reference)
void WM_report_banner_show(wmWindowManager *wm, wmWindow *win)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
wmOperatorStatus WM_operator_name_call_with_properties(bContext *C, const char *opstring, blender::wm::OpCallContext context, IDProperty *properties, const wmEvent *event)
void WM_event_remove_handlers(bContext *C, ListBase *handlers)
@ WM_HANDLER_TYPE_OP
@ TIMERAUTOSAVE
void WM_autosave_write(wmWindowManager *wm, Main *bmain)
Definition wm_files.cc:2351
static void free_post_file_close_action(void *arg)
Definition wm_files.cc:4341
void WM_file_autosave_init(wmWindowManager *wm)
Definition wm_files.cc:2394
bool WM_autosave_is_scheduled(wmWindowManager *wm)
Definition wm_files.cc:2346
bool WM_file_read(bContext *C, const char *filepath, const bool use_scripts_autoexec_check, ReportList *reports)
Definition wm_files.cc:1054
void wm_homefile_read(bContext *C, const wmHomeFileRead_Params *params_homefile, ReportList *reports)
Definition wm_files.cc:1591
void wm_autosave_timer_end(wmWindowManager *wm)
Definition wm_files.cc:2386
static void create_operator_state(wmOperatorType *ot, int first_state)
Definition wm_files.cc:3106
#define BKE_READ_EXOTIC_OK_BLEND
Definition wm_files.cc:557
bool wm_operator_close_file_dialog_if_needed(bContext *C, wmOperator *op, wmGenericCallbackFn post_action_fn)
Definition wm_files.cc:4905
static void wm_init_userdef(Main *bmain)
Definition wm_files.cc:512
static std::string wm_save_mainfile_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition wm_files.cc:3941
static wmOperatorStatus wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3520
static bool wm_save_mainfile_check(bContext *, wmOperator *op)
Definition wm_files.cc:3828
static wmOperatorStatus wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3255
static wmOperatorStatus wm_homefile_read_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2843
void wm_test_foreign_file_warning(bContext *C)
Definition wm_files.cc:4305
static uiBlock * block_create__close_file_dialog(bContext *C, ARegion *region, void *arg1)
Definition wm_files.cc:4722
static BlendFileReadWMSetupData * wm_file_read_setup_wm_init(bContext *C, Main *bmain, const bool is_read_homefile)
Definition wm_files.cc:211
void WM_OT_read_history(wmOperatorType *ot)
Definition wm_files.cc:2822
static wmOperatorStatus wm_history_file_read_exec(bContext *, wmOperator *)
Definition wm_files.cc:2815
static void wm_open_mainfile_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:3158
static bool wm_file_write_check_with_report_on_failure(Main *bmain, const char *filepath, ReportList *reports)
Definition wm_files.cc:2094
#define BKE_READ_EXOTIC_FAIL_FORMAT
Definition wm_files.cc:555
void wm_test_autorun_revert_action_set(wmOperatorType *ot, PointerRNA *ptr)
Definition wm_files.cc:4241
#define BKE_READ_EXOTIC_FAIL_OPEN
Definition wm_files.cc:556
ClearRecentInclude
Definition wm_files.cc:3998
@ CLEAR_RECENT_MISSING
Definition wm_files.cc:3998
@ CLEAR_RECENT_ALL
Definition wm_files.cc:3998
static uiBlock * block_create_autorun_warning(bContext *C, ARegion *region, void *)
Definition wm_files.cc:4115
bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWindowManager *wm)
Definition wm_files.cc:187
static ImBuf * blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **r_thumb)
Definition wm_files.cc:1879
static bool wm_file_read_opwrap(bContext *C, const char *filepath, const bool use_scripts_autoexec_check, ReportList *reports)
Definition wm_files.cc:3090
void WM_OT_save_as_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3862
void WM_OT_open_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3373
void WM_file_tag_modified()
Definition wm_files.cc:177
static wmOperatorStatus wm_recover_last_session_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3505
static void wm_block_file_close_cancel_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4675
static RecentFile * wm_file_history_find(const char *filepath)
Definition wm_files.cc:1671
const char * WM_init_state_app_template_get()
Definition wm_files.cc:1207
PointerRNA * ptr
Definition wm_files.cc:4238
void wm_file_read_report(Main *bmain, wmWindow *win)
Definition wm_files.cc:656
void wm_autosave_timer_begin(wmWindowManager *wm)
Definition wm_files.cc:2381
static void rna_struct_update_when_changed(bContext *C, Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b)
Definition wm_files.cc:2662
void WM_OT_recover_last_session(wmOperatorType *ot)
Definition wm_files.cc:3536
static void wm_file_read_pre(bool use_data, bool)
Definition wm_files.cc:688
static wmOperatorStatus wm_open_mainfile__select_file_path_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3182
static blender::int2 blend_file_thumb_clamp_size(const int size[2], const int limit)
Definition wm_files.cc:1862
static void wm_block_autorun_warning_enable_scripts(bContext *C, uiBlock *block)
Definition wm_files.cc:4095
static void save_file_overwrite_saveas(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4462
static void wm_history_files_free()
Definition wm_files.cc:1664
static RecentFile * wm_history_file_new(const char *filepath)
Definition wm_files.cc:1650
static bool wm_revert_mainfile_poll(bContext *)
Definition wm_files.cc:3450
static void wm_read_callback_pre_wrapper(bContext *C, const char *filepath)
Definition wm_files.cc:888
static wmOperatorStatus wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3584
void wm_history_file_read()
Definition wm_files.cc:1618
void wm_autosave_delete()
Definition wm_files.cc:2425
static bool wm_open_mainfile_check(bContext *, wmOperator *op)
Definition wm_files.cc:3312
static ImBuf * blend_file_thumb_from_camera(const bContext *C, Scene *scene, bScreen *screen, BlendThumbnail **r_thumb)
Definition wm_files.cc:1951
static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:2951
static OperatorDispatchTarget wm_open_mainfile_dispatch_targets[]
Definition wm_files.cc:3243
#define USERDEF_RESTORE(member)
static void wm_userpref_read_exceptions(UserDef *userdef_curr, const UserDef *userdef_prev)
Definition wm_files.cc:2648
static wmOperatorStatus wm_open_mainfile__open(bContext *C, wmOperator *op)
Definition wm_files.cc:3214
void WM_OT_read_userpref(wmOperatorType *ot)
Definition wm_files.cc:2756
static void wm_test_autorun_revert_action_exec(bContext *C)
Definition wm_files.cc:4254
static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:3511
static char save_images_when_file_is_closed
Definition wm_files.cc:4593
static void save_file_overwrite_cancel_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4418
static void wm_block_file_close_save_button(uiBlock *block, wmGenericCallback *post_action, const bool needs_overwrite_confirm)
Definition wm_files.cc:4691
static wmOperatorStatus wm_userpref_read_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2766
static struct @271061015143360014145002024272307131065165053366 wm_test_autorun_revert_action_data
void WM_OT_save_homefile(wmOperatorType *ot)
Definition wm_files.cc:2600
void WM_OT_read_factory_userpref(wmOperatorType *ot)
Definition wm_files.cc:2795
static void wm_file_read_setup_wm_finalize(bContext *C, Main *bmain, BlendFileReadWMSetupData *wm_setup_data)
Definition wm_files.cc:432
static int get_operator_state(wmOperator *op)
Definition wm_files.cc:3114
static wmOperatorStatus wm_userpref_write_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2617
void WM_init_state_app_template_set(const char *app_template)
Definition wm_files.cc:1195
static void save_set_compress(wmOperator *op)
Definition wm_files.cc:3637
@ OPEN_MAINFILE_STATE_OPEN
Definition wm_files.cc:3153
@ OPEN_MAINFILE_STATE_DISCARD_CHANGES
Definition wm_files.cc:3151
@ OPEN_MAINFILE_STATE_SELECT_FILE_PATH
Definition wm_files.cc:3152
static wmOperatorStatus wm_revert_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3431
static struct @151101107046063142034073030121153110227121110071 wm_init_state_app_template
void WM_OT_save_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3953
void WM_OT_recover_auto_save(wmOperatorType *ot)
Definition wm_files.cc:3599
void wm_homefile_read_ex(bContext *C, const wmHomeFileRead_Params *params_homefile, ReportList *reports, wmFileReadPost_Params **r_params_file_read_post)
Definition wm_files.cc:1218
static std::string wm_save_as_mainfile_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition wm_files.cc:3851
static void wm_file_read_setup_wm_keep_old(const bContext *C, Main *bmain, BlendFileReadWMSetupData *wm_setup_data, wmWindowManager *wm, const bool load_ui)
Definition wm_files.cc:320
static wmOperatorStatus wm_open_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3262
char app_template[64]
Definition wm_files.cc:1191
static wmOperatorStatus wm_read_factory_settings_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3032
static void wm_block_file_close_discard(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4601
void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *)
Definition wm_files.cc:2399
static void wm_history_file_write()
Definition wm_files.cc:1681
static void set_next_operator_state(wmOperator *op, int state)
Definition wm_files.cc:3119
void WM_file_autoexec_init(const char *filepath)
Definition wm_files.cc:641
static wmOperatorStatus wm_recover_auto_save_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3554
void wm_homefile_read_post(bContext *C, const wmFileReadPost_Params *params_file_read_post)
Definition wm_files.cc:1598
static wmOperatorStatus wm_open_mainfile__discard_changes_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3167
static void wm_file_read_post(bContext *C, const char *filepath, const wmFileReadPost_Params *params)
Definition wm_files.cc:722
static void wm_userpref_update_when_changed(bContext *C, Main *bmain, UserDef *userdef_prev, UserDef *userdef_curr)
Definition wm_files.cc:2696
static const char * close_file_dialog_name
Definition wm_files.cc:4713
static void wm_autosave_location(char filepath[FILE_MAX])
Definition wm_files.cc:2297
static wmOperatorStatus wm_clear_recent_files_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition wm_files.cc:4006
static std::string wm_open_mainfile_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition wm_files.cc:3267
static void save_catalogs_when_file_is_closed_set_fn(bContext *, void *arg1, void *)
Definition wm_files.cc:4715
static wmOperatorStatus operator_state_dispatch(bContext *C, wmOperator *op, OperatorDispatchTarget *targets)
Definition wm_files.cc:3129
static void wm_filepath_default(const Main *bmain, char *filepath)
Definition wm_files.cc:3627
void WM_OT_read_factory_settings(wmOperatorType *ot)
Definition wm_files.cc:3064
static wmOperatorStatus wm_homefile_write_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2571
static bool wm_autosave_write_try(Main *bmain, wmWindowManager *wm)
Definition wm_files.cc:2320
static int wm_read_exotic(const char *filepath)
Definition wm_files.cc:572
static void wm_block_autorun_warning_reload_with_scripts(bContext *C, uiBlock *block)
Definition wm_files.cc:4078
static wmOperatorStatus wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3687
static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bmain)
Definition wm_files.cc:4355
void wm_test_autorun_warning(bContext *C)
Definition wm_files.cc:4274
static void wm_block_file_close_cancel(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4595
static wmOperatorStatus wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3899
wmOperatorType * ot
Definition wm_files.cc:4237
static wmOperatorStatus wm_recover_last_session_impl(bContext *C, wmOperator *op, const bool use_scripts_autoexec_check)
Definition wm_files.cc:3487
static void wm_file_read_setup_wm_substitute_old_window(wmWindowManager *oldwm, wmWindowManager *wm, wmWindow *oldwin, wmWindow *win)
Definition wm_files.cc:274
static wmOperatorStatus wm_userpref_read_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2713
void wm_open_init_load_ui(wmOperator *op, bool use_prefs)
Definition wm_files.cc:2475
static wmOperatorStatus wm_open_mainfile_dispatch(bContext *C, wmOperator *op)
Definition wm_files.cc:3250
static void read_factory_reset_props(wmOperatorType *ot)
Definition wm_files.cc:2452
static void wm_read_callback_post_wrapper(bContext *C, const char *filepath, const bool success)
Definition wm_files.cc:895
static wmOperatorStatus wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2960
static void wm_clear_recent_files_ui(bContext *, wmOperator *op)
Definition wm_files.cc:4034
static void wm_block_autorun_warning_ignore(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4069
static void wm_history_file_free(RecentFile *recent)
Definition wm_files.cc:1657
static void wm_autosave_timer_begin_ex(wmWindowManager *wm, double timestep)
Definition wm_files.cc:2372
static const EnumPropertyItem prop_clear_recent_types[]
Definition wm_files.cc:4000
static void save_file_overwrite_confirm_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4453
static void wm_open_mainfile_def_property_use_scripts(wmOperatorType *ot)
Definition wm_files.cc:3363
#define BKE_READ_EXOTIC_FAIL_PATH
Definition wm_files.cc:554
static bool wm_file_write(bContext *C, const char *filepath, int fileflags, eBLO_WritePathRemap remap_mode, bool use_save_as_copy, ReportList *reports)
Definition wm_files.cc:2129
static void file_read_reports_finalize(BlendFileReadReport *bf_reports)
Definition wm_files.cc:924
static void wm_free_operator_properties_callback(void *user_data)
Definition wm_files.cc:4347
static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4615
static void save_set_filepath(bContext *C, wmOperator *op)
Definition wm_files.cc:3653
static wmOperatorStatus wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3706
static uiBlock * block_create_save_file_overwrite_dialog(bContext *C, ARegion *region, void *arg1)
Definition wm_files.cc:4480
static void wm_file_read_setup_wm_use_new(bContext *C, Main *, BlendFileReadWMSetupData *wm_setup_data, wmWindowManager *wm)
Definition wm_files.cc:371
void WM_OT_read_homefile(wmOperatorType *ot)
Definition wm_files.cc:2988
static std::string wm_save_as_mainfile_get_name(wmOperatorType *ot, PointerRNA *ptr)
Definition wm_files.cc:3843
static wmOperatorStatus wm_homefile_write_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2512
static uint8_t * blend_file_thumb_fast_downscale(const uint8_t *src_rect, const int src_size[2], const int dst_size[2])
Definition wm_files.cc:1772
static void wm_block_file_close_discard_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4683
static void wm_open_mainfile_ui(bContext *, wmOperator *op)
Definition wm_files.cc:3342
static void wm_history_file_update()
Definition wm_files.cc:1707
bool write_crash_blend()
Definition wm_files.cc:2078
static wmOperatorStatus wm_revert_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3412
static void wm_gpu_backend_override_from_userdef()
Definition wm_files.cc:498
void WM_OT_save_userpref(wmOperatorType *ot)
Definition wm_files.cc:2629
static void save_file_overwrite_confirm(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4426
void WM_OT_clear_recent_files(wmOperatorType *ot)
Definition wm_files.cc:4045
bool WM_file_recover_last_session(bContext *C, const bool use_scripts_autoexec_check, ReportList *reports)
Definition wm_files.cc:3475
static void read_homefile_props(wmOperatorType *ot)
Definition wm_files.cc:2970
void wm_save_file_overwrite_dialog(bContext *C, wmOperator *op)
Definition wm_files.cc:4574
static const char * save_file_overwrite_dialog_name
Definition wm_files.cc:4353
static void save_file_overwrite_cancel(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4412
static wmOperatorStatus wm_clear_recent_files_exec(bContext *, wmOperator *op)
Definition wm_files.cc:4014
static void save_file_overwrite_saveas_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4471
void WM_OT_revert_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3456
void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action)
Definition wm_files.cc:4894
bool wm_open_init_use_scripts(wmOperator *op, bool use_prefs)
Definition wm_files.cc:2485
void WM_reinit_gizmomap_all(Main *bmain)
void WM_init_splash(bContext *C)
void wm_exit_schedule_delayed(const bContext *C)
void WM_jobs_kill_all(wmWindowManager *wm)
Definition wm_jobs.cc:602
void WM_keyconfig_update(wmWindowManager *wm)
void WM_msgbus_destroy(wmMsgBus *mbus)
@ WM_MSG_STATICTYPE_FILE_READ
void WM_msg_publish_static(wmMsgBus *mbus, int event)
void WM_operator_properties_filesel(wmOperatorType *ot, const int filter, const short type, const eFileSel_Action action, const eFileSel_Flag flag, const short display, const short sort)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_operatortype_last_properties_clear_all()
wmOperatorStatus WM_operator_confirm(bContext *C, wmOperator *op, const wmEvent *)
void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
wmOperatorStatus WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)
void WM_operator_properties_free(PointerRNA *ptr)
wmOperatorStatus WM_operator_props_popup_confirm_ex(bContext *C, wmOperator *op, const wmEvent *, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default, std::optional< std::string > message)
void WM_toolsystem_init(const bContext *C)
void WM_generic_callback_free(wmGenericCallback *callback)
Definition wm_utils.cc:20
wmGenericCallback * WM_generic_callback_steal(wmGenericCallback *callback)
Definition wm_utils.cc:30
void WM_init_input_devices()
WorkSpaceLayout * WM_window_get_active_layout(const wmWindow *win)
void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
Definition wm_window.cc:463
void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen)
void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm)
void wm_window_clear_drawable(wmWindowManager *wm)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const wmEventType event_type, const double time_step)
void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
bool WM_window_is_temp_screen(const wmWindow *win)
void WM_window_set_active_layout(wmWindow *win, WorkSpace *workspace, WorkSpaceLayout *layout)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
bScreen * WM_window_get_active_screen(const wmWindow *win)