Blender V4.5
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_time.h"
41#include "BLI_memory_cache.hh"
42#include "BLI_string.h"
43#include "BLI_system.h"
44#include "BLI_threads.h"
45#include "BLI_time.h"
46#include "BLI_timer.h"
47#include "BLI_utildefines.h"
48#include BLI_SYSTEM_PID_H
49
50#include "BLO_readfile.hh"
51#include "BLT_translation.hh"
52
53#include "BLF_api.hh"
54
55#include "DNA_scene_types.h"
56#include "DNA_screen_types.h"
57#include "DNA_space_types.h"
58#include "DNA_userdef_types.h"
60#include "DNA_workspace_types.h"
61
62#include "AS_asset_library.hh"
63
64#ifndef WITH_CYCLES
65# include "BKE_addon.h"
66#endif
67#include "BKE_appdir.hh"
68#include "BKE_autoexec.hh"
69#include "BKE_blender.hh"
70#include "BKE_blender_version.h"
71#include "BKE_blendfile.hh"
72#include "BKE_callbacks.hh"
73#include "BKE_context.hh"
74#include "BKE_global.hh"
75#include "BKE_idprop.hh"
76#include "BKE_lib_id.hh"
77#include "BKE_lib_override.hh"
78#include "BKE_lib_remap.hh"
79#include "BKE_library.hh"
80#include "BKE_main.hh"
81#include "BKE_main_namemap.hh"
82#include "BKE_node.hh"
83#include "BKE_packedFile.hh"
84#include "BKE_report.hh"
85#include "BKE_scene.hh"
86#include "BKE_screen.hh"
87#include "BKE_sound.h"
88#include "BKE_undo_system.hh"
89#include "BKE_workspace.hh"
90
91#include "BLO_writefile.hh"
92
93#include "RNA_access.hh"
94#include "RNA_define.hh"
95
96#include "IMB_imbuf.hh"
97#include "IMB_imbuf_types.hh"
98#include "IMB_metadata.hh"
99#include "IMB_thumbs.hh"
100
101#include "ED_asset.hh"
102#include "ED_datafiles.h"
103#include "ED_fileselect.hh"
104#include "ED_image.hh"
105#include "ED_outliner.hh"
106#include "ED_render.hh"
107#include "ED_screen.hh"
108#include "ED_undo.hh"
109#include "ED_util.hh"
110#include "ED_view3d.hh"
111#include "ED_view3d_offscreen.hh"
112
113#include "NOD_composite.hh"
114
115#include "GHOST_C-api.h"
116#include "GHOST_Path-api.hh"
117
118#include "GPU_context.hh"
119
120#include "SEQ_sequencer.hh"
121
122#include "UI_interface.hh"
123#include "UI_resources.hh"
124#include "UI_view2d.hh"
125
126/* Only to report a missing engine. */
127#include "RE_engine.h"
128
129#ifdef WITH_PYTHON
130# include "BPY_extern_python.hh"
131# include "BPY_extern_run.hh"
132#endif
133
134#include "DEG_depsgraph.hh"
135
136#include "WM_api.hh"
137#include "WM_keymap.hh"
138#include "WM_message.hh"
139#include "WM_toolsystem.hh"
140#include "WM_types.hh"
141
142#include "wm.hh"
143#include "wm_event_system.hh"
144#include "wm_files.hh"
145#include "wm_window.hh"
146
147#include "CLG_log.h"
148
149static RecentFile *wm_file_history_find(const char *filepath);
150static void wm_history_file_free(RecentFile *recent);
151static void wm_history_files_free();
152static void wm_history_file_update();
153static void wm_history_file_write();
154
156
157static CLG_LogRef LOG = {"wm.files"};
158
168#define USE_THUMBNAIL_FAST_DOWNSCALE
169
170/* -------------------------------------------------------------------- */
173
175{
176 wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
177 if (wm->file_saved) {
178 wm->file_saved = 0;
179 /* Notifier that data changed, for save-over warning or header. */
181 }
182}
183
189
191
192/* -------------------------------------------------------------------- */
195
203
209 Main *bmain,
210 const bool is_read_homefile)
211{
212 using namespace blender;
213 BLI_assert(BLI_listbase_count_at_most(&bmain->wm, 2) <= 1);
214 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
216 wm_setup_data->is_read_homefile = is_read_homefile;
217 /* This info is not always known yet when this function is called. */
218 wm_setup_data->is_factory_startup = false;
219
220 if (wm == nullptr) {
221 return wm_setup_data;
222 }
223
224 /* First wrap up running stuff.
225 *
226 * Code copied from `wm_init_exit.cc`. */
228
229 wmWindow *active_win = CTX_wm_window(C);
230 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
231 CTX_wm_window_set(C, win); /* Needed by operator close callbacks. */
232 WM_event_remove_handlers(C, &win->handlers);
233 WM_event_remove_handlers(C, &win->modalhandlers);
235 }
236 /* Reset active window. */
237 CTX_wm_window_set(C, active_win);
238
239 /* NOTE(@ideasman42): Clear the message bus so it's always cleared on file load.
240 * Otherwise it's cleared when "Load UI" is set (see #USER_FILENOUI and #wm_close_and_free).
241 * However it's _not_ cleared when the UI is kept. This complicates use from add-ons
242 * which can re-register subscribers on file-load. To support this use case,
243 * it's best to have predictable behavior - always clear. */
244 if (wm->message_bus != nullptr) {
245 WM_msgbus_destroy(wm->message_bus);
246 wm->message_bus = nullptr;
247 }
248
249 /* XXX Hack! We have to clear context popup-region here, because removing all
250 * #wmWindow::modalhandlers above frees the active menu (at least, in the 'startup splash' case),
251 * causing use-after-free error in later handling of the button callbacks in UI code
252 * (see #ui_apply_but_funcs_after()).
253 * Tried solving this by always nullptr-ing context's menu when setting wm/win/etc.,
254 * but it broke popups refreshing (see #47632),
255 * so for now just handling this specific case here. */
256 CTX_wm_region_popup_set(C, nullptr);
257
258 ED_editors_exit(bmain, true);
259
260 /* Asset loading is done by the UI/editors and they keep pointers into it. So make sure to clear
261 * it after UI/editors. */
264
265 /* NOTE: `wm_setup_data->old_wm` cannot be set here, as this pointer may be swapped with the
266 * newly read one in `setup_app_data` process (See #swap_wm_data_for_blendfile). */
267
268 return wm_setup_data;
269}
270
273 wmWindow *oldwin,
274 wmWindow *win)
275{
276 win->ghostwin = oldwin->ghostwin;
277 win->gpuctx = oldwin->gpuctx;
278 win->active = oldwin->active;
279 if (win->active) {
280 wm->winactive = win;
281 }
282 if (oldwm->windrawable == oldwin) {
283 oldwm->windrawable = nullptr;
284 wm->windrawable = win;
285 }
286
287 /* File loading in background mode still calls this. */
288 if (!G.background) {
289 /* Pointer back. */
290 GHOST_SetWindowUserData(static_cast<GHOST_WindowHandle>(win->ghostwin), win);
291 }
292
293 oldwin->ghostwin = nullptr;
294 oldwin->gpuctx = nullptr;
295
296 win->eventstate = oldwin->eventstate;
298 oldwin->eventstate = nullptr;
299 oldwin->event_last_handled = nullptr;
300
301 /* Ensure proper screen re-scaling. */
302 win->sizex = oldwin->sizex;
303 win->sizey = oldwin->sizey;
304 win->posx = oldwin->posx;
305 win->posy = oldwin->posy;
306}
307
318 Main *bmain,
319 BlendFileReadWMSetupData *wm_setup_data,
321 const bool load_ui)
322{
323 /* This data is not needed here, besides detecting that old WM has been kept (in caller code).
324 * Since `old_wm` is kept, do not free it, just clear the pointer as clean-up. */
325 wm_setup_data->old_wm = nullptr;
326
327 if (!load_ui) {
328 /* When loading without UI (i.e. keeping existing UI), no matching needed.
329 *
330 * The other UI data (workspaces, layouts, screens) has also been re-used from old Main, and
331 * newly read one from file has already been discarded in #setup_app_data. */
332 return;
333 }
334
335 /* Old WM is being reused, but other UI data (workspaces, layouts, screens) comes from the new
336 * file, so the WM needs to be updated to use these. */
337 bScreen *screen = CTX_wm_screen(C);
338 if (screen != nullptr) {
339 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
340 WorkSpace *workspace;
341
342 WorkSpaceLayout *layout_ref = BKE_workspace_layout_find_global(bmain, screen, &workspace);
343 BKE_workspace_active_set(win->workspace_hook, workspace);
344 win->scene = CTX_data_scene(C);
345
346 /* All windows get active screen from file. */
347 if (screen->winid == 0) {
348 WM_window_set_active_screen(win, workspace, screen);
349 }
350 else {
351#if 0
352 /* NOTE(@ideasman42): The screen referenced from the window has been freed,
353 * see: #107525. */
355#endif
357 bmain, workspace, layout_ref, win);
358
359 WM_window_set_active_layout(win, workspace, layout_new);
360 }
361
362 bScreen *win_screen = WM_window_get_active_screen(win);
363 win_screen->winid = win->winid;
364 }
365 }
366}
367
369 Main * /*bmain*/,
370 BlendFileReadWMSetupData *wm_setup_data,
372{
373 wmWindowManager *old_wm = wm_setup_data->old_wm;
374
375 wm->op_undo_depth = old_wm->op_undo_depth;
376
377 /* Move existing key configurations into the new WM. */
378 wm->keyconfigs = old_wm->keyconfigs;
379 wm->addonconf = old_wm->addonconf;
380 wm->defaultconf = old_wm->defaultconf;
381 wm->userconf = old_wm->userconf;
382
384 old_wm->addonconf = nullptr;
385 old_wm->defaultconf = nullptr;
386 old_wm->userconf = nullptr;
387
388 /* Ensure new keymaps are made, and space types are set. */
389 wm->init_flag = 0;
390 wm->winactive = nullptr;
391
392 /* Clearing drawable of old WM before deleting any context to avoid clearing the wrong wm. */
394
395 bool has_match = false;
396 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
397 LISTBASE_FOREACH (wmWindow *, old_win, &old_wm->windows) {
398 if (old_win->winid == win->winid) {
399 has_match = true;
400
401 wm_file_read_setup_wm_substitute_old_window(old_wm, wm, old_win, win);
402 }
403 }
404 }
405 /* Ensure that at least one window is kept open so we don't lose the context, see #42303. */
406 if (!has_match) {
408 wm,
409 static_cast<wmWindow *>(old_wm->windows.first),
410 static_cast<wmWindow *>(wm->windows.first));
411 }
412
413 wm_setup_data->old_wm = nullptr;
414 wm_close_and_free(C, old_wm);
415 /* Don't handle user counts as this is only ever called once #G_MAIN has already been freed via
416 * #BKE_main_free so any access to ID's referenced by the window-manager (from ID properties)
417 * will crash. See: #100703. */
418 BKE_libblock_free_data(&old_wm->id, false);
420 MEM_freeN(old_wm);
421}
422
430 Main *bmain,
431 BlendFileReadWMSetupData *wm_setup_data)
432{
433 BLI_assert(BLI_listbase_count_at_most(&bmain->wm, 2) <= 1);
434 BLI_assert(wm_setup_data != nullptr);
435 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
436
437 /* If reading factory startup file, and there was no previous WM, clear the size of the windows
438 * in newly read WM so that they get resized to occupy the whole available space on current
439 * monitor.
440 */
441 if (wm_setup_data->is_read_homefile && wm_setup_data->is_factory_startup &&
442 wm_setup_data->old_wm == nullptr)
443 {
445 }
446
447 if (wm == nullptr) {
448 /* Add a default WM in case none exists in newly read main (should only happen when opening
449 * an old pre-2.5 .blend file at startup). */
450 wm_add_default(bmain, C);
451 }
452 else if (wm_setup_data->old_wm != nullptr) {
453 if (wm_setup_data->old_wm == wm) {
454 /* Old WM was kept, update it with new workspaces/layouts/screens read from file.
455 *
456 * Happens when not loading UI, or when the newly read file has no WM (pre-2.5 files). */
458 C, bmain, wm_setup_data, wm, (G.fileflags & G_FILE_NO_UI) == 0);
459 }
460 else {
461 /* Using new WM from read file, try to keep current GHOST windows, transfer keymaps, etc.,
462 * from old WM.
463 *
464 * Also takes care of clearing old WM data (temporarily stored in `wm_setup_data->old_wm`).
465 */
466 wm_file_read_setup_wm_use_new(C, bmain, wm_setup_data, wm);
467 }
468 }
469 /* Else just using the new WM read from file, nothing to do. */
470 BLI_assert(wm_setup_data->old_wm == nullptr);
471 MEM_delete(wm_setup_data);
472
473 /* UI Updates. */
474 /* Flag local View3D's to check and exit if they are empty. */
475 LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
476 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
477 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
478 if (sl->spacetype == SPACE_VIEW3D) {
479 View3D *v3d = reinterpret_cast<View3D *>(sl);
480 if (v3d->localvd) {
482 }
483 }
484 }
485 }
486 }
487}
488
490
491/* -------------------------------------------------------------------- */
494
496{
497 /* Check if GPU backend is already set from the command line arguments. The command line
498 * arguments have higher priority than user preferences. */
500 return;
501 }
502
504}
505
509static void wm_init_userdef(Main *bmain)
510{
511 /* Not versioning, just avoid errors. */
512#ifndef WITH_CYCLES
513 BKE_addon_remove_safe(&U.addons, "cycles");
514#endif
515
517
518 /* Needed so loading a file from the command line respects user-pref #26156. */
519 SET_FLAG_FROM_TEST(G.fileflags, U.flag & USER_FILENOUI, G_FILE_NO_UI);
520
521 /* Set the python auto-execute setting from user prefs. */
522 /* Enabled by default, unless explicitly enabled in the command line which overrides. */
523 if ((G.f & G_FLAG_SCRIPT_OVERRIDE_PREF) == 0) {
525 }
526
527 /* Only reset "offline mode" if they weren't passes via command line arguments. */
528 if ((G.f & G_FLAG_INTERNET_OVERRIDE_PREF_ANY) == 0) {
530 }
531
532 const int64_t cache_limit = int64_t(U.memcachelimit) * 1024 * 1024;
533 MEM_CacheLimiter_set_maximum(cache_limit);
535
536 BKE_sound_init(bmain);
537
538 /* Update the temporary directory from the preferences or fall back to the system default. */
539 BKE_tempdir_init(U.tempdir);
540
541 /* Update input device preference. */
543
545
548}
549
550/* Return codes. */
551#define BKE_READ_EXOTIC_FAIL_PATH -3 /* File format is not supported. */
552#define BKE_READ_EXOTIC_FAIL_FORMAT -2 /* File format is not supported. */
553#define BKE_READ_EXOTIC_FAIL_OPEN -1 /* Can't open the file. */
554#define BKE_READ_EXOTIC_OK_BLEND 0 /* `.blend` file. */
555#if 0
556# define BKE_READ_EXOTIC_OK_OTHER 1 /* Other supported formats. */
557#endif
558
560
561/* -------------------------------------------------------------------- */
567
568/* Intended to check for non-blender formats but for now it only reads blends. */
569static int wm_read_exotic(const char *filepath)
570{
571 /* Make sure we're not trying to read a directory. */
572
573 int filepath_len = strlen(filepath);
574 if (filepath_len > 0 && ELEM(filepath[filepath_len - 1], '/', '\\')) {
576 }
577
578 /* Open the file. */
579 const int filedes = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
580 if (filedes == -1) {
582 }
583
584 FileReader *rawfile = BLI_filereader_new_file(filedes);
585 if (rawfile == nullptr) {
587 }
588
589 /* Read the header (7 bytes are enough to identify all known types). */
590 char header[7];
591 if (rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) {
592 rawfile->close(rawfile);
594 }
595 rawfile->seek(rawfile, 0, SEEK_SET);
596
597 /* Check for uncompressed `.blend`. */
598 if (STREQLEN(header, "BLENDER", 7)) {
599 rawfile->close(rawfile);
601 }
602
603 /* Check for compressed `.blend`. */
604 FileReader *compressed_file = nullptr;
605 if (BLI_file_magic_is_gzip(header)) {
606 /* In earlier versions of Blender (before 3.0), compressed files used `Gzip` instead of `Zstd`.
607 * While these files will no longer be written, there still needs to be reading support. */
608 compressed_file = BLI_filereader_new_gzip(rawfile);
609 }
610 else if (BLI_file_magic_is_zstd(header)) {
611 compressed_file = BLI_filereader_new_zstd(rawfile);
612 }
613
614 /* If a compression signature matches,
615 * try decompressing the start and check if it's a `.blend`. */
616 if (compressed_file != nullptr) {
617 size_t len = compressed_file->read(compressed_file, header, sizeof(header));
618 compressed_file->close(compressed_file);
619 if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) {
621 }
622 }
623 else {
624 rawfile->close(rawfile);
625 }
626
627 /* Add check for future file formats here. */
628
630}
631
633
634/* -------------------------------------------------------------------- */
637
638void WM_file_autoexec_init(const char *filepath)
639{
641 return;
642 }
643
644 if (G.f & G_FLAG_SCRIPT_AUTOEXEC) {
645 char dirpath[FILE_MAX];
646 BLI_path_split_dir_part(filepath, dirpath, sizeof(dirpath));
647 if (BKE_autoexec_match(dirpath)) {
649 }
650 }
651}
652
654{
655 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
656 ReportList *reports = &wm->runtime->reports;
657 bool found = false;
658 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
659 if (scene->r.engine[0] &&
660 BLI_findstring(&R_engines, scene->r.engine, offsetof(RenderEngineType, idname)) == nullptr)
661 {
663 RPT_ERROR,
664 "Engine '%s' not available for scene '%s' (an add-on may need to be installed "
665 "or enabled)",
666 scene->r.engine,
667 scene->id.name + 2);
668 found = true;
669 }
670 }
671
672 if (found) {
673 if (!G.background) {
675 }
676 }
677}
678
685static void wm_file_read_pre(bool use_data, bool /*use_userdef*/)
686{
687 if (use_data) {
689 }
690
691 /* Always do this as both startup and preferences may have loaded in many font's
692 * at a different zoom level to the file being loaded. */
694
696}
697
714
720 const char *filepath,
722{
724
725 const bool use_data = params->use_data;
726 const bool use_userdef = params->use_userdef;
727 const bool is_startup_file = params->is_startup_file;
728 const bool is_factory_startup = params->is_factory_startup;
729 const bool reset_app_template = params->reset_app_template;
730
731 bool addons_loaded = false;
732
733 if (use_data) {
734 if (!G.background) {
735 /* Remove windows which failed to be added via #WM_check. */
737 }
738 CTX_wm_window_set(C, static_cast<wmWindow *>(wm->windows.first));
739 }
740
741#ifdef WITH_PYTHON
742 if (is_startup_file) {
743 /* The following block handles data & preferences being reloaded
744 * which requires resetting some internal variables. */
745 if (!params->is_first_time) {
747 bool reset_all = use_userdef;
748 if (use_userdef || reset_app_template) {
749 /* Only run when we have a template path found. */
751 const char *imports[] = {"bl_app_template_utils", nullptr};
752 BPY_run_string_eval(C, imports, "bl_app_template_utils.reset()");
753 reset_all = true;
754 }
755 }
756 if (reset_all) {
757 const char *imports[] = {"bpy", "addon_utils", nullptr};
759 C,
760 imports,
761 /* Refresh scripts as the preferences may have changed the user-scripts path.
762 *
763 * This is needed when loading settings from the previous version,
764 * otherwise the script path stored in the preferences would be ignored. */
765 "bpy.utils.refresh_script_paths()\n"
766 /* Sync add-ons, these may have changed from the defaults. */
767 "addon_utils.reset_all()");
768 }
769 if (use_data) {
771 }
772 addons_loaded = true;
773 }
774 }
775 else {
776 /* Run any texts that were loaded in and flagged as modules. */
777 if (use_data) {
779 }
780 addons_loaded = true;
781 }
782#else
783 UNUSED_VARS(is_startup_file, reset_app_template);
784#endif /* WITH_PYTHON */
785
786 Main *bmain = CTX_data_main(C);
787
788 if (use_userdef) {
789 if (is_factory_startup) {
791 }
792 }
793
794 if (is_factory_startup && BLT_translate_new_dataname()) {
795 /* Translate workspace names. */
796 LISTBASE_FOREACH_MUTABLE (WorkSpace *, workspace, &bmain->workspaces) {
798 *bmain, workspace->id, CTX_DATA_(BLT_I18NCONTEXT_ID_WORKSPACE, workspace->id.name + 2));
799 }
800 }
801
802 if (use_data) {
803 /* Important to do before nulling the context. */
805
806 /* Load-post must run before evaluating drivers & depsgraph, see: #109720.
807 * On failure, the caller handles #BKE_CB_EVT_LOAD_POST_FAIL. */
808 if (params->success) {
810 }
811
812 if (is_factory_startup) {
814 }
815 }
816
817 if (use_data) {
819
820 /* After load post, so for example the driver namespace can be filled
821 * before evaluating the depsgraph. */
822 if (!G.background || (G.fileflags & G_BACKGROUND_NO_DEPSGRAPH) == 0) {
824 }
825
827
828 /* Add-ons are disabled when loading the startup file, so the Render Layer node in compositor
829 * node trees might be wrong due to missing render engines that are available as add-ons, like
830 * Cycles. So we need to update compositor node trees after reading the file when add-ons are
831 * now loaded. */
832 if (is_startup_file) {
833 FOREACH_NODETREE_BEGIN (bmain, node_tree, owner_id) {
834 if (node_tree->type == NTREE_COMPOSIT) {
836 }
837 }
839 }
840
841#if 1
843 /* Clear static filtered asset tree caches. */
845#else
847#endif
848 }
849
850 /* Report any errors.
851 * Currently disabled if add-ons aren't yet loaded. */
852 if (addons_loaded) {
853 wm_file_read_report(bmain, static_cast<wmWindow *>(wm->windows.first));
854 }
855
856 if (use_data) {
857 if (!G.background) {
858 if (wm->undo_stack == nullptr) {
859 wm->undo_stack = BKE_undosys_stack_create();
860 }
861 else {
862 BKE_undosys_stack_clear(wm->undo_stack);
863 }
864 BKE_undosys_stack_init_from_main(wm->undo_stack, bmain);
866 }
867 }
868
869 if (use_data) {
870 if (!G.background) {
871 /* In background mode this makes it hard to load
872 * a blend file and do anything since the screen
873 * won't be set to a valid value again. */
874 CTX_wm_window_set(C, nullptr); /* Exits queues. */
875
876 /* Ensure auto-run action is not used from a previous blend file load. */
877 wm_test_autorun_revert_action_set(nullptr, nullptr);
878
879 /* Ensure tools are registered. */
881 }
882 }
883}
884
885static void wm_read_callback_pre_wrapper(bContext *C, const char *filepath)
886{
887 /* NOTE: either #BKE_CB_EVT_LOAD_POST or #BKE_CB_EVT_LOAD_POST_FAIL must run.
888 * Runs at the end of this function, don't return beforehand. */
890}
891
892static void wm_read_callback_post_wrapper(bContext *C, const char *filepath, const bool success)
893{
894 Main *bmain = CTX_data_main(C);
895 /* Temporarily set the window context as this was once supported, see: #107759.
896 * If the window is already set, don't change it. */
897 bool has_window = CTX_wm_window(C) != nullptr;
898 if (!has_window) {
899 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
900 wmWindow *win = static_cast<wmWindow *>(wm->windows.first);
901 CTX_wm_window_set(C, win);
902 }
903
904 /* On success: #BKE_CB_EVT_LOAD_POST runs from #wm_file_read_post. */
905 if (success == false) {
907 }
908
909 /* This function should leave the window null when the function entered. */
910 if (!has_window) {
911 CTX_wm_window_set(C, nullptr);
912 }
913}
914
916
917/* -------------------------------------------------------------------- */
920
922{
923 double duration_whole_minutes, duration_whole_seconds;
924 double duration_libraries_minutes, duration_libraries_seconds;
925 double duration_lib_override_minutes, duration_lib_override_seconds;
926 double duration_lib_override_resync_minutes, duration_lib_override_resync_seconds;
927 double duration_lib_override_recursive_resync_minutes,
928 duration_lib_override_recursive_resync_seconds;
929
931 nullptr,
932 nullptr,
933 &duration_whole_minutes,
934 &duration_whole_seconds,
935 nullptr);
937 nullptr,
938 nullptr,
939 &duration_libraries_minutes,
940 &duration_libraries_seconds,
941 nullptr);
943 nullptr,
944 nullptr,
945 &duration_lib_override_minutes,
946 &duration_lib_override_seconds,
947 nullptr);
949 nullptr,
950 nullptr,
951 &duration_lib_override_resync_minutes,
952 &duration_lib_override_resync_seconds,
953 nullptr);
955 nullptr,
956 nullptr,
957 &duration_lib_override_recursive_resync_minutes,
958 &duration_lib_override_recursive_resync_seconds,
959 nullptr);
960
961 CLOG_INFO(
962 &LOG, 0, "Blender file read in %.0fm%.2fs", duration_whole_minutes, duration_whole_seconds);
963 CLOG_INFO(&LOG,
964 0,
965 " * Loading libraries: %.0fm%.2fs",
966 duration_libraries_minutes,
967 duration_libraries_seconds);
968 CLOG_INFO(&LOG,
969 0,
970 " * Applying overrides: %.0fm%.2fs",
971 duration_lib_override_minutes,
972 duration_lib_override_seconds);
973 CLOG_INFO(&LOG,
974 0,
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
1047 const char *filepath,
1048 const bool use_scripts_autoexec_check,
1050{
1051 /* Assume automated tasks with background, don't write recent file list. */
1052 const bool do_history_file_update = (G.background == false) &&
1053 (CTX_wm_manager(C)->op_undo_depth == 0);
1054 bool success = false;
1055
1056 const bool use_data = true;
1057 const bool use_userdef = false;
1058
1059 /* NOTE: a matching #wm_read_callback_post_wrapper must be called. */
1061
1062 Main *bmain = CTX_data_main(C);
1063
1064 /* So we can get the error message. */
1065 errno = 0;
1066
1067 WM_cursor_wait(true);
1068
1069 /* First try to append data from exotic file formats. */
1070 /* It throws error box when file doesn't exist and returns -1. */
1071 /* NOTE(ton): it should set some error message somewhere. */
1072 const int retval = wm_read_exotic(filepath);
1073
1074 /* We didn't succeed, now try to read Blender file. */
1075 if (retval == BKE_READ_EXOTIC_OK_BLEND) {
1077 params.is_startup = false;
1078 /* Loading preferences when the user intended to load a regular file is a security
1079 * risk, because the excluded path list is also loaded. Further it's just confusing
1080 * if a user loads a file and various preferences change. */
1081 params.skip_flags = BLO_READ_SKIP_USERDEF;
1082
1083 BlendFileReadReport bf_reports{};
1084 bf_reports.reports = reports;
1085 bf_reports.duration.whole = BLI_time_now_seconds();
1086 BlendFileData *bfd = BKE_blendfile_read(filepath, &params, &bf_reports);
1087 if (bfd != nullptr) {
1088 wm_file_read_pre(use_data, use_userdef);
1089
1090 /* Close any user-loaded fonts. */
1092
1093 /* Put WM into a stable state for post-readfile processes (kill jobs, removes event handlers,
1094 * message bus, and so on). */
1095 BlendFileReadWMSetupData *wm_setup_data = wm_file_read_setup_wm_init(C, bmain, false);
1096
1097 /* This flag is initialized by the operator but overwritten on read.
1098 * need to re-enable it here else drivers and registered scripts won't work. */
1099 const int G_f_orig = G.f;
1100
1101 /* Frees the current main and replaces it with the new one read from file. */
1103 C, bfd, &params, wm_setup_data, &bf_reports, false, nullptr);
1104 bmain = CTX_data_main(C);
1105
1106 /* Finalize handling of WM, using the read WM and/or the current WM depending on things like
1107 * whether the UI is loaded from the .blend file or not, etc. */
1108 wm_file_read_setup_wm_finalize(C, bmain, wm_setup_data);
1109
1110 if (G.f != G_f_orig) {
1111 const int flags_keep = G_FLAG_ALL_RUNTIME;
1113 G.f = (G.f & ~flags_keep) | (G_f_orig & flags_keep);
1114 }
1115
1116 /* Set by the `use_scripts` property on file load.
1117 * If this was not set, then it should be calculated based on the file-path.
1118 * Note that this uses `bmain->filepath` and not `filepath`, necessary when
1119 * recovering the last session, where the file-path can be #BLENDER_QUIT_FILE. */
1120 if (use_scripts_autoexec_check) {
1122 }
1123
1124 WM_check(C); /* Opens window(s), checks keymaps. */
1125
1126 if (do_history_file_update) {
1128 }
1129
1130 wmFileReadPost_Params read_file_post_params{};
1131 read_file_post_params.use_data = use_data;
1132 read_file_post_params.use_userdef = use_userdef;
1133 read_file_post_params.is_startup_file = false;
1134 read_file_post_params.is_factory_startup = false;
1135 read_file_post_params.reset_app_template = false;
1136 read_file_post_params.success = true;
1137 read_file_post_params.is_alloc = false;
1138 wm_file_read_post(C, filepath, &read_file_post_params);
1139
1140 bf_reports.duration.whole = BLI_time_now_seconds() - bf_reports.duration.whole;
1141 file_read_reports_finalize(&bf_reports);
1142
1143 success = true;
1144 }
1145 }
1146#if 0
1147 else if (retval == BKE_READ_EXOTIC_OK_OTHER) {
1148 BKE_undo_write(C, "Import file");
1149 }
1150#endif
1151 else if (retval == BKE_READ_EXOTIC_FAIL_OPEN) {
1153 RPT_ERROR,
1154 "Cannot read file \"%s\": %s",
1155 filepath,
1156 errno ? strerror(errno) : RPT_("unable to open the file"));
1157 }
1158 else if (retval == BKE_READ_EXOTIC_FAIL_FORMAT) {
1159 BKE_reportf(reports, RPT_ERROR, "File format is not supported in file \"%s\"", filepath);
1160 }
1161 else if (retval == BKE_READ_EXOTIC_FAIL_PATH) {
1162 BKE_reportf(reports, RPT_ERROR, "File path \"%s\" invalid", filepath);
1163 }
1164 else {
1165 BKE_reportf(reports, RPT_ERROR, "Unknown error loading \"%s\"", filepath);
1166 BLI_assert_msg(0, "invalid 'retval'");
1167 }
1168
1169 /* NOTE: even if the file fails to load, keep the file in the "Recent Files" list.
1170 * This is done because failure to load could be caused by the file-system being
1171 * temporarily offline, see: #127825. */
1172
1173 WM_cursor_wait(false);
1174
1175 wm_read_callback_post_wrapper(C, filepath, success);
1176
1178
1179 return success;
1180}
1181
1182static struct {
1184 bool override;
1186
1188{
1189 if (app_template) {
1191 wm_init_state_app_template.override = true;
1192 }
1193 else {
1194 wm_init_state_app_template.app_template[0] = '\0';
1195 wm_init_state_app_template.override = false;
1196 }
1197}
1198
1200{
1201 return wm_init_state_app_template.override ? wm_init_state_app_template.app_template : nullptr;
1202}
1203
1205
1206/* -------------------------------------------------------------------- */
1209
1211 const wmHomeFileRead_Params *params_homefile,
1213 wmFileReadPost_Params **r_params_file_read_post)
1214{
1215 /* NOTE: unlike #WM_file_read, don't set the wait cursor when reading the home-file.
1216 * While technically both are reading a file and could use the wait cursor,
1217 * avoid doing so for the following reasons.
1218 *
1219 * - When loading blend with a file (command line or external file browser)
1220 * the home-file is read before the file being loaded.
1221 * Toggling the wait cursor twice causes the cursor to flicker which looks like a glitch.
1222 * - In practice it's not that useful as users tend not to set scenes with slow loading times
1223 * as their startup.
1224 */
1225
1226/* UNUSED, keep as this may be needed later & the comment below isn't self evident. */
1227#if 0
1228 /* Context does not always have valid main pointer here. */
1229 Main *bmain = G_MAIN;
1230#endif
1231 bool success = false;
1232
1233 /* May be enabled, when the user configuration doesn't exist. */
1234 const bool use_data = params_homefile->use_data;
1235 const bool use_userdef = params_homefile->use_userdef;
1236 bool use_factory_settings = params_homefile->use_factory_settings;
1237 /* Currently this only impacts preferences as it doesn't make much sense to keep the default
1238 * startup open in the case the app-template doesn't happen to define its own startup.
1239 * Unlike preferences where we might want to only reset the app-template part of the preferences
1240 * so as not to reset the preferences for all other Blender instances, see: #96427. */
1241 const bool use_factory_settings_app_template_only =
1243 const bool use_empty_data = params_homefile->use_empty_data;
1244 const char *filepath_startup_override = params_homefile->filepath_startup_override;
1245 const char *app_template_override = params_homefile->app_template_override;
1246
1247 bool filepath_startup_is_factory = true;
1248 char filepath_startup[FILE_MAX];
1249 char filepath_userdef[FILE_MAX];
1250
1251 /* When 'app_template' is set:
1252 * `{BLENDER_USER_CONFIG}/{app_template}`. */
1253 char app_template_system[FILE_MAX];
1254 /* When 'app_template' is set:
1255 * `{BLENDER_SYSTEM_SCRIPTS}/startup/bl_app_templates_system/{app_template}`. */
1256 char app_template_config[FILE_MAX];
1257
1258 eBLOReadSkip skip_flags = eBLOReadSkip(0);
1259
1260 if (use_data == false) {
1261 skip_flags |= BLO_READ_SKIP_DATA;
1262 }
1263 if (use_userdef == false) {
1264 skip_flags |= BLO_READ_SKIP_USERDEF;
1265 }
1266
1267 /* True if we load startup.blend from memory
1268 * or use app-template startup.blend which the user hasn't saved. */
1269 bool is_factory_startup = true;
1270
1271 const char *app_template = nullptr;
1272 bool update_defaults = false;
1273
1274 /* Current Main is not always available in context here. */
1275 Main *bmain = G_MAIN;
1276
1277 if (filepath_startup_override != nullptr) {
1278 /* Pass. */
1279 }
1280 else if (app_template_override) {
1281 /* This may be clearing the current template by setting to an empty string. */
1282 app_template = app_template_override;
1283 }
1284 else if (!use_factory_settings && U.app_template[0]) {
1285 app_template = U.app_template;
1286 }
1287
1288 const bool reset_app_template = ((!app_template && U.app_template[0]) ||
1289 (app_template && !STREQ(app_template, U.app_template)));
1290
1291 /* Options exclude each other. */
1292 BLI_assert((use_factory_settings && filepath_startup_override) == 0);
1293
1294 if ((G.f & G_FLAG_SCRIPT_OVERRIDE_PREF) == 0) {
1296 }
1297
1298 if (use_data) {
1299 if (reset_app_template) {
1300 /* Always load UI when switching to another template. */
1301 G.fileflags &= ~G_FILE_NO_UI;
1302 }
1303 }
1304
1305 if (use_userdef || reset_app_template) {
1306#ifdef WITH_PYTHON
1307 /* This only runs once Blender has already started. */
1308 if (!params_homefile->is_first_time) {
1310 /* This is restored by 'wm_file_read_post', disable before loading any preferences
1311 * so an add-on can read their own preferences when un-registering,
1312 * and use new preferences if/when re-registering, see #67577.
1313 *
1314 * Note that this fits into 'wm_file_read_pre' function but gets messy
1315 * since we need to know if 'reset_app_template' is true. */
1316 const char *imports[] = {"addon_utils", nullptr};
1317 BPY_run_string_eval(C, imports, "addon_utils.disable_all()");
1318 }
1319#endif /* WITH_PYTHON */
1320 }
1321
1322 if (use_data) {
1323 /* NOTE: a matching #wm_read_callback_post_wrapper must be called.
1324 * This runs from #wm_homefile_read_post. */
1326 }
1327
1328 /* For regular file loading this only runs after the file is successfully read.
1329 * In the case of the startup file, the in-memory startup file is used as a fallback
1330 * so we know this will work if all else fails. */
1331 wm_file_read_pre(use_data, use_userdef);
1332
1333 BlendFileReadWMSetupData *wm_setup_data = nullptr;
1334 if (use_data) {
1335 /* Put WM into a stable state for post-readfile processes (kill jobs, removes event handlers,
1336 * message bus, and so on). */
1337 wm_setup_data = wm_file_read_setup_wm_init(C, bmain, true);
1338 }
1339
1340 filepath_startup[0] = '\0';
1341 filepath_userdef[0] = '\0';
1342 app_template_system[0] = '\0';
1343 app_template_config[0] = '\0';
1344
1345 const std::optional<std::string> cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, nullptr);
1346 if (!use_factory_settings) {
1347 if (cfgdir.has_value()) {
1349 filepath_startup, sizeof(filepath_startup), cfgdir->c_str(), BLENDER_STARTUP_FILE);
1350 filepath_startup_is_factory = false;
1351 if (use_userdef) {
1353 filepath_userdef, sizeof(filepath_startup), cfgdir->c_str(), BLENDER_USERPREF_FILE);
1354 }
1355 }
1356 else {
1357 use_factory_settings = true;
1358 }
1359
1360 if (filepath_startup_override) {
1361 STRNCPY(filepath_startup, filepath_startup_override);
1362 filepath_startup_is_factory = false;
1363 }
1364 }
1365
1366 /* Load preferences before `startup.blend`. */
1367 if (use_userdef) {
1368 if (use_factory_settings_app_template_only) {
1369 /* Use the current preferences as-is (only load in the app_template preferences). */
1370 skip_flags |= BLO_READ_SKIP_USERDEF;
1371 }
1372 else if (!use_factory_settings && BLI_exists(filepath_userdef)) {
1373 UserDef *userdef = BKE_blendfile_userdef_read(filepath_userdef, nullptr);
1374 if (userdef != nullptr) {
1375 CLOG_INFO(&LOG, 0, "read prefs: \"%s\"", filepath_userdef);
1376
1378 userdef = nullptr;
1379
1380 skip_flags |= BLO_READ_SKIP_USERDEF;
1381 }
1382 }
1383 }
1384
1385 if ((app_template != nullptr) && (app_template[0] != '\0')) {
1387 app_template, app_template_system, sizeof(app_template_system)))
1388 {
1389 /* Can safely continue with code below, just warn it's not found. */
1390 BKE_reportf(reports, RPT_WARNING, "Application Template \"%s\" not found", app_template);
1391 }
1392
1393 /* Insert template name into startup file. */
1394
1395 /* Note that the path is being set even when `use_factory_settings == true`
1396 * this is done so we can load a templates factory-settings. */
1397 if (!use_factory_settings && cfgdir.has_value()) {
1399 app_template_config, sizeof(app_template_config), cfgdir->c_str(), app_template);
1401 filepath_startup, sizeof(filepath_startup), app_template_config, BLENDER_STARTUP_FILE);
1402 filepath_startup_is_factory = false;
1403 if (BLI_access(filepath_startup, R_OK) != 0) {
1404 filepath_startup[0] = '\0';
1405 }
1406 }
1407 else {
1408 filepath_startup[0] = '\0';
1409 }
1410
1411 if (filepath_startup[0] == '\0') {
1413 filepath_startup, sizeof(filepath_startup), app_template_system, BLENDER_STARTUP_FILE);
1414 filepath_startup_is_factory = true;
1415
1416 /* Update defaults only for system templates. */
1417 update_defaults = true;
1418 }
1419 }
1420
1421 if (!use_factory_settings || (filepath_startup[0] != '\0')) {
1422 if (BLI_access(filepath_startup, R_OK) == 0) {
1424 params.is_startup = true;
1425 params.is_factory_settings = use_factory_settings;
1426 params.skip_flags = skip_flags | BLO_READ_SKIP_USERDEF;
1427 BlendFileReadReport bf_reports{};
1428 bf_reports.reports = reports;
1429 BlendFileData *bfd = BKE_blendfile_read(filepath_startup, &params, &bf_reports);
1430
1431 if (bfd != nullptr) {
1432 CLOG_INFO(&LOG, 0, "read startup: \"%s\"", filepath_startup);
1433
1434 /* Frees the current main and replaces it with the new one read from file. */
1436 bfd,
1437 &params,
1438 wm_setup_data,
1439 &bf_reports,
1440 update_defaults && use_data,
1441 app_template);
1442 success = true;
1443 bmain = CTX_data_main(C);
1444 }
1445 }
1446 if (success) {
1447 is_factory_startup = filepath_startup_is_factory;
1448 }
1449 }
1450
1451 if (use_userdef) {
1452 if ((skip_flags & BLO_READ_SKIP_USERDEF) == 0) {
1453 UserDef *userdef_default = BKE_blendfile_userdef_from_defaults();
1455 skip_flags |= BLO_READ_SKIP_USERDEF;
1456 }
1457 }
1458
1459 if (success == false && filepath_startup_override && reports) {
1460 /* We can not return from here because wm is already reset. */
1461 BKE_reportf(reports, RPT_ERROR, "Could not read \"%s\"", filepath_startup_override);
1462 }
1463
1464 bool loaded_factory_settings = false;
1465 if (success == false) {
1466 BlendFileReadParams read_file_params{};
1467 read_file_params.is_startup = true;
1468 read_file_params.is_factory_settings = use_factory_settings;
1469 read_file_params.skip_flags = skip_flags;
1471 datatoc_startup_blend, datatoc_startup_blend_size, &read_file_params, nullptr);
1472 if (bfd != nullptr) {
1473 BlendFileReadReport read_report{};
1474 /* Frees the current main and replaces it with the new one read from file. */
1476 C, bfd, &read_file_params, wm_setup_data, &read_report, true, nullptr);
1477 success = true;
1478 loaded_factory_settings = true;
1479 bmain = CTX_data_main(C);
1480 }
1481 }
1482
1483 if (use_empty_data) {
1485 }
1486
1487 /* Load template preferences,
1488 * unlike regular preferences we only use some of the settings,
1489 * see: #BKE_blender_userdef_set_app_template. */
1490 if (app_template_system[0] != '\0') {
1491 char temp_path[FILE_MAX];
1492 temp_path[0] = '\0';
1493 if (!use_factory_settings) {
1494 BLI_path_join(temp_path, sizeof(temp_path), app_template_config, BLENDER_USERPREF_FILE);
1495 if (BLI_access(temp_path, R_OK) != 0) {
1496 temp_path[0] = '\0';
1497 }
1498 }
1499
1500 if (temp_path[0] == '\0') {
1501 BLI_path_join(temp_path, sizeof(temp_path), app_template_system, BLENDER_USERPREF_FILE);
1502 }
1503
1504 if (use_userdef) {
1505 UserDef *userdef_template = nullptr;
1506 /* Just avoids missing file warning. */
1507 if (BLI_exists(temp_path)) {
1508 userdef_template = BKE_blendfile_userdef_read(temp_path, nullptr);
1509 if (userdef_template) {
1510 CLOG_INFO(&LOG, 0, "read prefs from app-template: \"%s\"", temp_path);
1511 }
1512 }
1513 if (userdef_template == nullptr) {
1514 /* We need to have preferences load to overwrite preferences from previous template. */
1515 userdef_template = BKE_blendfile_userdef_from_defaults();
1516 }
1517 if (userdef_template) {
1519 userdef_template = nullptr;
1520 }
1521 }
1522 }
1523
1524 if (app_template_override) {
1525 STRNCPY(U.app_template, app_template_override);
1526 }
1527
1528 if (use_userdef) {
1529 /* Check userdef before open window, keymaps etc. */
1530 wm_init_userdef(bmain);
1531 }
1532
1533 if (use_data) {
1534 /* Finalize handling of WM, using the read WM and/or the current WM depending on things like
1535 * whether the UI is loaded from the .blend file or not, etc. */
1536 wm_setup_data->is_factory_startup = loaded_factory_settings;
1537 wm_file_read_setup_wm_finalize(C, bmain, wm_setup_data);
1538 }
1539
1540 if (use_userdef) {
1541 /* Clear keymaps because the current default keymap may have been initialized
1542 * from user preferences, which have been reset. */
1543 LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
1544 if (wm->defaultconf) {
1545 wm->defaultconf->flag &= ~KEYCONF_INIT_DEFAULT;
1546 }
1547 }
1548 }
1549
1550 if (use_data) {
1551 WM_check(C); /* Opens window(s), checks keymaps. */
1552
1553 bmain->filepath[0] = '\0';
1554 }
1555
1556 {
1557 wmFileReadPost_Params params_file_read_post{};
1558 params_file_read_post.use_data = use_data;
1559 params_file_read_post.use_userdef = use_userdef;
1560 params_file_read_post.is_startup_file = true;
1561 params_file_read_post.is_factory_startup = is_factory_startup;
1562 params_file_read_post.is_first_time = params_homefile->is_first_time;
1563
1564 params_file_read_post.reset_app_template = reset_app_template;
1565
1566 params_file_read_post.success = success;
1567 params_file_read_post.is_alloc = false;
1568
1569 if (r_params_file_read_post == nullptr) {
1570 wm_homefile_read_post(C, &params_file_read_post);
1571 }
1572 else {
1573 params_file_read_post.is_alloc = true;
1574 *r_params_file_read_post = MEM_mallocN<wmFileReadPost_Params>(__func__);
1575 **r_params_file_read_post = params_file_read_post;
1576
1577 /* Match #wm_file_read_post which leaves the window cleared too. */
1578 CTX_wm_window_set(C, nullptr);
1579 }
1580 }
1581}
1582
1584 const wmHomeFileRead_Params *params_homefile,
1586{
1587 wm_homefile_read_ex(C, params_homefile, reports, nullptr);
1588}
1589
1590void wm_homefile_read_post(bContext *C, const wmFileReadPost_Params *params_file_read_post)
1591{
1592 const char *filepath = "";
1593 wm_file_read_post(C, filepath, params_file_read_post);
1594
1595 if (params_file_read_post->use_data) {
1596 wm_read_callback_post_wrapper(C, filepath, params_file_read_post->success);
1597 }
1598
1599 if (params_file_read_post->is_alloc) {
1600 MEM_freeN(params_file_read_post);
1601 }
1602}
1603
1605
1606/* -------------------------------------------------------------------- */
1609
1611{
1612 const std::optional<std::string> cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, nullptr);
1613 if (!cfgdir.has_value()) {
1614 return;
1615 }
1616
1617 char filepath[FILE_MAX];
1618 LinkNode *l;
1619 int num;
1620
1621 BLI_path_join(filepath, sizeof(filepath), cfgdir->c_str(), BLENDER_HISTORY_FILE);
1622
1623 LinkNode *lines = BLI_file_read_as_lines(filepath);
1624
1626
1627 /* Read list of recent opened files from #BLENDER_HISTORY_FILE to memory. */
1628 for (l = lines, num = 0; l && (num < U.recent_files); l = l->next) {
1629 const char *line = static_cast<const char *>(l->link);
1630 /* Don't check if files exist, causes slow startup for remote/external drives. */
1631 if (line[0]) {
1632 RecentFile *recent = MEM_mallocN<RecentFile>("RecentFile");
1633 BLI_addtail(&(G.recent_files), recent);
1634 recent->filepath = BLI_strdup(line);
1635 num++;
1636 }
1637 }
1638
1639 BLI_file_free_lines(lines);
1640}
1641
1642static RecentFile *wm_history_file_new(const char *filepath)
1643{
1644 RecentFile *recent = MEM_mallocN<RecentFile>("RecentFile");
1645 recent->filepath = BLI_strdup(filepath);
1646 return recent;
1647}
1648
1650{
1651 BLI_assert(BLI_findindex(&G.recent_files, recent) != -1);
1652 MEM_freeN(recent->filepath);
1653 BLI_freelinkN(&G.recent_files, recent);
1654}
1655
1657{
1658 LISTBASE_FOREACH_MUTABLE (RecentFile *, recent, &G.recent_files) {
1659 wm_history_file_free(recent);
1660 }
1661}
1662
1663static RecentFile *wm_file_history_find(const char *filepath)
1664{
1665 return static_cast<RecentFile *>(
1666 BLI_findstring_ptr(&G.recent_files, filepath, offsetof(RecentFile, filepath)));
1667}
1668
1674{
1675 char filepath[FILE_MAX];
1676 FILE *fp;
1677
1678 /* Will be nullptr in background mode. */
1679 const std::optional<std::string> user_config_dir = BKE_appdir_folder_id_create(
1680 BLENDER_USER_CONFIG, nullptr);
1681 if (!user_config_dir.has_value()) {
1682 return;
1683 }
1684
1685 BLI_path_join(filepath, sizeof(filepath), user_config_dir->c_str(), BLENDER_HISTORY_FILE);
1686
1687 fp = BLI_fopen(filepath, "w");
1688 if (fp) {
1689 LISTBASE_FOREACH (RecentFile *, recent, &G.recent_files) {
1690 fprintf(fp, "%s\n", recent->filepath);
1691 }
1692 fclose(fp);
1693 }
1694}
1695
1700{
1701 RecentFile *recent;
1702 const char *blendfile_path = BKE_main_blendfile_path_from_global();
1703
1704 /* No write history for recovered startup files. */
1705 if (blendfile_path[0] == '\0') {
1706 return;
1707 }
1708
1709 recent = static_cast<RecentFile *>(G.recent_files.first);
1710 /* Refresh #BLENDER_HISTORY_FILE of recent opened files, when current file was changed. */
1711 if (!(recent) || (BLI_path_cmp(recent->filepath, blendfile_path) != 0)) {
1712
1713 recent = wm_file_history_find(blendfile_path);
1714 if (recent) {
1715 BLI_remlink(&G.recent_files, recent);
1716 }
1717 else {
1718 RecentFile *recent_next;
1719 for (recent = static_cast<RecentFile *>(BLI_findlink(&G.recent_files, U.recent_files - 1));
1720 recent;
1721 recent = recent_next)
1722 {
1723 recent_next = recent->next;
1724 wm_history_file_free(recent);
1725 }
1726 recent = wm_history_file_new(blendfile_path);
1727 }
1728
1729 /* Add current file to the beginning of list. */
1730 BLI_addhead(&(G.recent_files), recent);
1731
1732 /* Write current file to #BLENDER_HISTORY_FILE. */
1734
1735 /* Also update most recent files on system. */
1736 GHOST_addToSystemRecentFiles(blendfile_path);
1737 }
1738}
1739
1741
1742/* -------------------------------------------------------------------- */
1762
1763#ifdef USE_THUMBNAIL_FAST_DOWNSCALE
1764static uint8_t *blend_file_thumb_fast_downscale(const uint8_t *src_rect,
1765 const int src_size[2],
1766 const int dst_size[2])
1767{
1768 /* NOTE: this is a faster alternative to #IMBScaleFilter::Box which is
1769 * especially slow in debug builds, normally debug performance isn't a
1770 * consideration however it's slow enough to get in the way of development.
1771 * In release builds this gives ~1.4x speedup. */
1772
1773 /* Scaling using a box-filter where each box uses an integer-rounded region.
1774 * Accept a slightly lower quality scale as this is only for thumbnails.
1775 * In practice the result is visually indistinguishable.
1776 *
1777 * Technically the color accumulation *could* overflow (creating some invalid pixels),
1778 * however this would require the source image to be larger than
1779 * 65,535 pixels squared (when scaling down to 256x256).
1780 * As the source input is a screenshot or a small camera render created for the thumbnail,
1781 * this isn't a concern. */
1782
1783 BLI_assert(dst_size[0] <= src_size[0] && dst_size[1] <= src_size[1]);
1784 uint8_t *dst_rect = MEM_malloc_arrayN<uint8_t>(size_t(4 * dst_size[0] * dst_size[1]), __func__);
1785
1786 /* A row, the width of the destination to accumulate pixel values into
1787 * before writing into the image. */
1788 uint32_t *accum_row = MEM_calloc_arrayN<uint32_t>(size_t(dst_size[0] * 4), __func__);
1789
1790# ifndef NDEBUG
1791 /* Assert that samples are calculated correctly. */
1792 uint64_t sample_count_all = 0;
1793# endif
1794
1795 const uint32_t src_size_x = src_size[0];
1796 const uint32_t src_size_y = src_size[1];
1797
1798 const uint32_t dst_size_x = dst_size[0];
1799 const uint32_t dst_size_y = dst_size[1];
1800 const uint8_t *src_px = src_rect;
1801
1802 uint32_t src_y = 0;
1803 for (uint32_t dst_y = 0; dst_y < dst_size_y; dst_y++) {
1804 const uint32_t src_y_beg = src_y;
1805 const uint32_t src_y_end = ((dst_y + 1) * src_size_y) / dst_size_y;
1806 for (; src_y < src_y_end; src_y++) {
1807 uint32_t *accum = accum_row;
1808 uint32_t src_x = 0;
1809 for (uint32_t dst_x = 0; dst_x < dst_size_x; dst_x++, accum += 4) {
1810 const uint32_t src_x_end = ((dst_x + 1) * src_size_x) / dst_size_x;
1811 for (; src_x < src_x_end; src_x++) {
1812 accum[0] += uint32_t(src_px[0]);
1813 accum[1] += uint32_t(src_px[1]);
1814 accum[2] += uint32_t(src_px[2]);
1815 accum[3] += uint32_t(src_px[3]);
1816 src_px += 4;
1817 }
1818 BLI_assert(src_x == src_x_end);
1819 }
1820 BLI_assert(accum == accum_row + (4 * dst_size[0]));
1821 }
1822
1823 uint32_t *accum = accum_row;
1824 uint8_t *dst_px = dst_rect + ((dst_y * dst_size_x) * 4);
1825 uint32_t src_x_beg = 0;
1826 const uint32_t span_y = src_y_end - src_y_beg;
1827 for (uint32_t dst_x = 0; dst_x < dst_size_x; dst_x++) {
1828 const uint32_t src_x_end = ((dst_x + 1) * src_size_x) / dst_size_x;
1829 const uint32_t span_x = src_x_end - src_x_beg;
1830
1831 const uint32_t sample_count = span_x * span_y;
1832 dst_px[0] = uint8_t(accum[0] / sample_count);
1833 dst_px[1] = uint8_t(accum[1] / sample_count);
1834 dst_px[2] = uint8_t(accum[2] / sample_count);
1835 dst_px[3] = uint8_t(accum[3] / sample_count);
1836 accum[0] = accum[1] = accum[2] = accum[3] = 0;
1837 accum += 4;
1838 dst_px += 4;
1839
1840 src_x_beg = src_x_end;
1841# ifndef NDEBUG
1842 sample_count_all += sample_count;
1843# endif
1844 }
1845 }
1846 BLI_assert(src_px == src_rect + (sizeof(uint8_t[4]) * src_size[0] * src_size[1]));
1847 BLI_assert(sample_count_all == size_t(src_size[0]) * size_t(src_size[1]));
1848
1849 MEM_freeN(accum_row);
1850 return dst_rect;
1851}
1852#endif /* USE_THUMBNAIL_FAST_DOWNSCALE */
1853
1854static blender::int2 blend_file_thumb_clamp_size(const int size[2], const int limit)
1855{
1857 if (size[0] > size[1]) {
1858 result.x = limit;
1859 result.y = max_ii(1, int((float(size[1]) / float(size[0])) * limit));
1860 }
1861 else {
1862 result.x = max_ii(1, int((float(size[0]) / float(size[1])) * limit));
1863 result.y = limit;
1864 }
1865 return result;
1866}
1867
1872{
1873 *r_thumb = nullptr;
1874
1875 wmWindow *win = CTX_wm_window(C);
1876 if (G.background || (win == nullptr)) {
1877 return nullptr;
1878 }
1879
1880 /* The window to capture should be a main window (without parent). */
1881 while (win->parent) {
1882 win = win->parent;
1883 }
1884
1886 int win_size[2];
1887 /* NOTE: always read from front-buffer as drawing a window can cause problems while saving,
1888 * even if this means the thumbnail from the screen-shot fails to be created, see: #98462. */
1889 ImBuf *ibuf = nullptr;
1890
1891 if (uint8_t *buffer = WM_window_pixels_read_from_frontbuffer(wm, win, win_size)) {
1892 const blender::int2 thumb_size_2x = blend_file_thumb_clamp_size(win_size, BLEN_THUMB_SIZE * 2);
1893 const blender::int2 thumb_size = blend_file_thumb_clamp_size(win_size, BLEN_THUMB_SIZE);
1894
1895#ifdef USE_THUMBNAIL_FAST_DOWNSCALE
1896 if ((thumb_size_2x[0] <= win_size[0]) && (thumb_size_2x[1] <= win_size[1])) {
1897 uint8_t *rect_2x = blend_file_thumb_fast_downscale(buffer, win_size, thumb_size_2x);
1898 uint8_t *rect = blend_file_thumb_fast_downscale(rect_2x, thumb_size_2x, thumb_size);
1899
1900 MEM_freeN(buffer);
1901 ibuf = IMB_allocFromBufferOwn(rect_2x, nullptr, thumb_size_2x.x, thumb_size_2x.y, 24);
1902
1903 BlendThumbnail *thumb = BKE_main_thumbnail_from_buffer(nullptr, rect, thumb_size);
1904 MEM_freeN(rect);
1905 *r_thumb = thumb;
1906 }
1907 else
1908#endif /* USE_THUMBNAIL_FAST_DOWNSCALE */
1909 {
1910 ibuf = IMB_allocFromBufferOwn(buffer, nullptr, win_size[0], win_size[1], 24);
1911 BLI_assert(ibuf != nullptr); /* Never expected to fail. */
1912
1913 /* File-system thumbnail image can be 256x256. */
1914 IMB_scale(ibuf, thumb_size_2x.x, thumb_size_2x.y, IMBScaleFilter::Box, false);
1915
1916 /* Thumbnail inside blend should be 128x128. */
1917 ImBuf *thumb_ibuf = IMB_dupImBuf(ibuf);
1918 IMB_scale(thumb_ibuf, thumb_size.x, thumb_size.y, IMBScaleFilter::Box, false);
1919
1920 BlendThumbnail *thumb = BKE_main_thumbnail_from_imbuf(nullptr, thumb_ibuf);
1921 IMB_freeImBuf(thumb_ibuf);
1922 *r_thumb = thumb;
1923 }
1924 }
1925
1926 if (ibuf) {
1927 /* Save metadata for quick access. */
1928 char version_str[10];
1929 SNPRINTF(version_str, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100);
1931 IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_str);
1932 }
1933
1934 /* Must be freed by caller. */
1935 return ibuf;
1936}
1937
1944 Scene *scene,
1945 bScreen *screen,
1946 BlendThumbnail **r_thumb)
1947{
1948 *r_thumb = nullptr;
1949
1950 /* Scene can be nullptr if running a script at startup and calling the save operator. */
1951 if (G.background || scene == nullptr) {
1952 return nullptr;
1953 }
1954
1955 /* Will be scaled down, but gives some nice oversampling. */
1956 ImBuf *ibuf;
1957 BlendThumbnail *thumb;
1959 const float pixelsize_old = U.pixelsize;
1960 wmWindow *windrawable_old = wm->windrawable;
1961 char err_out[256] = "unknown";
1962
1963 /* Screen if no camera found. */
1964 ScrArea *area = nullptr;
1965 ARegion *region = nullptr;
1966 View3D *v3d = nullptr;
1967
1968 if (screen != nullptr) {
1969 area = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0);
1970 if (area) {
1971 v3d = static_cast<View3D *>(area->spacedata.first);
1973 }
1974 }
1975
1976 if (scene->camera == nullptr && v3d == nullptr) {
1977 return nullptr;
1978 }
1979
1981
1982 /* Note that with scaling, this ends up being 0.5,
1983 * as it's a thumbnail, we don't need object centers and friends to be 1:1 size. */
1984 U.pixelsize = 1.0f;
1985
1986 if (scene->camera) {
1988 scene,
1989 (v3d) ? &v3d->shading : nullptr,
1990 (v3d) ? eDrawType(v3d->shading.type) : OB_SOLID,
1991 scene->camera,
1998 nullptr,
1999 nullptr,
2000 nullptr,
2001 err_out);
2002 }
2003 else {
2005 scene,
2006 OB_SOLID,
2007 v3d,
2008 region,
2013 nullptr,
2014 true,
2015 nullptr,
2016 nullptr,
2017 err_out);
2018 }
2019
2020 U.pixelsize = pixelsize_old;
2021
2022 /* Reset to old drawable. */
2023 if (windrawable_old) {
2024 wm_window_make_drawable(wm, windrawable_old);
2025 }
2026 else {
2028 }
2029
2030 if (ibuf) {
2031 /* Dirty oversampling. */
2032 ImBuf *thumb_ibuf;
2033 thumb_ibuf = IMB_dupImBuf(ibuf);
2034
2035 /* Save metadata for quick access. */
2036 char version_str[10];
2037 SNPRINTF(version_str, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100);
2039 IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_str);
2040
2041 /* BLEN_THUMB_SIZE is size of thumbnail inside blend file: 128x128. */
2043 thumb = BKE_main_thumbnail_from_imbuf(nullptr, thumb_ibuf);
2044 IMB_freeImBuf(thumb_ibuf);
2045 /* Thumbnail saved to file-system should be 256x256. */
2046 IMB_scale(ibuf,
2050 false);
2051 }
2052 else {
2053 /* '*r_thumb' needs to stay nullptr to prevent a bad thumbnail from being handled. */
2054 CLOG_WARN(&LOG, "failed to create thumbnail: %s", err_out);
2055 thumb = nullptr;
2056 }
2057
2058 /* Must be freed by caller. */
2059 *r_thumb = thumb;
2060
2061 return ibuf;
2062}
2063
2065
2066/* -------------------------------------------------------------------- */
2069
2071{
2072 char filepath[FILE_MAX];
2073
2075 BLI_path_extension_replace(filepath, sizeof(filepath), "_crash.blend");
2077 const bool success = BLO_write_file(G_MAIN, filepath, G.fileflags, &params, nullptr);
2078 printf("%s: \"%s\"\n", success ? "written" : "failed", filepath);
2079 return success;
2080}
2081
2087 const char *filepath,
2089{
2090 const int filepath_len = strlen(filepath);
2091 if (filepath_len == 0) {
2092 BKE_report(reports, RPT_ERROR, "Path is empty, cannot save");
2093 return false;
2094 }
2095
2096 if (filepath_len >= FILE_MAX) {
2097 BKE_report(reports, RPT_ERROR, "Path too long, cannot save");
2098 return false;
2099 }
2100
2101 if (bmain->is_asset_edit_file &&
2103 {
2104 BKE_report(reports, RPT_ERROR, "Cannot overwrite files that are managed by the asset system");
2105 return false;
2106 }
2107
2108 LISTBASE_FOREACH (Library *, li, &bmain->libraries) {
2109 if (BLI_path_cmp(li->runtime->filepath_abs, filepath) == 0) {
2110 BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath);
2111 return false;
2112 }
2113 }
2114
2115 return true;
2116}
2117
2122 const char *filepath,
2123 int fileflags,
2124 eBLO_WritePathRemap remap_mode,
2125 bool use_save_as_copy,
2127{
2128 Main *bmain = CTX_data_main(C);
2129 BlendThumbnail *thumb = nullptr, *main_thumb = nullptr;
2130 ImBuf *ibuf_thumb = nullptr;
2131
2132 /* NOTE: used to replace the file extension (to ensure `.blend`),
2133 * no need to now because the operator ensures,
2134 * its handy for scripts to save to a predefined name without blender editing it. */
2135
2136 if (!wm_file_write_check_with_report_on_failure(bmain, filepath, reports)) {
2137 return false;
2138 }
2139
2140 /* Call pre-save callbacks before writing preview,
2141 * that way you can generate custom file thumbnail. */
2142
2143 /* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
2144 * Runs at the end of this function, don't return beforehand. */
2146
2147 /* Check if file write permission is OK. */
2148 if (const int st_mode = BLI_exists(filepath)) {
2149 bool ok = true;
2150
2151 if (!BLI_file_is_writable(filepath)) {
2153 reports, RPT_ERROR, "Cannot save blend file, path \"%s\" is not writable", filepath);
2154 ok = false;
2155 }
2156 else if (S_ISDIR(st_mode)) {
2157 /* While the UI mostly prevents this, it's possible to save to an existing
2158 * directory from Python because the path is used unmodified.
2159 * Besides it being unlikely the user wants to replace a directory,
2160 * the file versioning logic (to create `*.blend1` files)
2161 * would rename the directory with a `1` suffix, see #134101. */
2163 reports, RPT_ERROR, "Cannot save blend file, path \"%s\" is a directory", filepath);
2164 ok = false;
2165 }
2166
2167 if (!ok) {
2169 return false;
2170 }
2171 }
2172
2174
2175 /* Enforce full override check/generation on file save. */
2177
2178 /* NOTE: Ideally we would call `WM_redraw_windows` here to remove any open menus.
2179 * But we can crash if saving from a script, see #92704 & #97627.
2180 * Just checking `!G.background && BLI_thread_is_main()` is not sufficient to fix this.
2181 * Additionally some EGL configurations don't support reading the front-buffer
2182 * immediately after drawing, see: #98462. In that case off-screen drawing is necessary. */
2183
2184 /* Don't forget not to return without! */
2185 WM_cursor_wait(true);
2186
2187 if (U.file_preview_type != USER_FILE_PREVIEW_NONE) {
2188 /* Blend file thumbnail.
2189 *
2190 * - Save before exiting edit-mode, otherwise evaluated-mesh for shared data gets corrupted.
2191 * See #27765.
2192 * - Main can store a `.blend` thumbnail,
2193 * useful for background-mode or thumbnail customization.
2194 */
2195 main_thumb = thumb = bmain->blen_thumb;
2196 if (thumb != nullptr) {
2197 /* In case we are given a valid thumbnail data, just generate image from it. */
2198 ibuf_thumb = BKE_main_thumbnail_to_imbuf(nullptr, thumb);
2199 }
2200 else if (BLI_thread_is_main()) {
2201 int file_preview_type = U.file_preview_type;
2202
2203 if (file_preview_type == USER_FILE_PREVIEW_AUTO) {
2204 Scene *scene = CTX_data_scene(C);
2205 bScreen *screen = CTX_wm_screen(C);
2206 bool do_render = (scene != nullptr && scene->camera != nullptr && screen != nullptr &&
2207 (BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0) != nullptr));
2208 file_preview_type = do_render ? USER_FILE_PREVIEW_CAMERA : USER_FILE_PREVIEW_SCREENSHOT;
2209 }
2210
2211 switch (file_preview_type) {
2213 ibuf_thumb = blend_file_thumb_from_screenshot(C, &thumb);
2214 break;
2215 }
2217 ibuf_thumb = blend_file_thumb_from_camera(
2218 C, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
2219 break;
2220 }
2221 default:
2223 }
2224 }
2225 }
2226
2227 /* Operator now handles overwrite checks. */
2228
2229 if (G.fileflags & G_FILE_AUTOPACK) {
2230 BKE_packedfile_pack_all(bmain, reports, false);
2231 }
2232
2234
2235 /* XXX(ton): temp solution to solve bug, real fix coming. */
2236 bmain->recovered = false;
2237
2238 BlendFileWriteParams blend_write_params{};
2239 blend_write_params.remap_mode = remap_mode;
2240 blend_write_params.use_save_versions = true;
2241 blend_write_params.use_save_as_copy = use_save_as_copy;
2242 blend_write_params.thumb = thumb;
2243
2244 const bool success = BLO_write_file(bmain, filepath, fileflags, &blend_write_params, reports);
2245
2246 if (success) {
2247 const bool do_history_file_update = (G.background == false) &&
2248 (CTX_wm_manager(C)->op_undo_depth == 0);
2249
2250 if (use_save_as_copy == false) {
2251 STRNCPY(bmain->filepath, filepath); /* Is guaranteed current file. */
2252 }
2253
2254 SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
2255
2256 /* Prevent background mode scripts from clobbering history. */
2257 if (do_history_file_update) {
2259 }
2260
2261 /* Run this function after because the file can't be written before the blend is. */
2262 if (ibuf_thumb) {
2263 IMB_thumb_delete(filepath, THB_FAIL); /* Without this a failed thumb overrides. */
2264 ibuf_thumb = IMB_thumb_create(filepath, THB_LARGE, THB_SOURCE_BLEND, ibuf_thumb);
2265 }
2266 }
2267
2269 bmain, success ? BKE_CB_EVT_SAVE_POST : BKE_CB_EVT_SAVE_POST_FAIL, filepath);
2270
2271 if (ibuf_thumb) {
2272 IMB_freeImBuf(ibuf_thumb);
2273 }
2274 if (thumb && thumb != main_thumb) {
2275 MEM_freeN(thumb);
2276 }
2277
2278 WM_cursor_wait(false);
2279
2280 return success;
2281}
2282
2284
2285/* -------------------------------------------------------------------- */
2288
2289static void wm_autosave_location(char filepath[FILE_MAX])
2290{
2291 const int pid = abs(getpid());
2292 char filename[1024];
2293
2294 /* Normally there is no need to check for this to be nullptr,
2295 * however this runs on exit when it may be cleared. */
2296 Main *bmain = G_MAIN;
2297 const char *blendfile_path = bmain ? BKE_main_blendfile_path(bmain) : nullptr;
2298
2299 if (blendfile_path && (blendfile_path[0] != '\0')) {
2300 const char *basename = BLI_path_basename(blendfile_path);
2301 int len = strlen(basename) - 6;
2302 SNPRINTF(filename, "%.*s_%d_autosave.blend", len, basename, pid);
2303 }
2304 else {
2305 SNPRINTF(filename, "%d_autosave.blend", pid);
2306 }
2307
2308 const char *tempdir_base = BKE_tempdir_base();
2309 BLI_path_join(filepath, FILE_MAX, tempdir_base, filename);
2310}
2311
2313{
2314 char filepath[FILE_MAX];
2315
2316 wm_autosave_location(filepath);
2317
2318 /* Technically, we could always just save here, but that would cause performance regressions
2319 * compared to when the #MemFile undo step was used for saving undo-steps. So for now just skip
2320 * auto-save when we are in a mode where auto-save wouldn't have worked previously anyway. This
2321 * check can be removed once the performance regressions have been solved. */
2322 if (ED_undosys_stack_memfile_get_if_active(wm->undo_stack) != nullptr) {
2323 WM_autosave_write(wm, bmain);
2324 return true;
2325 }
2326 if ((U.uiflag & USER_GLOBALUNDO) == 0) {
2327 WM_autosave_write(wm, bmain);
2328 return true;
2329 }
2330 /* Can't auto-save with MemFile right now, try again later. */
2331 return false;
2332}
2333
2335{
2336 return wm->autosave_scheduled;
2337}
2338
2340{
2342
2343 char filepath[FILE_MAX];
2344 wm_autosave_location(filepath);
2345 /* Save as regular blend file with recovery information and always compress them, see: !132685.
2346 */
2347 const int fileflags = G.fileflags | G_FILE_RECOVER_WRITE | G_FILE_COMPRESS;
2348
2349 /* Error reporting into console. */
2351 BLO_write_file(bmain, filepath, fileflags, &params, nullptr);
2352
2353 /* Restart auto-save timer. */
2356
2357 wm->autosave_scheduled = false;
2358}
2359
2360static void wm_autosave_timer_begin_ex(wmWindowManager *wm, double timestep)
2361{
2363
2364 if (U.flag & USER_AUTOSAVE) {
2365 wm->autosavetimer = WM_event_timer_add(wm, nullptr, TIMERAUTOSAVE, timestep);
2366 }
2367}
2368
2373
2375{
2376 if (wm->autosavetimer) {
2377 WM_event_timer_remove(wm, nullptr, wm->autosavetimer);
2378 wm->autosavetimer = nullptr;
2379 }
2380}
2381
2386
2388{
2390
2391 /* If a modal operator is running, don't autosave because we might not be in
2392 * a valid state to save. But try again in 10ms. */
2393 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
2394 LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
2395 if (handler_base->type == WM_HANDLER_TYPE_OP) {
2396 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
2397 if (handler->op) {
2399 return;
2400 }
2401 }
2402 }
2403 }
2404
2405 wm->autosave_scheduled = false;
2406 if (!wm_autosave_write_try(bmain, wm)) {
2407 wm->autosave_scheduled = true;
2408 }
2409 /* Restart the timer after file write, just in case file write takes a long time. */
2411}
2412
2414{
2415 char filepath[FILE_MAX];
2416
2417 wm_autosave_location(filepath);
2418
2419 if (BLI_exists(filepath)) {
2420 char filepath_quit[FILE_MAX];
2421 BLI_path_join(filepath_quit, sizeof(filepath_quit), BKE_tempdir_base(), BLENDER_QUIT_FILE);
2422
2423 /* For global undo; remove temporarily saved file, otherwise rename. */
2424 if (U.uiflag & USER_GLOBALUNDO) {
2425 BLI_delete(filepath, false, false);
2426 }
2427 else {
2428 BLI_rename_overwrite(filepath, filepath_quit);
2429 }
2430 }
2431}
2432
2434
2435/* -------------------------------------------------------------------- */
2438
2441{
2442 PropertyRNA *prop;
2443
2444 /* So it's possible to reset app-template settings without resetting other defaults. */
2445 prop = RNA_def_boolean(ot->srna,
2446 "use_factory_startup_app_template_only",
2447 false,
2448 "Factory Startup App-Template Only",
2449 "");
2451}
2452
2454
2455/* -------------------------------------------------------------------- */
2462
2463void wm_open_init_load_ui(wmOperator *op, bool use_prefs)
2464{
2465 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "load_ui");
2466 if (!RNA_property_is_set(op->ptr, prop)) {
2467 bool value = use_prefs ? ((U.flag & USER_FILENOUI) == 0) : ((G.fileflags & G_FILE_NO_UI) == 0);
2468
2469 RNA_property_boolean_set(op->ptr, prop, value);
2470 }
2471}
2472
2473bool wm_open_init_use_scripts(wmOperator *op, bool use_prefs)
2474{
2475 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
2476 bool use_scripts_autoexec_check = false;
2477 if (!RNA_property_is_set(op->ptr, prop)) {
2478 /* Use #G_FLAG_SCRIPT_AUTOEXEC rather than the userpref because this means if
2479 * the flag has been disabled from the command line, then opening
2480 * from the menu won't enable this setting. */
2481 bool value = use_prefs ? ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) :
2482 ((G.f & G_FLAG_SCRIPT_AUTOEXEC) != 0);
2483
2484 RNA_property_boolean_set(op->ptr, prop, value);
2485 use_scripts_autoexec_check = true;
2486 }
2487 return use_scripts_autoexec_check;
2488}
2489
2491
2492/* -------------------------------------------------------------------- */
2495
2501{
2502 Main *bmain = CTX_data_main(C);
2504 wmWindow *win = CTX_wm_window(C);
2505 char filepath[FILE_MAX];
2506 int fileflags;
2507
2508 const char *app_template = U.app_template[0] ? U.app_template : nullptr;
2509 const std::optional<std::string> cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG,
2510 app_template);
2511 if (!cfgdir.has_value()) {
2512 BKE_report(op->reports, RPT_ERROR, "Unable to create user config path");
2513 return OPERATOR_CANCELLED;
2514 }
2515
2516 /* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
2517 * Runs at the end of this function, don't return beforehand. */
2520
2521 /* Check current window and close it if temp. */
2522 if (win && WM_window_is_temp_screen(win)) {
2523 wm_window_close(C, wm, win);
2524 }
2525
2526 /* Update keymaps in user preferences. */
2528
2529 BLI_path_join(filepath, sizeof(filepath), cfgdir->c_str(), BLENDER_STARTUP_FILE);
2530
2531 if (!G.quiet) {
2532 printf("Writing homefile: \"%s\" ", filepath);
2533 }
2534
2536
2537 /* Force save as regular blend file. */
2538 fileflags = G.fileflags & ~G_FILE_COMPRESS;
2539
2540 BlendFileWriteParams blend_write_params{};
2541 /* Make all paths absolute when saving the startup file.
2542 * On load the `G.main->filepath` will be empty so the paths
2543 * won't have a base for resolving the relative paths. */
2544 blend_write_params.remap_mode = BLO_WRITE_PATH_REMAP_ABSOLUTE;
2545 /* Don't apply any path changes to the current blend file. */
2546 blend_write_params.use_save_as_copy = true;
2547
2548 const bool success = BLO_write_file(
2549 bmain, filepath, fileflags, &blend_write_params, op->reports);
2550
2552
2553 if (success) {
2554 if (!G.quiet) {
2555 printf("ok\n");
2556 }
2557 BKE_report(op->reports, RPT_INFO, "Startup file saved");
2558 return OPERATOR_FINISHED;
2559 }
2560 if (!G.quiet) {
2561 printf("fail\n");
2562 }
2563 return OPERATOR_CANCELLED;
2564}
2565
2567 wmOperator *op,
2568 const wmEvent * /*event*/)
2569{
2570 if (!U.app_template[0]) {
2572 op,
2573 IFACE_("Overwrite Startup File"),
2574 IFACE_("Blender will start next time as it is now."),
2575 IFACE_("Overwrite"),
2577 false);
2578 }
2579
2580 /* A different message if this is overriding a specific template startup file. */
2581 char display_name[FILE_MAX];
2582 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
2583 std::string message = fmt::format(
2584 fmt::runtime(IFACE_("Template \"{}\" will start next time as it is now.")),
2585 IFACE_(display_name));
2587 op,
2588 IFACE_("Overwrite Template Startup File"),
2589 message.c_str(),
2590 IFACE_("Overwrite"),
2592 false);
2593}
2594
2596{
2597 ot->name = "Save Startup File";
2598 ot->idname = "WM_OT_save_homefile";
2599 ot->description = "Make the current file the default startup file";
2600
2601 ot->invoke = wm_homefile_write_invoke;
2602 ot->exec = wm_homefile_write_exec;
2603}
2604
2606
2607/* -------------------------------------------------------------------- */
2610
2611/* Only save the prefs block. operator entry. */
2613{
2615
2616 /* Update keymaps in user preferences. */
2618
2619 const bool success = BKE_blendfile_userdef_write_all(op->reports);
2620
2621 return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2622}
2623
2625{
2626 ot->name = "Save Preferences";
2627 ot->idname = "WM_OT_save_userpref";
2628 ot->description = "Make the current preferences default";
2629
2630 ot->invoke = WM_operator_confirm;
2631 ot->exec = wm_userpref_write_exec;
2632}
2633
2635
2636/* -------------------------------------------------------------------- */
2639
2643static void wm_userpref_read_exceptions(UserDef *userdef_curr, const UserDef *userdef_prev)
2644{
2645#define USERDEF_RESTORE(member) \
2646 { \
2647 userdef_curr->member = userdef_prev->member; \
2648 } \
2649 ((void)0)
2650
2651 /* Current visible preferences category. */
2652 USERDEF_RESTORE(space_data.section_active);
2653
2654#undef USERDEF_RESTORE
2655}
2656
2658 Main *bmain,
2659 PointerRNA *ptr_a,
2660 PointerRNA *ptr_b)
2661{
2663 PropertyRNA *iterprop = RNA_struct_iterator_property(ptr_a->type);
2664 BLI_assert(ptr_a->type == ptr_b->type);
2665 RNA_property_collection_begin(ptr_a, iterprop, &iter);
2666 for (; iter.valid; RNA_property_collection_next(&iter)) {
2667 PropertyRNA *prop = static_cast<PropertyRNA *>(iter.ptr.data);
2668 if (STREQ(RNA_property_identifier(prop), "rna_type")) {
2669 continue;
2670 }
2671 switch (RNA_property_type(prop)) {
2672 case PROP_POINTER: {
2673 PointerRNA ptr_sub_a = RNA_property_pointer_get(ptr_a, prop);
2674 PointerRNA ptr_sub_b = RNA_property_pointer_get(ptr_b, prop);
2675 rna_struct_update_when_changed(C, bmain, &ptr_sub_a, &ptr_sub_b);
2676 break;
2677 }
2678 case PROP_COLLECTION:
2679 /* Don't handle collections. */
2680 break;
2681 default: {
2682 if (!RNA_property_equals(bmain, ptr_a, ptr_b, prop, RNA_EQ_STRICT)) {
2683 RNA_property_update(C, ptr_b, prop);
2684 }
2685 }
2686 }
2687 }
2689}
2690
2692 Main *bmain,
2693 UserDef *userdef_prev,
2694 UserDef *userdef_curr)
2695{
2696 PointerRNA ptr_a = RNA_pointer_create_discrete(nullptr, &RNA_Preferences, userdef_prev);
2697 PointerRNA ptr_b = RNA_pointer_create_discrete(nullptr, &RNA_Preferences, userdef_curr);
2698 const bool is_dirty = userdef_curr->runtime.is_dirty;
2699
2700 rna_struct_update_when_changed(C, bmain, &ptr_a, &ptr_b);
2701
2704
2705 userdef_curr->runtime.is_dirty = is_dirty;
2706}
2707
2709{
2710 Main *bmain = CTX_data_main(C);
2711 const bool use_data = false;
2712 const bool use_userdef = true;
2713 const bool use_factory_settings = STREQ(op->type->idname, "WM_OT_read_factory_userpref");
2714 const bool use_factory_settings_app_template_only =
2715 (use_factory_settings && RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only"));
2716
2718
2719 UserDef U_backup = blender::dna::shallow_copy(U);
2720
2721 wmHomeFileRead_Params read_homefile_params{};
2722 read_homefile_params.use_data = use_data;
2723 read_homefile_params.use_userdef = use_userdef;
2724 read_homefile_params.use_factory_settings = use_factory_settings;
2725 read_homefile_params.use_factory_settings_app_template_only =
2726 use_factory_settings_app_template_only;
2727 read_homefile_params.use_empty_data = false;
2728 read_homefile_params.filepath_startup_override = nullptr;
2730 wm_homefile_read(C, &read_homefile_params, op->reports);
2731
2732 wm_userpref_read_exceptions(&U, &U_backup);
2733 SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT);
2734
2735 wm_userpref_update_when_changed(C, bmain, &U_backup, &U);
2736
2737 if (use_factory_settings) {
2738 U.runtime.is_dirty = true;
2739 }
2740
2742
2743 /* Needed to recalculate UI scaling values (eg, #UserDef.inv_dpi_fac). */
2744 wm_window_clear_drawable(static_cast<wmWindowManager *>(bmain->wm.first));
2745
2747
2748 return OPERATOR_FINISHED;
2749}
2750
2752{
2753 ot->name = "Load Preferences";
2754 ot->idname = "WM_OT_read_userpref";
2755 ot->description = "Load last saved preferences";
2756
2757 ot->invoke = WM_operator_confirm;
2758 ot->exec = wm_userpref_read_exec;
2759}
2760
2762 wmOperator *op,
2763 const wmEvent * /*event*/)
2764{
2765 std::string title;
2766
2767 const bool template_only = U.app_template[0] &&
2768 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only");
2769
2770 if (template_only) {
2771 char display_name[FILE_MAX];
2772 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
2773 title = fmt::format(fmt::runtime(IFACE_("Load Factory \"{}\" Preferences.")),
2774 IFACE_(display_name));
2775 }
2776 else {
2777 title = IFACE_("Load Factory Blender Preferences");
2778 }
2779
2781 C,
2782 op,
2783 title.c_str(),
2784 IFACE_("To make changes to Preferences permanent, use \"Save Preferences\""),
2785 IFACE_("Load"),
2787 false);
2788}
2789
2791{
2792 ot->name = "Load Factory Preferences";
2793 ot->idname = "WM_OT_read_factory_userpref";
2794 ot->description =
2795 "Load factory default preferences. "
2796 "To make changes to preferences permanent, use \"Save Preferences\"";
2797
2798 ot->invoke = wm_userpref_read_invoke;
2799 ot->exec = wm_userpref_read_exec;
2800
2802}
2803
2805
2806/* -------------------------------------------------------------------- */
2809
2816
2818{
2819 ot->name = "Reload History File";
2820 ot->idname = "WM_OT_read_history";
2821 ot->description = "Reloads history and bookmarks";
2822
2823 ot->invoke = WM_operator_confirm;
2825
2826 /* This operator is only used for loading settings from a previous blender install. */
2827 ot->flag = OPTYPE_INTERNAL;
2828}
2829
2831
2832/* -------------------------------------------------------------------- */
2837
2839{
2840 const bool use_factory_startup_and_userdef = STREQ(op->type->idname,
2841 "WM_OT_read_factory_settings");
2842 const bool use_factory_settings = use_factory_startup_and_userdef ||
2843 RNA_boolean_get(op->ptr, "use_factory_startup");
2844 const bool use_factory_settings_app_template_only =
2845 (use_factory_startup_and_userdef &&
2846 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only"));
2847
2848 bool use_userdef = false;
2849 char filepath_buf[FILE_MAX];
2850 const char *filepath = nullptr;
2851 UserDef U_backup = blender::dna::shallow_copy(U);
2852
2853 if (!use_factory_settings) {
2854 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath");
2855
2856 /* This can be used when loading of a start-up file should only change
2857 * the scene content but keep the blender UI as it is. */
2858 wm_open_init_load_ui(op, true);
2859 SET_FLAG_FROM_TEST(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
2860
2861 if (RNA_property_is_set(op->ptr, prop)) {
2862 RNA_property_string_get(op->ptr, prop, filepath_buf);
2863 filepath = filepath_buf;
2864 if (BLI_access(filepath, R_OK)) {
2866 op->reports, RPT_ERROR, "Can't read alternative start-up file: \"%s\"", filepath);
2867 return OPERATOR_CANCELLED;
2868 }
2869 }
2870 }
2871 else {
2872 if (use_factory_startup_and_userdef) {
2873 /* Always load UI for factory settings (prefs will re-init). */
2874 G.fileflags &= ~G_FILE_NO_UI;
2875 /* Always load preferences with factory settings. */
2876 use_userdef = true;
2877 }
2878 }
2879
2880 /* Close any user-loaded fonts. */
2882
2883 char app_template_buf[sizeof(U.app_template)];
2884 const char *app_template;
2885 PropertyRNA *prop_app_template = RNA_struct_find_property(op->ptr, "app_template");
2886 const bool use_splash = !use_factory_settings && RNA_boolean_get(op->ptr, "use_splash");
2887 const bool use_empty_data = RNA_boolean_get(op->ptr, "use_empty");
2888
2889 if (prop_app_template && RNA_property_is_set(op->ptr, prop_app_template)) {
2890 RNA_property_string_get(op->ptr, prop_app_template, app_template_buf);
2891 app_template = app_template_buf;
2892
2893 if (!use_factory_settings) {
2894 /* Always load preferences when switching templates with own preferences. */
2897 }
2898
2899 /* Turn override off, since we're explicitly loading a different app-template. */
2901 }
2902 else {
2903 /* Normally nullptr, only set when overriding from the command-line. */
2905 }
2906
2907 if (use_userdef) {
2909 }
2910
2911 wmHomeFileRead_Params read_homefile_params{};
2912 read_homefile_params.use_data = true;
2913 read_homefile_params.use_userdef = use_userdef;
2914 read_homefile_params.use_factory_settings = use_factory_settings;
2915 read_homefile_params.use_factory_settings_app_template_only =
2916 use_factory_settings_app_template_only;
2917 read_homefile_params.use_empty_data = use_empty_data;
2918 read_homefile_params.filepath_startup_override = filepath;
2919 read_homefile_params.app_template_override = app_template;
2920 wm_homefile_read(C, &read_homefile_params, op->reports);
2921
2922 if (use_splash) {
2924 }
2925
2926 if (use_userdef) {
2927 wm_userpref_read_exceptions(&U, &U_backup);
2928 SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT);
2929
2930 if (use_factory_settings) {
2931 U.runtime.is_dirty = true;
2932 }
2933 }
2934
2935 if (use_userdef) {
2937 }
2938
2939 if (G.fileflags & G_FILE_NO_UI) {
2941 }
2942
2943 return OPERATOR_FINISHED;
2944}
2945
2947{
2949 C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data, nullptr);
2950}
2951
2961
2963{
2964 PropertyRNA *prop;
2965
2966 prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", "");
2968
2969 prop = RNA_def_boolean(
2970 ot->srna,
2971 "use_empty",
2972 false,
2973 "Empty",
2974 "After loading, remove everything except scenes, windows, and workspaces. This makes it "
2975 "possible to load the startup file with its scene configuration and window layout intact, "
2976 "but no objects, materials, animations, ...");
2978}
2979
2981{
2982 PropertyRNA *prop;
2983 ot->name = "Reload Start-Up File";
2984 ot->idname = "WM_OT_read_homefile";
2985 ot->description = "Open the default file";
2986
2987 ot->invoke = wm_homefile_read_invoke;
2988 ot->exec = wm_homefile_read_exec;
2989
2990 prop = RNA_def_string_file_path(ot->srna,
2991 "filepath",
2992 nullptr,
2993 FILE_MAX,
2994 "File Path",
2995 "Path to an alternative start-up file");
2997
2998 /* So scripts can use an alternative start-up file without the UI. */
2999 prop = RNA_def_boolean(
3000 ot->srna, "load_ui", true, "Load UI", "Load user interface setup from the .blend file");
3002
3003 /* So the splash can be kept open after loading a file (for templates). */
3004 prop = RNA_def_boolean(ot->srna, "use_splash", false, "Splash", "");
3006
3007 /* So scripts can load factory-startup without resetting preferences
3008 * (which has other implications such as reloading all add-ons).
3009 * Match naming for `--factory-startup` command line argument. */
3010 prop = RNA_def_boolean(ot->srna,
3011 "use_factory_startup",
3012 false,
3013 "Factory Startup",
3014 "Load the default ('factory startup') blend file. "
3015 "This is independent of the normal start-up file that the user can save");
3018
3020
3021 /* Omit poll to run in background mode. */
3022}
3023
3025 wmOperator *op,
3026 const wmEvent * /*event*/)
3027{
3029 CTX_wm_manager(C));
3030 std::string title;
3031 const bool template_only = U.app_template[0] &&
3032 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only");
3033
3034 if (template_only) {
3035 char display_name[FILE_MAX];
3036 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
3037 title = fmt::format(fmt::runtime(IFACE_("Load Factory \"{}\" Startup File and Preferences")),
3038 IFACE_(display_name));
3039 }
3040 else {
3041 title = IFACE_("Load Factory Default Startup File and Preferences");
3042 }
3043
3045 C,
3046 op,
3047 title.c_str(),
3048 unsaved ? IFACE_("To make changes to Preferences permanent, use \"Save Preferences\".\n"
3049 "Warning: Your file is unsaved! Proceeding will abandon your changes.") :
3050 IFACE_("To make changes to Preferences permanent, use \"Save Preferences\"."),
3051 IFACE_("Load"),
3053 false);
3054}
3055
3057{
3058 ot->name = "Load Factory Settings";
3059 ot->idname = "WM_OT_read_factory_settings";
3060 ot->description =
3061 "Load factory default startup file and preferences. "
3062 "To make changes permanent, use \"Save Startup File\" and \"Save Preferences\"";
3063
3065 ot->exec = wm_homefile_read_exec;
3066 /* Omit poll to run in background mode. */
3067
3069
3071}
3072
3074
3075/* -------------------------------------------------------------------- */
3078
3083 const char *filepath,
3084 const bool use_scripts_autoexec_check,
3086{
3087 /* XXX: wm in context is not set correctly after #WM_file_read -> crash. */
3088 /* Do it before for now, but is this correct with multiple windows? */
3090
3091 const bool success = WM_file_read(C, filepath, use_scripts_autoexec_check, reports);
3092
3093 return success;
3094}
3095
3096/* Generic operator state utilities. */
3097
3098static void create_operator_state(wmOperatorType *ot, int first_state)
3099{
3100 PropertyRNA *prop = RNA_def_int(
3101 ot->srna, "state", first_state, INT32_MIN, INT32_MAX, "State", "", INT32_MIN, INT32_MAX);
3104}
3105
3107{
3108 return RNA_int_get(op->ptr, "state");
3109}
3110
3112{
3113 RNA_int_set(op->ptr, "state", state);
3114}
3115
3120
3122 wmOperator *op,
3123 OperatorDispatchTarget *targets)
3124{
3125 int state = get_operator_state(op);
3126 for (int i = 0; targets[i].run; i++) {
3127 OperatorDispatchTarget target = targets[i];
3128 if (target.state == state) {
3129 return target.run(C, op);
3130 }
3131 }
3133 return OPERATOR_CANCELLED;
3134}
3135
3137
3138/* -------------------------------------------------------------------- */
3141
3142enum {
3146};
3147
3149
3151{
3153 C, "WM_OT_open_mainfile", WM_OP_INVOKE_DEFAULT, (IDProperty *)user_data, nullptr);
3154}
3155
3170
3172{
3174
3175 Main *bmain = CTX_data_main(C);
3176 const char *blendfile_path = BKE_main_blendfile_path(bmain);
3177
3178 if (CTX_wm_window(C) == nullptr) {
3179 /* In rare cases this could happen, when trying to invoke in background
3180 * mode on load for example. Don't use poll for this because exec()
3181 * can still run without a window. */
3182 BKE_report(op->reports, RPT_ERROR, "Context window not set");
3183 return OPERATOR_CANCELLED;
3184 }
3185
3186 /* If possible, get the name of the most recently used `.blend` file. */
3187 if (G.recent_files.first) {
3188 RecentFile *recent = static_cast<RecentFile *>(G.recent_files.first);
3189 blendfile_path = recent->filepath;
3190 }
3191
3192 RNA_string_set(op->ptr, "filepath", blendfile_path);
3193 wm_open_init_load_ui(op, true);
3194 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3195 UNUSED_VARS(use_scripts_autoexec_check); /* The user can set this in the UI. */
3196 op->customdata = nullptr;
3197
3199
3201}
3202
3204{
3205 char filepath[FILE_MAX];
3206 bool success;
3207
3208 RNA_string_get(op->ptr, "filepath", filepath);
3209 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3210
3211 /* For file opening, also print in console for warnings, not only errors. */
3213
3214 /* Re-use last loaded setting so we can reload a file without changing. */
3215 wm_open_init_load_ui(op, false);
3216 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, false);
3217
3218 SET_FLAG_FROM_TEST(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
3220 success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, op->reports);
3221
3222 if (success) {
3223 if (G.fileflags & G_FILE_NO_UI) {
3225 }
3227 return OPERATOR_FINISHED;
3228 }
3229 return OPERATOR_CANCELLED;
3230}
3231
3238
3243
3245 wmOperator *op,
3246 const wmEvent * /*event*/)
3247{
3248 return wm_open_mainfile_dispatch(C, op);
3249}
3250
3255
3257 wmOperatorType * /*ot*/,
3258 PointerRNA *ptr)
3259{
3260 if (!RNA_struct_property_is_set(ptr, "filepath")) {
3261 return "";
3262 }
3263
3264 char filepath[FILE_MAX];
3265 RNA_string_get(ptr, "filepath", filepath);
3266
3267 BLI_stat_t stats;
3268 if (BLI_stat(filepath, &stats) == -1) {
3269 return fmt::format("{}\n\n{}", filepath, TIP_("File Not Found"));
3270 }
3271
3272 /* Date. */
3273 char date_str[FILELIST_DIRENTRY_DATE_LEN];
3274 char time_str[FILELIST_DIRENTRY_TIME_LEN];
3275 bool is_today, is_yesterday;
3277 nullptr, int64_t(stats.st_mtime), false, time_str, date_str, &is_today, &is_yesterday);
3278 if (is_today || is_yesterday) {
3279 STRNCPY(date_str, is_today ? TIP_("Today") : TIP_("Yesterday"));
3280 }
3281
3282 /* Size. */
3283 char size_str[FILELIST_DIRENTRY_SIZE_LEN];
3284 BLI_filelist_entry_size_to_string(nullptr, uint64_t(stats.st_size), false, size_str);
3285
3286 return fmt::format("{}\n\n{}: {} {}\n{}: {}",
3287 filepath,
3288 TIP_("Modified"),
3289 date_str,
3290 time_str,
3291 TIP_("Size"),
3292 size_str);
3293}
3294
3295/* Currently fits in a pointer. */
3298};
3299BLI_STATIC_ASSERT(sizeof(FileRuntime) <= sizeof(void *), "Struct must not exceed pointer size");
3300
3302{
3303 FileRuntime *file_info = (FileRuntime *)&op->customdata;
3304 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
3305 bool is_untrusted = false;
3306 char filepath[FILE_MAX];
3307 char *lslash;
3308
3309 RNA_string_get(op->ptr, "filepath", filepath);
3310
3311 /* Get the directory. */
3312 lslash = (char *)BLI_path_slash_rfind(filepath);
3313 if (lslash) {
3314 *(lslash + 1) = '\0';
3315 }
3316
3317 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
3318 if (BKE_autoexec_match(filepath) == true) {
3319 RNA_property_boolean_set(op->ptr, prop, false);
3320 is_untrusted = true;
3321 }
3322 }
3323
3324 if (file_info) {
3325 file_info->is_untrusted = is_untrusted;
3326 }
3327
3328 return is_untrusted;
3329}
3330
3331static void wm_open_mainfile_ui(bContext * /*C*/, wmOperator *op)
3332{
3333 FileRuntime *file_info = (FileRuntime *)&op->customdata;
3334 uiLayout *layout = op->layout;
3335 const char *autoexec_text;
3336
3337 layout->prop(op->ptr, "load_ui", UI_ITEM_NONE, std::nullopt, ICON_NONE);
3338
3339 uiLayout *col = &layout->column(false);
3340 if (file_info->is_untrusted) {
3341 autoexec_text = IFACE_("Trusted Source [Untrusted Path]");
3342 uiLayoutSetActive(col, false);
3343 uiLayoutSetEnabled(col, false);
3344 }
3345 else {
3346 autoexec_text = IFACE_("Trusted Source");
3347 }
3348
3349 col->prop(op->ptr, "use_scripts", UI_ITEM_NONE, autoexec_text, ICON_NONE);
3350}
3351
3353{
3354 RNA_def_boolean(ot->srna,
3355 "use_scripts",
3356 false,
3357 "Trusted Source",
3358 "Allow .blend file to execute scripts automatically, default available from "
3359 "system preferences");
3360}
3361
3363{
3364 ot->name = "Open";
3365 ot->idname = "WM_OT_open_mainfile";
3366 ot->description = "Open a Blender file";
3367 ot->get_description = wm_open_mainfile_get_description;
3368
3369 ot->invoke = wm_open_mainfile_invoke;
3370 ot->exec = wm_open_mainfile_exec;
3371 ot->check = wm_open_mainfile_check;
3372 ot->ui = wm_open_mainfile_ui;
3373 /* Omit window poll so this can work in background mode. */
3374
3382
3384 ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file");
3385
3387
3389 ot->srna, "display_file_selector", true, "Display File Selector", "");
3391
3393}
3394
3396
3397/* -------------------------------------------------------------------- */
3400
3402 wmOperator *op,
3403 const wmEvent * /*event*/)
3404{
3405 std::string message = IFACE_("Any unsaved changes will be lost.");
3407 message += "\n";
3408 message += IFACE_("Warning: There are unsaved external image(s).");
3409 }
3410
3412 op,
3413 IFACE_("Revert to the Saved File"),
3414 message.c_str(),
3415 IFACE_("Revert"),
3417 false);
3418}
3419
3421{
3422 Main *bmain = CTX_data_main(C);
3423 bool success;
3424 char filepath[FILE_MAX];
3425
3426 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, false);
3427
3429
3430 STRNCPY(filepath, BKE_main_blendfile_path(bmain));
3431 success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, op->reports);
3432
3433 if (success) {
3434 return OPERATOR_FINISHED;
3435 }
3436 return OPERATOR_CANCELLED;
3437}
3438
3440{
3441 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3442 return (blendfile_path[0] != '\0');
3443}
3444
3446{
3447 ot->name = "Revert";
3448 ot->idname = "WM_OT_revert_mainfile";
3449 ot->description = "Reload the saved file";
3450
3451 ot->invoke = wm_revert_mainfile_invoke;
3454
3456}
3457
3459
3460/* -------------------------------------------------------------------- */
3463
3465 const bool use_scripts_autoexec_check,
3467{
3468 char filepath[FILE_MAX];
3469 BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE);
3470 G.fileflags |= G_FILE_RECOVER_READ;
3471 const bool success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, reports);
3472 G.fileflags &= ~G_FILE_RECOVER_READ;
3473 return success;
3474}
3475
3477 wmOperator *op,
3478 const bool use_scripts_autoexec_check)
3479{
3481 if (WM_file_recover_last_session(C, use_scripts_autoexec_check, op->reports)) {
3482 if (!G.background) {
3483 wmOperatorType *ot = op->type;
3484 PointerRNA *props_ptr = MEM_new<PointerRNA>(__func__);
3486 RNA_boolean_set(props_ptr, "use_scripts", true);
3488 }
3489 return OPERATOR_FINISHED;
3490 }
3491 return OPERATOR_CANCELLED;
3492}
3493
3495{
3496 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3497 return wm_recover_last_session_impl(C, op, use_scripts_autoexec_check);
3498}
3499
3501{
3503 C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data, nullptr);
3504}
3505
3507 wmOperator *op,
3508 const wmEvent * /*event*/)
3509{
3510 /* Keep the current setting instead of using the preferences since a file selector
3511 * doesn't give us the option to change the setting. */
3512 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, false);
3513
3516 {
3517 return OPERATOR_INTERFACE;
3518 }
3519 return wm_recover_last_session_impl(C, op, use_scripts_autoexec_check);
3520}
3521
3523{
3524 ot->name = "Recover Last Session";
3525 ot->idname = "WM_OT_recover_last_session";
3526 ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")";
3527
3530
3532}
3533
3535
3536/* -------------------------------------------------------------------- */
3539
3541{
3542 char filepath[FILE_MAX];
3543 bool success;
3544
3545 RNA_string_get(op->ptr, "filepath", filepath);
3546 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3547
3548 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3550
3551 G.fileflags |= G_FILE_RECOVER_READ;
3552
3553 success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, op->reports);
3554
3555 G.fileflags &= ~G_FILE_RECOVER_READ;
3556
3557 if (success) {
3558 if (!G.background) {
3559 wmOperatorType *ot = op->type;
3560 PointerRNA *props_ptr = MEM_new<PointerRNA>(__func__);
3562 RNA_boolean_set(props_ptr, "use_scripts", true);
3564 }
3565 return OPERATOR_FINISHED;
3566 }
3567 return OPERATOR_CANCELLED;
3568}
3569
3571 wmOperator *op,
3572 const wmEvent * /*event*/)
3573{
3574 char filepath[FILE_MAX];
3575
3576 wm_autosave_location(filepath);
3577 RNA_string_set(op->ptr, "filepath", filepath);
3578 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3579 UNUSED_VARS(use_scripts_autoexec_check); /* The user can set this in the UI. */
3581
3583}
3584
3586{
3587 ot->name = "Recover Auto Save";
3588 ot->idname = "WM_OT_recover_auto_save";
3589 ot->description = "Open an automatically saved file to recover it";
3590
3593
3601
3603}
3604
3606
3607/* -------------------------------------------------------------------- */
3612
3613static void wm_filepath_default(const Main *bmain, char *filepath)
3614{
3615 if (bmain->filepath[0] == '\0') {
3616 char filename_untitled[FILE_MAXFILE];
3617 SNPRINTF(filename_untitled, "%s.blend", DATA_("Untitled"));
3618 BLI_path_filename_ensure(filepath, FILE_MAX, filename_untitled);
3619 }
3620}
3621
3623{
3624 PropertyRNA *prop;
3625
3626 prop = RNA_struct_find_property(op->ptr, "compress");
3627 if (!RNA_property_is_set(op->ptr, prop)) {
3628 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3629 if (blendfile_path[0] != '\0') { /* Keep flag for existing file. */
3630 RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0);
3631 }
3632 else { /* Use userdef for new file. */
3633 RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0);
3634 }
3635 }
3636}
3637
3639{
3640 Main *bmain = CTX_data_main(C);
3641 PropertyRNA *prop;
3642 char filepath[FILE_MAX];
3643
3644 prop = RNA_struct_find_property(op->ptr, "filepath");
3645 if (!RNA_property_is_set(op->ptr, prop)) {
3646 const char *blendfile_path = BKE_main_blendfile_path(bmain);
3647 /* If not saved before, get the name of the most recently used `.blend` file. */
3648 if ((blendfile_path[0] == '\0') && G.recent_files.first) {
3649 RecentFile *recent = static_cast<RecentFile *>(G.recent_files.first);
3650 STRNCPY(filepath, recent->filepath);
3651 }
3652 else {
3653 STRNCPY(filepath, blendfile_path);
3654 }
3655
3656 /* For convenience when using "Save As" on asset system files:
3657 * Replace `.asset.blend` extension with just `.blend`.
3658 * Asset system files must not be overridden (except by the asset system),
3659 * there are further checks to prevent this entirely. */
3660 if (bmain->is_asset_edit_file &&
3662 {
3663 filepath[strlen(filepath) - strlen(BLENDER_ASSET_FILE_SUFFIX)] = '\0';
3664 BLI_path_extension_ensure(filepath, FILE_MAX, ".blend");
3665 }
3666
3667 wm_filepath_default(bmain, filepath);
3668 RNA_property_string_set(op->ptr, prop, filepath);
3669 }
3670}
3671
3673 wmOperator *op,
3674 const wmEvent * /*event*/)
3675{
3676
3678 save_set_filepath(C, op);
3679
3680 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
3681 if (!RNA_property_is_set(op->ptr, prop)) {
3682 RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_RELPATHS));
3683 }
3684
3686
3688}
3689
3690/* Function used for #WM_OT_save_mainfile too. */
3692{
3693 Main *bmain = CTX_data_main(C);
3694 char filepath[FILE_MAX];
3695 const bool is_save_as = (op->type->invoke == wm_save_as_mainfile_invoke);
3696 const bool use_save_as_copy = is_save_as && RNA_boolean_get(op->ptr, "copy");
3697
3698 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "incremental");
3699 const bool is_incremental = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
3700
3701 /* We could expose all options to the users however in most cases remapping
3702 * existing relative paths is a good default.
3703 * Users can manually make their paths relative & absolute if they wish. */
3704 const eBLO_WritePathRemap remap_mode = RNA_boolean_get(op->ptr, "relative_remap") ?
3708
3709 const bool is_filepath_set = RNA_struct_property_is_set(op->ptr, "filepath");
3710 if (is_filepath_set) {
3711 RNA_string_get(op->ptr, "filepath", filepath);
3712 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3713 }
3714 else {
3715 STRNCPY(filepath, BKE_main_blendfile_path(bmain));
3716 }
3717
3718 if (filepath[0] == '\0') {
3719 BKE_report(op->reports,
3720 RPT_ERROR,
3721 "Unable to save an unsaved file with an empty or unset \"filepath\" property");
3722 return OPERATOR_CANCELLED;
3723 }
3724
3725 if ((is_save_as == false) && is_incremental) {
3726 char head[FILE_MAXFILE], tail[FILE_MAXFILE];
3727 ushort digits;
3728 int num = BLI_path_sequence_decode(filepath, head, sizeof(head), tail, sizeof(tail), &digits);
3729 /* Numbers greater than INT_MAX return 0, resulting in always appending "1" to the name. */
3730 if (num == 0 && digits == 0) {
3731 /* This does nothing if there are no numbers at the end of the head. */
3733 }
3734
3735 const int tries_limit = 1000;
3736 int tries = 0;
3737 bool in_use = true;
3738 do {
3739 num++;
3740 tries++;
3741 BLI_path_sequence_encode(filepath, sizeof(filepath), head, tail, digits, num);
3742 in_use = BLI_exists(filepath);
3743 } while (in_use && tries < tries_limit && num < INT_MAX);
3744 if (in_use) {
3745 BKE_report(op->reports, RPT_ERROR, "Unable to find an available incremented file name");
3746 return OPERATOR_CANCELLED;
3747 }
3748 }
3749
3750 const int fileflags_orig = G.fileflags;
3751 int fileflags = G.fileflags;
3752
3753 /* Set compression flag. */
3754 SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "compress"), G_FILE_COMPRESS);
3755
3756 const bool success = wm_file_write(
3757 C, filepath, fileflags, remap_mode, use_save_as_copy, op->reports);
3758
3759 if ((op->flag & OP_IS_INVOKE) == 0) {
3760 /* OP_IS_INVOKE is set when the operator is called from the GUI.
3761 * If it is not set, the operator is called from a script and
3762 * shouldn't influence G.fileflags. */
3763 G.fileflags = fileflags_orig;
3764 }
3765
3766 if (success == false) {
3767 return OPERATOR_CANCELLED;
3768 }
3769
3770 const char *filename = BLI_path_basename(filepath);
3771
3772 if (is_incremental) {
3773 BKE_reportf(op->reports, RPT_INFO, "Saved incremental as \"%s\"", filename);
3774 }
3775 else if (is_save_as) {
3776 /* use_save_as_copy depends upon is_save_as. */
3777 if (use_save_as_copy) {
3778 BKE_reportf(op->reports, RPT_INFO, "Saved copy as \"%s\"", filename);
3779 }
3780 else {
3781 BKE_reportf(op->reports, RPT_INFO, "Saved as \"%s\"", filename);
3782 }
3783 }
3784 else {
3785 BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", filename);
3786 }
3787
3788 if (!use_save_as_copy) {
3789 /* If saved file is the active one, there are technically no more compatibility issues, the
3790 * file on disk now matches the currently opened data version-wise. */
3791 bmain->has_forward_compatibility_issues = false;
3792
3793 /* If saved file is the active one, notify WM so that saved status and window title can be
3794 * updated. */
3797 /* Restart auto-save timer to avoid unnecessary unexpected freezing (because of auto-save)
3798 * when often saving manually. */
3801 wm->autosave_scheduled = false;
3802 }
3803 }
3804
3805 if (!is_save_as && RNA_boolean_get(op->ptr, "exit")) {
3807 }
3808
3809 return OPERATOR_FINISHED;
3810}
3811
3813{
3814 char filepath[FILE_MAX];
3815 RNA_string_get(op->ptr, "filepath", filepath);
3816 if (!BKE_blendfile_extension_check(filepath)) {
3817 /* NOTE(@ideasman42): some users would prefer #BLI_path_extension_replace(),
3818 * we have had some nitpicking bug reports about this.
3819 * Always adding the extension as users may use '.' as part of the file-name. */
3820 BLI_path_extension_ensure(filepath, FILE_MAX, ".blend");
3821 RNA_string_set(op->ptr, "filepath", filepath);
3822 return true;
3823 }
3824 return false;
3825}
3826
3828{
3829 if (RNA_boolean_get(ptr, "copy")) {
3830 return CTX_IFACE_(ot->translation_context, "Save Copy");
3831 }
3832 return "";
3833}
3834
3836 wmOperatorType * /*ot*/,
3837 PointerRNA *ptr)
3838{
3839 if (RNA_boolean_get(ptr, "copy")) {
3840 return BLI_strdup(TIP_(
3841 "Save the current file in the desired location but do not make the saved file active"));
3842 }
3843 return "";
3844}
3845
3847{
3848 PropertyRNA *prop;
3849
3850 ot->name = "Save As";
3851 ot->idname = "WM_OT_save_as_mainfile";
3852 ot->description = "Save the current file in the desired location";
3853
3856 ot->get_name = wm_save_as_mainfile_get_name;
3857 ot->get_description = wm_save_as_mainfile_get_description;
3858 ot->check = wm_save_mainfile_check;
3859 /* Omit window poll so this can work in background mode. */
3860
3864 FILE_SAVE,
3868 RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
3869 RNA_def_boolean(ot->srna,
3870 "relative_remap",
3871 true,
3872 "Remap Relative",
3873 "Remap relative paths when saving to a different directory");
3874 prop = RNA_def_boolean(
3875 ot->srna,
3876 "copy",
3877 false,
3878 "Save Copy",
3879 "Save a copy of the actual working state but does not make saved file active");
3881}
3882
3884 wmOperator *op,
3885 const wmEvent * /*event*/)
3886{
3888
3889 /* Cancel if no active window. */
3890 if (CTX_wm_window(C) == nullptr) {
3891 return OPERATOR_CANCELLED;
3892 }
3893
3895 save_set_filepath(C, op);
3896
3897 /* If we're saving for the first time and prefer relative paths -
3898 * any existing paths will be absolute,
3899 * enable the option to remap paths to avoid confusion, see: #37240. */
3900 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3901 if ((blendfile_path[0] == '\0') && (U.flag & USER_RELPATHS)) {
3902 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
3903 if (!RNA_property_is_set(op->ptr, prop)) {
3904 RNA_property_boolean_set(op->ptr, prop, true);
3905 }
3906 }
3907
3908 if (blendfile_path[0] != '\0') {
3912 }
3913 else {
3915 }
3916 }
3917 else {
3920 }
3921
3922 return ret;
3923}
3924
3926 wmOperatorType * /*ot*/,
3927 PointerRNA *ptr)
3928{
3929 if (RNA_boolean_get(ptr, "incremental")) {
3930 return TIP_(
3931 "Save the current Blender file with a numerically incremented name that does not "
3932 "overwrite any existing files");
3933 }
3934 return "";
3935}
3936
3938{
3939 ot->name = "Save Blender File";
3940 ot->idname = "WM_OT_save_mainfile";
3941 ot->description = "Save the current Blender file";
3942
3943 ot->invoke = wm_save_mainfile_invoke;
3945 ot->check = wm_save_mainfile_check;
3946 ot->get_description = wm_save_mainfile_get_description;
3947 /* Omit window poll so this can work in background mode. */
3948
3949 PropertyRNA *prop;
3953 FILE_SAVE,
3957 RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
3958 RNA_def_boolean(ot->srna,
3959 "relative_remap",
3960 false,
3961 "Remap Relative",
3962 "Remap relative paths when saving to a different directory");
3963
3964 prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "Exit Blender after saving");
3966
3967 prop = RNA_def_boolean(ot->srna,
3968 "incremental",
3969 false,
3970 "Incremental",
3971 "Save the current Blender file with a numerically incremented name that "
3972 "does not overwrite any existing files");
3974}
3975
3977
3978/* -------------------------------------------------------------------- */
3981
3983
3985 {CLEAR_RECENT_ALL, "ALL", 0, "All Items", ""},
3986 {CLEAR_RECENT_MISSING, "MISSING", 0, "Items Not Found", ""},
3987 {0, nullptr, 0, nullptr, nullptr},
3988};
3989
3991 wmOperator *op,
3992 const wmEvent *event)
3993{
3995 C, op, event, IFACE_("Clear Recent Files List"), IFACE_("Remove"));
3996}
3997
3999{
4000 ClearRecentInclude include = static_cast<ClearRecentInclude>(RNA_enum_get(op->ptr, "remove"));
4001
4002 if (include == CLEAR_RECENT_ALL) {
4004 }
4005 else if (include == CLEAR_RECENT_MISSING) {
4006 LISTBASE_FOREACH_MUTABLE (RecentFile *, recent, &G.recent_files) {
4007 if (!BLI_exists(recent->filepath)) {
4008 wm_history_file_free(recent);
4009 }
4010 }
4011 }
4012
4014
4015 return OPERATOR_FINISHED;
4016}
4017
4019{
4020 uiLayout *layout = op->layout;
4021 uiLayoutSetPropSep(layout, true);
4022 uiLayoutSetPropDecorate(layout, false);
4023
4024 layout->separator();
4025 layout->prop(op->ptr, "remove", UI_ITEM_R_TOGGLE, std::nullopt, ICON_NONE);
4026 layout->separator();
4027}
4028
4030{
4031 ot->name = "Clear Recent Files List";
4032 ot->idname = "WM_OT_clear_recent_files";
4033 ot->description = "Clear the recent files list";
4034
4038
4039 /* flags */
4040 ot->flag = OPTYPE_REGISTER;
4041
4042 /* props */
4043 ot->prop = RNA_def_enum(
4044 ot->srna, "remove", prop_clear_recent_types, CLEAR_RECENT_ALL, "Remove", "");
4045}
4046
4048
4049/* -------------------------------------------------------------------- */
4052
4053static void wm_block_autorun_warning_ignore(bContext *C, void *arg_block, void * /*arg*/)
4054{
4055 wmWindow *win = CTX_wm_window(C);
4056 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4057
4058 /* Free the data as it's no longer needed. */
4059 wm_test_autorun_revert_action_set(nullptr, nullptr);
4060}
4061
4063{
4064 wmWindow *win = CTX_wm_window(C);
4065
4066 UI_popup_block_close(C, win, block);
4067
4068 /* Save user preferences for permanent execution. */
4069 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
4070 WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, nullptr, nullptr);
4071 }
4072
4073 /* Load file again with scripts enabled.
4074 * The reload is necessary to allow scripts to run when the files loads. */
4076}
4077
4079{
4080 wmWindow *win = CTX_wm_window(C);
4081 Main *bmain = CTX_data_main(C);
4082
4083 UI_popup_block_close(C, win, block);
4084
4085 /* Save user preferences for permanent execution. */
4086 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
4087 WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, nullptr, nullptr);
4088 }
4089
4090 /* Force a full refresh, but without reloading the file. */
4091 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
4093 }
4094}
4095
4096/* Build the auto-run warning dialog UI. */
4097static uiBlock *block_create_autorun_warning(bContext *C, ARegion *region, void * /*arg1*/)
4098{
4099 const char *blendfile_path = BKE_main_blendfile_path_from_global();
4101
4102 uiBlock *block = UI_block_begin(
4103 C, region, "autorun_warning_popup", blender::ui::EmbossType::Emboss);
4108
4109 const char *title = RPT_(
4110 "For security reasons, automatic execution of Python scripts "
4111 "in this file was disabled:");
4112 const char *message = RPT_("This may lead to unexpected behavior");
4113 const char *checkbox_text = RPT_("Permanently allow execution of scripts");
4114
4115 /* Measure strings to find the longest. */
4116 const uiStyle *style = UI_style_get_dpi();
4117 UI_fontstyle_set(&style->widget);
4118 int text_width = int(BLF_width(style->widget.uifont_id, title, BLF_DRAW_STR_DUMMY_MAX));
4119 text_width = std::max(text_width,
4120 int(BLF_width(style->widget.uifont_id, message, BLF_DRAW_STR_DUMMY_MAX)));
4121 text_width = std::max(
4122 text_width,
4123 int(BLF_width(style->widget.uifont_id, checkbox_text, BLF_DRAW_STR_DUMMY_MAX) +
4124 (UI_SCALE_FAC * 25.0f)));
4125
4126 const int dialog_width = std::max(int(400.0f * UI_SCALE_FAC),
4127 text_width + int(style->columnspace * 2.5));
4128 const short icon_size = 40 * UI_SCALE_FAC;
4129 uiLayout *layout = uiItemsAlertBox(
4130 block, style, dialog_width + icon_size, ALERT_ICON_ERROR, icon_size);
4131
4132 /* Title and explanation text. */
4133 uiLayout *col = &layout->column(true);
4134 uiItemL_ex(col, title, ICON_NONE, true, false);
4135 uiItemL_ex(col, G.autoexec_fail, ICON_NONE, false, true);
4136 col->label(message, ICON_NONE);
4137
4138 layout->separator();
4139
4140 PointerRNA pref_ptr = RNA_pointer_create_discrete(nullptr, &RNA_PreferencesFilePaths, &U);
4141 layout->prop(&pref_ptr, "use_scripts_auto_execute", UI_ITEM_NONE, checkbox_text, ICON_NONE);
4142
4143 layout->separator(2.0f);
4144
4145 /* Buttons. */
4146 uiBut *but;
4147 uiLayout *split = &layout->split(0.0f, true);
4148 uiLayoutSetScaleY(split, 1.2f);
4149
4150 /* Empty space. */
4151 col = &split->column(false);
4152 col->separator();
4153
4154 col = &split->column(false);
4155
4156 /* Allow reload if we have a saved file.
4157 * Otherwise just enable scripts and reset the depsgraphs. */
4158 if ((blendfile_path[0] != '\0') && wm->file_saved) {
4159 but = uiDefIconTextBut(block,
4161 0,
4162 ICON_NONE,
4163 IFACE_("Allow Execution"),
4164 0,
4165 0,
4166 50,
4167 UI_UNIT_Y,
4168 nullptr,
4169 0,
4170 0,
4171 TIP_("Reload file with execution of Python scripts enabled"));
4173 but, [block](bContext &C) { wm_block_autorun_warning_reload_with_scripts(&C, block); });
4174 }
4175 else {
4176 but = uiDefIconTextBut(block,
4178 0,
4179 ICON_NONE,
4180 IFACE_("Allow Execution"),
4181 0,
4182 0,
4183 50,
4184 UI_UNIT_Y,
4185 nullptr,
4186 0,
4187 0,
4188 TIP_("Enable scripts"));
4189 UI_but_func_set(but,
4190 [block](bContext &C) { wm_block_autorun_warning_enable_scripts(&C, block); });
4191 }
4193
4194 col = &split->column(false);
4195 but = uiDefIconTextBut(block,
4197 0,
4198 ICON_NONE,
4199 IFACE_("Ignore"),
4200 0,
4201 0,
4202 50,
4203 UI_UNIT_Y,
4204 nullptr,
4205 0,
4206 0,
4207 TIP_("Continue using file without Python scripts"));
4211
4213
4214 return block;
4215}
4216
4224static struct {
4228
4241
4243{
4246
4247 /* Use regular revert. */
4248 if (ot == nullptr) {
4249 ot = WM_operatortype_find("WM_OT_revert_mainfile", false);
4250 ptr = MEM_new<PointerRNA>(__func__);
4252 RNA_boolean_set(ptr, "use_scripts", true);
4253
4254 /* Set state, so it's freed correctly. */
4256 }
4257
4259 wm_test_autorun_revert_action_set(nullptr, nullptr);
4260}
4261
4263{
4264 /* Test if any auto-execution of scripts failed. */
4265 if ((G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL) == 0) {
4266 return;
4267 }
4268
4269 /* Only show the warning once. */
4271 return;
4272 }
4273
4275
4277 wmWindow *win = (wm->winactive) ? wm->winactive : static_cast<wmWindow *>(wm->windows.first);
4278
4279 if (win) {
4280 /* We want this warning on the Main window, not a child window even if active. See #118765. */
4281 if (win->parent) {
4282 win = win->parent;
4283 }
4284
4285 wmWindow *prevwin = CTX_wm_window(C);
4286 CTX_wm_window_set(C, win);
4288 CTX_wm_window_set(C, prevwin);
4289 }
4290}
4291
4293
4294/* -------------------------------------------------------------------- */
4297
4298static void free_post_file_close_action(void *arg)
4299{
4300 wmGenericCallback *action = (wmGenericCallback *)arg;
4302}
4303
4304static void wm_free_operator_properties_callback(void *user_data)
4305{
4306 IDProperty *properties = (IDProperty *)user_data;
4307 IDP_FreeProperty(properties);
4308}
4309
4310static const char *save_file_overwrite_dialog_name = "save_file_overwrite_popup";
4311
4312static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bmain)
4313{
4314 uiLayout *layout = &parent_layout->column(true);
4315 /* Trick to make both lines of text below close enough to look like they are part of a same
4316 * block. */
4317 uiLayoutSetScaleY(layout, 0.70f);
4318
4320 char writer_ver_str[16];
4321 char current_ver_str[16];
4322 if (bmain->versionfile == BLENDER_VERSION) {
4324 writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile);
4326 current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION);
4327 }
4328 else {
4330 writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
4332 current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1);
4333 }
4334
4335 char message_line1[256];
4336 char message_line2[256];
4337 SNPRINTF(message_line1,
4338 RPT_("This file was saved by a newer version of Blender (%s)."),
4339 writer_ver_str);
4340 SNPRINTF(message_line2,
4341 RPT_("Saving it with this Blender (%s) may cause loss of data."),
4342 current_ver_str);
4343 layout->label(message_line1, ICON_NONE);
4344 layout->label(message_line2, ICON_NONE);
4345 }
4346
4347 if (bmain->is_asset_edit_file) {
4349 layout->separator(1.4f);
4350 }
4351
4352 layout->label(RPT_("This file is managed by the Blender asset system. It can only be"),
4353 ICON_NONE);
4354 layout->label(RPT_("saved as a new, regular file."), ICON_NONE);
4355 }
4356}
4357
4358static void save_file_overwrite_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
4359{
4360 wmWindow *win = CTX_wm_window(C);
4361 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4362}
4363
4365{
4366 uiBut *but = uiDefIconTextBut(
4367 block, UI_BTYPE_BUT, 0, ICON_NONE, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, nullptr, 0, 0, "");
4368 UI_but_func_set(but, save_file_overwrite_cancel, block, post_action);
4370}
4371
4372static void save_file_overwrite_confirm(bContext *C, void *arg_block, void *arg_data)
4373{
4374 wmWindow *win = CTX_wm_window(C);
4375
4376 /* Re-use operator properties as defined for the initial "Save" operator,
4377 * which triggered this "Forward Compatibility" popup. */
4379 static_cast<wmGenericCallback *>(arg_data));
4380
4381 /* Needs to be done after stealing the callback data above, otherwise it would cause a
4382 * use-after-free. */
4383 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4384
4385 PointerRNA operator_propptr = {};
4386 PointerRNA *operator_propptr_p = &operator_propptr;
4387 IDProperty *operator_idproperties = static_cast<IDProperty *>(callback->user_data);
4388 WM_operator_properties_alloc(&operator_propptr_p, &operator_idproperties, "WM_OT_save_mainfile");
4389
4390 WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, operator_propptr_p, nullptr);
4391
4392 WM_generic_callback_free(callback);
4393}
4394
4396{
4397 uiBut *but = uiDefIconTextBut(block,
4399 0,
4400 ICON_NONE,
4401 IFACE_("Overwrite"),
4402 0,
4403 0,
4404 0,
4405 UI_UNIT_Y,
4406 nullptr,
4407 0,
4408 0,
4409 "");
4410 UI_but_func_set(but, save_file_overwrite_confirm, block, post_action);
4413}
4414
4415static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*arg_data*/)
4416{
4417 wmWindow *win = CTX_wm_window(C);
4418 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4419
4420 WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
4421}
4422
4424{
4425 uiBut *but = uiDefIconTextBut(block,
4427 0,
4428 ICON_NONE,
4429 IFACE_("Save As..."),
4430 0,
4431 0,
4432 0,
4433 UI_UNIT_Y,
4434 nullptr,
4435 0,
4436 0,
4437 "");
4438 UI_but_func_set(but, save_file_overwrite_saveas, block, post_action);
4441}
4442
4444{
4445 wmGenericCallback *post_action = static_cast<wmGenericCallback *>(arg1);
4446 Main *bmain = CTX_data_main(C);
4447
4448 uiBlock *block = UI_block_begin(
4453
4454 uiLayout *layout = uiItemsAlertBox(block, 44, ALERT_ICON_WARNING);
4455
4456 /* Title. */
4458 if (bmain->is_asset_edit_file) {
4459 uiItemL_ex(layout,
4460 RPT_("Cannot overwrite asset system files. Save as new file"),
4461 ICON_NONE,
4462 true,
4463 false);
4464 uiItemL_ex(layout, RPT_("with an older Blender version?"), ICON_NONE, true, false);
4465 }
4466 else {
4467 uiItemL_ex(
4468 layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
4469 }
4470 }
4471 else if (bmain->is_asset_edit_file) {
4472 uiItemL_ex(layout,
4473 RPT_("Cannot overwrite asset system files. Save as new file?"),
4474 ICON_NONE,
4475 true,
4476 false);
4477 }
4478 else {
4480 }
4481
4482 /* Filename. */
4483 const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
4484 char filename[FILE_MAX];
4485 if (blendfile_path[0] != '\0') {
4486 BLI_path_split_file_part(blendfile_path, filename, sizeof(filename));
4487 }
4488 else {
4489 SNPRINTF(filename, "%s.blend", DATA_("Untitled"));
4490 /* Since this dialog should only be shown when re-saving an existing file, current filepath
4491 * should never be empty. */
4493 }
4494 layout->label(filename, ICON_NONE);
4495
4496 /* Detailed message info. */
4497 file_overwrite_detailed_info_show(layout, bmain);
4498
4499 layout->separator(4.0f);
4500
4501 /* Buttons. */
4502
4503 uiLayout *split = &layout->split(0.3f, true);
4504 uiLayoutSetScaleY(split, 1.2f);
4505
4506 split->column(false);
4507 /* Asset files don't actually allow overriding. */
4508 const bool allow_overwrite = !bmain->is_asset_edit_file;
4509 if (allow_overwrite) {
4510 save_file_overwrite_confirm_button(block, post_action);
4511 }
4512
4513 uiLayout *split_right = &split->split(0.1f, true);
4514
4515 split_right->column(false);
4516 /* Empty space. */
4517
4518 split_right->column(false);
4519 save_file_overwrite_cancel_button(block, post_action);
4520
4521 split_right->column(false);
4522 save_file_overwrite_saveas_button(block, post_action);
4523
4525 return block;
4526}
4527
4540
4542
4543/* -------------------------------------------------------------------- */
4546
4548
4549static void wm_block_file_close_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
4550{
4551 wmWindow *win = CTX_wm_window(C);
4552 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4553}
4554
4555static void wm_block_file_close_discard(bContext *C, void *arg_block, void *arg_data)
4556{
4558
4559 /* Close the popup before executing the callback. Otherwise
4560 * the popup might be closed by the callback, which will lead
4561 * to a crash. */
4562 wmWindow *win = CTX_wm_window(C);
4563 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4564
4565 callback->exec(C, callback->user_data);
4566 WM_generic_callback_free(callback);
4567}
4568
4569static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_data)
4570{
4571 const Main *bmain = CTX_data_main(C);
4572 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
4574 bool execute_callback = true;
4575
4576 wmWindow *win = CTX_wm_window(C);
4577 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4578
4579 int modified_images_count = ED_image_save_all_modified_info(CTX_data_main(C), nullptr);
4580 if (modified_images_count > 0 && save_images_when_file_is_closed) {
4581 if (ED_image_should_save_modified(bmain)) {
4585 }
4586 else {
4587 execute_callback = false;
4588 }
4589 }
4590
4591 bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0';
4592
4593 if (file_has_been_saved_before) {
4595 /* Need to invoke to get the file-browser and choose where to save the new file.
4596 * This also makes it impossible to keep on going with current operation, which is why
4597 * callback cannot be executed anymore.
4598 *
4599 * This is the same situation as what happens when the file has never been saved before
4600 * (outer `else` statement, below). */
4601 WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
4602 execute_callback = false;
4603 }
4604 else {
4605 if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, nullptr, nullptr) &
4607 {
4608 execute_callback = false;
4609 }
4610 }
4611 }
4612 else {
4613 WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
4614 execute_callback = false;
4615 }
4616
4617 if (execute_callback) {
4618 callback->exec(C, callback->user_data);
4619 }
4620 WM_generic_callback_free(callback);
4621}
4622
4624{
4625 uiBut *but = uiDefIconTextBut(
4626 block, UI_BTYPE_BUT, 0, ICON_NONE, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, nullptr, 0, 0, "");
4627 UI_but_func_set(but, wm_block_file_close_cancel, block, post_action);
4629}
4630
4632{
4633 uiBut *but = uiDefIconTextBut(block,
4635 0,
4636 ICON_NONE,
4637 IFACE_("Don't Save"),
4638 0,
4639 0,
4640 0,
4641 UI_UNIT_Y,
4642 nullptr,
4643 0,
4644 0,
4645 "");
4646 UI_but_func_set(but, wm_block_file_close_discard, block, post_action);
4648}
4649
4651 wmGenericCallback *post_action,
4652 const bool needs_overwrite_confirm)
4653{
4654 uiBut *but = uiDefIconTextBut(
4655 block,
4657 0,
4658 ICON_NONE,
4659 /* Forward compatibility issues force using 'save as' operator instead of 'save' one. */
4660 needs_overwrite_confirm ? IFACE_("Save As...") : IFACE_("Save"),
4661 0,
4662 0,
4663 0,
4664 UI_UNIT_Y,
4665 nullptr,
4666 0,
4667 0,
4668 "");
4669 UI_but_func_set(but, wm_block_file_close_save, block, post_action);
4672}
4673
4674static const char *close_file_dialog_name = "file_close_popup";
4675
4676static void save_catalogs_when_file_is_closed_set_fn(bContext * /*C*/, void *arg1, void * /*arg2*/)
4677{
4678 char *save_catalogs_when_file_is_closed = static_cast<char *>(arg1);
4680 *save_catalogs_when_file_is_closed != 0);
4681}
4682
4684{
4685 using namespace blender;
4686 wmGenericCallback *post_action = (wmGenericCallback *)arg1;
4687 Main *bmain = CTX_data_main(C);
4688
4689 uiBlock *block = UI_block_begin(
4694
4695 uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION);
4696
4697 const bool needs_overwrite_confirm = BKE_main_needs_overwrite_confirm(bmain);
4698
4699 /* Title. */
4700 uiItemL_ex(layout, RPT_("Save changes before closing?"), ICON_NONE, true, false);
4701
4702 /* Filename. */
4703 const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
4704 char filename[FILE_MAX];
4705 if (blendfile_path[0] != '\0') {
4706 BLI_path_split_file_part(blendfile_path, filename, sizeof(filename));
4707 }
4708 else {
4709 SNPRINTF(filename, "%s.blend", DATA_("Untitled"));
4710 }
4711 layout->label(filename, ICON_NONE);
4712
4713 /* Potential forward compatibility issues message. */
4714 if (needs_overwrite_confirm) {
4715 file_overwrite_detailed_info_show(layout, bmain);
4716 }
4717
4718 /* Image Saving Warnings. */
4721 uint modified_images_count = ED_image_save_all_modified_info(bmain, &reports);
4722
4723 LISTBASE_FOREACH (Report *, report, &reports.list) {
4724 uiLayout *row = &layout->column(false);
4725 uiLayoutSetScaleY(row, 0.6f);
4726 row->separator();
4727
4728 /* Error messages created in ED_image_save_all_modified_info() can be long,
4729 * but are made to separate into two parts at first colon between text and paths.
4730 */
4731 char *message = BLI_strdupn(report->message, report->len);
4732 char *path_info = strstr(message, ": ");
4733 if (path_info) {
4734 /* Terminate message string at colon. */
4735 path_info[1] = '\0';
4736 /* Skip over the ": ". */
4737 path_info += 2;
4738 }
4739 uiItemL_ex(row, message, ICON_NONE, false, true);
4740 if (path_info) {
4741 uiItemL_ex(row, path_info, ICON_NONE, false, true);
4742 }
4743 MEM_freeN(message);
4744 }
4745
4746 /* Used to determine if extra separators are needed. */
4747 bool has_extra_checkboxes = false;
4748
4749 /* Modified Images Checkbox. */
4750 if (modified_images_count > 0) {
4751 char message[64];
4752 SNPRINTF(message, "Save %u modified image(s)", modified_images_count);
4753 /* Only the first checkbox should get extra separation. */
4754 if (!has_extra_checkboxes) {
4755 layout->separator();
4756 }
4757 uiDefButBitC(block,
4759 1,
4760 0,
4761 message,
4762 0,
4763 0,
4764 0,
4765 UI_UNIT_Y,
4767 0,
4768 0,
4769 "");
4770 has_extra_checkboxes = true;
4771 }
4772
4774 static char save_catalogs_when_file_is_closed;
4775
4776 save_catalogs_when_file_is_closed = ed::asset::catalogs_get_save_catalogs_when_file_is_saved();
4777
4778 /* Only the first checkbox should get extra separation. */
4779 if (!has_extra_checkboxes) {
4780 layout->separator();
4781 }
4782 uiBut *but = uiDefButBitC(block,
4784 1,
4785 0,
4786 "Save modified asset catalogs",
4787 0,
4788 0,
4789 0,
4790 UI_UNIT_Y,
4791 &save_catalogs_when_file_is_closed,
4792 0,
4793 0,
4794 "");
4795 UI_but_func_set(but,
4797 &save_catalogs_when_file_is_closed,
4798 nullptr);
4799 has_extra_checkboxes = true;
4800 }
4801
4803
4804 layout->separator(2.0f);
4805
4806 /* Buttons. */
4807#ifdef _WIN32
4808 const bool windows_layout = true;
4809#else
4810 const bool windows_layout = false;
4811#endif
4812
4813 if (windows_layout) {
4814 /* Windows standard layout. */
4815
4816 uiLayout *split = &layout->split(0.0f, true);
4817 uiLayoutSetScaleY(split, 1.2f);
4818
4819 split->column(false);
4820 wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm);
4821
4822 split->column(false);
4823 wm_block_file_close_discard_button(block, post_action);
4824
4825 split->column(false);
4826 wm_block_file_close_cancel_button(block, post_action);
4827 }
4828 else {
4829 /* Non-Windows layout (macOS and Linux). */
4830
4831 uiLayout *split = &layout->split(0.3f, true);
4832 uiLayoutSetScaleY(split, 1.2f);
4833
4834 split->column(false);
4835 wm_block_file_close_discard_button(block, post_action);
4836
4837 uiLayout *split_right = &split->split(0.1f, true);
4838
4839 split_right->column(false);
4840 /* Empty space. */
4841
4842 split_right->column(false);
4843 wm_block_file_close_cancel_button(block, post_action);
4844
4845 split_right->column(false);
4846 wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm);
4847 }
4848
4850 return block;
4851}
4852
4863
4865 wmOperator *op,
4866 wmGenericCallbackFn post_action_fn)
4867{
4868 if (U.uiflag & USER_SAVE_PROMPT &&
4870 {
4872 callback->exec = post_action_fn;
4873 callback->user_data = IDP_CopyProperty(op->properties);
4875 wm_close_file_dialog(C, callback);
4876 return true;
4877 }
4878
4879 return false;
4880}
4881
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:1078
void BKE_tempdir_init(const char *userdir)
Definition appdir.cc:1197
#define BLENDER_USERPREF_FILE
@ BLENDER_USER_CONFIG
bool BKE_appdir_app_template_has_userpref(const char *app_template) ATTR_NONNULL(1)
Definition appdir.cc:1092
#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:717
#define BLENDER_STARTUP_FILE
#define BLENDER_QUIT_FILE
const char * BKE_tempdir_base() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
Definition appdir.cc:1249
bool BKE_appdir_app_template_any()
Definition appdir.cc:1073
std::optional< std::string > BKE_appdir_folder_id_create(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:777
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:466
void BKE_blender_userdef_data_set_and_free(UserDef *userdef)
Definition blender.cc:285
#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:153
#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:84
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)
bool CTX_py_init_get(bContext *C)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
wmWindow * CTX_wm_window(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
#define G_FLAG_ALL_RUNTIME
@ G_BACKGROUND_NO_DEPSGRAPH
@ G_FILE_RECOVER_READ
@ G_FILE_AUTOPACK
@ G_FILE_RECOVER_WRITE
@ G_FILE_NO_UI
@ G_FILE_COMPRESS
void IDP_FreeProperty(IDProperty *prop)
Definition idprop.cc:1243
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:873
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:2350
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:822
bool BKE_main_needs_overwrite_confirm(const Main *bmain)
Definition main.cc:474
#define BLEN_THUMB_SIZE
Definition BKE_main.hh:657
ImBuf * BKE_main_thumbnail_to_imbuf(Main *bmain, BlendThumbnail *data)
Definition main.cc:846
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:872
BlendThumbnail * BKE_main_thumbnail_from_buffer(Main *bmain, const uint8_t *rect, const int size[2])
Definition main.cc:800
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:877
bool BKE_main_namemap_validate(Main &bmain)
#define FOREACH_NODETREE_END
Definition BKE_node.hh:866
#define FOREACH_NODETREE_BEGIN(bmain, _nodetree, _id)
Definition BKE_node.hh:856
void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_reports_free(ReportList *reports)
Definition report.cc:70
void BKE_report_print_level_set(ReportList *reports, eReportType level)
Definition report.cc:238
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
void BKE_reports_init(ReportList *reports, int flag)
Definition report.cc:55
void BKE_scene_free_depsgraph_hash(Scene *scene)
Definition scene.cc:3322
ScrArea * BKE_screen_find_big_area(const bScreen *screen, int spacetype, short min)
Definition screen.cc:938
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:840
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:565
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:468
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:805
#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:373
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:576
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:622
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:505
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:57
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:599
int BLI_str_rstrip_digits(char *str) ATTR_NONNULL()
Definition string.cc:1022
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
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:65
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:181
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
@ 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:212
bool ED_editors_flush_edits(Main *bmain)
Definition ed_util.cc:324
void ED_editors_init(bContext *C)
Definition ed_util.cc:88
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()
bool GPU_backend_type_selection_detect()
void GPU_backend_type_selection_set_override(eGPUBackendType backend_type)
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:777
@ 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:486
void IMB_thumb_delete(const char *file_or_lib_path, ThumbSize size)
Definition thumbs.cc:516
@ 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:155
@ PROP_COLLECTION
Definition RNA_types.hh:156
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
@ UI_BLOCK_THEME_STYLE_POPUP
#define UI_UNIT_Y
uiBut * uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_init_userdef()
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
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_TEXT_LEFT
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)
uiBut * uiDefButBitC(uiBlock *block, int 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_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:667
@ UI_BLOCK_NUMSELECT
@ UI_BLOCK_LOOP
@ UI_BLOCK_KEEP_OPEN
@ UI_BLOCK_NO_WIN_CLIP
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BTYPE_BUT
@ UI_BTYPE_CHECKBOX
@ UI_BUT_REDALERT
@ UI_BUT_ACTIVE_DEFAULT
void UI_but_flag_enable(uiBut *but, int flag)
@ ALERT_ICON_WARNING
@ ALERT_ICON_QUESTION
@ ALERT_ICON_ERROR
@ UI_ITEM_R_TOGGLE
void uiLayoutSetActive(uiLayout *layout, bool active)
void uiLayoutSetEnabled(uiLayout *layout, bool enabled)
uiLayout * uiItemsAlertBox(uiBlock *block, const uiStyle *style, const int dialog_width, const eAlertIcon icon, const int icon_size)
void uiLayoutSetScaleY(uiLayout *layout, float scale)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
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:1075
@ FILE_OPENFILE
Definition WM_api.hh:1084
@ FILE_SAVE
Definition WM_api.hh:1085
#define ND_ASSET_LIST_READING
Definition WM_types.hh:547
#define NC_WINDOW
Definition WM_types.hh:372
#define ND_FILEREAD
Definition WM_types.hh:409
#define NC_WM
Definition WM_types.hh:371
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_DATACHANGED
Definition WM_types.hh:411
ReportList * reports
Definition WM_types.hh:1025
#define NC_ASSET
Definition WM_types.hh:401
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:245
#define ND_FILESAVE
Definition WM_types.hh:410
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 abs
#define printf(...)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ListBase R_engines
#define LOG(severity)
Definition log.h:32
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)
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)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_int_get(PointerRNA *ptr, const char *name)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
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::@365377131325274324253051140175317050064341373234 count
int resynced_lib_overrides_libraries_count
struct BlendFileReadReport::@140267113215111030100161154125212310177247344001 duration
wmWindowManager * old_wm
const BlendThumbnail * thumb
eBLO_WritePathRemap remap_mode
FileReaderSeekFn seek
FileReaderCloseFn close
FileReaderReadFn read
bool is_untrusted
Definition wm_files.cc:3297
IDProperty * metadata
char filepath[1024]
Definition DNA_ID.h:507
struct LinkNode * next
void * first
ListBase scenes
Definition BKE_main.hh:245
ListBase wm
Definition BKE_main.hh:276
short subversionfile
Definition BKE_main.hh:156
bool is_asset_edit_file
Definition BKE_main.hh:171
bool has_forward_compatibility_issues
Definition BKE_main.hh:164
char filepath[1024]
Definition BKE_main.hh:155
bool recovered
Definition BKE_main.hh:178
BlendThumbnail * blen_thumb
Definition BKE_main.hh:231
ListBase libraries
Definition BKE_main.hh:246
ListBase screens
Definition BKE_main.hh:261
short versionfile
Definition BKE_main.hh:156
ListBase workspaces
Definition BKE_main.hh:284
wmOperatorStatus(* run)(bContext *C, wmOperator *op)
Definition wm_files.cc:3118
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
RecentFile * next
Definition WM_types.hh:1445
char * filepath
Definition WM_types.hh:1446
struct Object * camera
ListBase spacedata
UserDef_Runtime runtime
View3D_Runtime runtime
struct View3D * localvd
View3DShading shading
Wrapper for bScreen.
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 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:1032
wmOperatorStatus(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1062
struct ReportList * reports
IDProperty * properties
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
struct wmKeyConfig * defaultconf
struct wmKeyConfig * userconf
struct wmWindow * windrawable
struct wmKeyConfig * addonconf
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:570
void wm_clear_default_size(bContext *C)
Definition wm.cc:510
void WM_check(bContext *C)
Definition wm.cc:471
void WM_keyconfig_reload(bContext *C)
Definition wm.cc:422
void wm_add_default(Main *bmain, bContext *C)
Definition wm.cc:532
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:1298
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)
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
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_with_properties(bContext *C, const char *opstring, wmOperatorCallContext 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:2339
static void free_post_file_close_action(void *arg)
Definition wm_files.cc:4298
void WM_file_autosave_init(wmWindowManager *wm)
Definition wm_files.cc:2382
bool WM_autosave_is_scheduled(wmWindowManager *wm)
Definition wm_files.cc:2334
bool WM_file_read(bContext *C, const char *filepath, const bool use_scripts_autoexec_check, ReportList *reports)
Definition wm_files.cc:1046
void wm_homefile_read(bContext *C, const wmHomeFileRead_Params *params_homefile, ReportList *reports)
Definition wm_files.cc:1583
void wm_autosave_timer_end(wmWindowManager *wm)
Definition wm_files.cc:2374
static void create_operator_state(wmOperatorType *ot, int first_state)
Definition wm_files.cc:3098
#define BKE_READ_EXOTIC_OK_BLEND
Definition wm_files.cc:554
bool wm_operator_close_file_dialog_if_needed(bContext *C, wmOperator *op, wmGenericCallbackFn post_action_fn)
Definition wm_files.cc:4864
static void wm_init_userdef(Main *bmain)
Definition wm_files.cc:509
static std::string wm_save_mainfile_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition wm_files.cc:3925
static wmOperatorStatus wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3506
static bool wm_save_mainfile_check(bContext *, wmOperator *op)
Definition wm_files.cc:3812
static wmOperatorStatus wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3244
static wmOperatorStatus wm_homefile_read_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2838
static uiBlock * block_create__close_file_dialog(bContext *C, ARegion *region, void *arg1)
Definition wm_files.cc:4683
static BlendFileReadWMSetupData * wm_file_read_setup_wm_init(bContext *C, Main *bmain, const bool is_read_homefile)
Definition wm_files.cc:208
void WM_OT_read_history(wmOperatorType *ot)
Definition wm_files.cc:2817
static wmOperatorStatus wm_history_file_read_exec(bContext *, wmOperator *)
Definition wm_files.cc:2810
static void wm_open_mainfile_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:3150
static bool wm_file_write_check_with_report_on_failure(Main *bmain, const char *filepath, ReportList *reports)
Definition wm_files.cc:2086
#define BKE_READ_EXOTIC_FAIL_FORMAT
Definition wm_files.cc:552
void wm_test_autorun_revert_action_set(wmOperatorType *ot, PointerRNA *ptr)
Definition wm_files.cc:4229
#define BKE_READ_EXOTIC_FAIL_OPEN
Definition wm_files.cc:553
ClearRecentInclude
Definition wm_files.cc:3982
@ CLEAR_RECENT_MISSING
Definition wm_files.cc:3982
@ CLEAR_RECENT_ALL
Definition wm_files.cc:3982
static uiBlock * block_create_autorun_warning(bContext *C, ARegion *region, void *)
Definition wm_files.cc:4097
bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWindowManager *wm)
Definition wm_files.cc:184
static ImBuf * blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **r_thumb)
Definition wm_files.cc:1871
static bool wm_file_read_opwrap(bContext *C, const char *filepath, const bool use_scripts_autoexec_check, ReportList *reports)
Definition wm_files.cc:3082
void WM_OT_save_as_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3846
void WM_OT_open_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3362
void WM_file_tag_modified()
Definition wm_files.cc:174
static wmOperatorStatus wm_recover_last_session_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3494
static void wm_block_file_close_cancel_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4623
static RecentFile * wm_file_history_find(const char *filepath)
Definition wm_files.cc:1663
const char * WM_init_state_app_template_get()
Definition wm_files.cc:1199
PointerRNA * ptr
Definition wm_files.cc:4226
void wm_file_read_report(Main *bmain, wmWindow *win)
Definition wm_files.cc:653
void wm_autosave_timer_begin(wmWindowManager *wm)
Definition wm_files.cc:2369
static void rna_struct_update_when_changed(bContext *C, Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b)
Definition wm_files.cc:2657
void WM_OT_recover_last_session(wmOperatorType *ot)
Definition wm_files.cc:3522
static void wm_file_read_pre(bool use_data, bool)
Definition wm_files.cc:685
static wmOperatorStatus wm_open_mainfile__select_file_path_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3171
static blender::int2 blend_file_thumb_clamp_size(const int size[2], const int limit)
Definition wm_files.cc:1854
static void wm_block_autorun_warning_enable_scripts(bContext *C, uiBlock *block)
Definition wm_files.cc:4078
static void save_file_overwrite_saveas(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4415
static void wm_history_files_free()
Definition wm_files.cc:1656
static RecentFile * wm_history_file_new(const char *filepath)
Definition wm_files.cc:1642
static bool wm_revert_mainfile_poll(bContext *)
Definition wm_files.cc:3439
static void wm_read_callback_pre_wrapper(bContext *C, const char *filepath)
Definition wm_files.cc:885
static wmOperatorStatus wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3570
void wm_history_file_read()
Definition wm_files.cc:1610
void wm_autosave_delete()
Definition wm_files.cc:2413
static bool wm_open_mainfile_check(bContext *, wmOperator *op)
Definition wm_files.cc:3301
static ImBuf * blend_file_thumb_from_camera(const bContext *C, Scene *scene, bScreen *screen, BlendThumbnail **r_thumb)
Definition wm_files.cc:1943
static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:2946
static OperatorDispatchTarget wm_open_mainfile_dispatch_targets[]
Definition wm_files.cc:3232
#define USERDEF_RESTORE(member)
static void wm_userpref_read_exceptions(UserDef *userdef_curr, const UserDef *userdef_prev)
Definition wm_files.cc:2643
static wmOperatorStatus wm_open_mainfile__open(bContext *C, wmOperator *op)
Definition wm_files.cc:3203
void WM_OT_read_userpref(wmOperatorType *ot)
Definition wm_files.cc:2751
static void wm_test_autorun_revert_action_exec(bContext *C)
Definition wm_files.cc:4242
static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:3500
static char save_images_when_file_is_closed
Definition wm_files.cc:4547
static void save_file_overwrite_cancel_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4364
static void wm_block_file_close_save_button(uiBlock *block, wmGenericCallback *post_action, const bool needs_overwrite_confirm)
Definition wm_files.cc:4650
static wmOperatorStatus wm_userpref_read_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2761
void WM_OT_save_homefile(wmOperatorType *ot)
Definition wm_files.cc:2595
void WM_OT_read_factory_userpref(wmOperatorType *ot)
Definition wm_files.cc:2790
static void wm_file_read_setup_wm_finalize(bContext *C, Main *bmain, BlendFileReadWMSetupData *wm_setup_data)
Definition wm_files.cc:429
static int get_operator_state(wmOperator *op)
Definition wm_files.cc:3106
static wmOperatorStatus wm_userpref_write_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2612
void WM_init_state_app_template_set(const char *app_template)
Definition wm_files.cc:1187
static void save_set_compress(wmOperator *op)
Definition wm_files.cc:3622
@ OPEN_MAINFILE_STATE_OPEN
Definition wm_files.cc:3145
@ OPEN_MAINFILE_STATE_DISCARD_CHANGES
Definition wm_files.cc:3143
@ OPEN_MAINFILE_STATE_SELECT_FILE_PATH
Definition wm_files.cc:3144
static wmOperatorStatus wm_revert_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3420
void WM_OT_save_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3937
void WM_OT_recover_auto_save(wmOperatorType *ot)
Definition wm_files.cc:3585
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:1210
static std::string wm_save_as_mainfile_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition wm_files.cc:3835
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:317
static wmOperatorStatus wm_open_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3251
char app_template[64]
Definition wm_files.cc:1183
static wmOperatorStatus wm_read_factory_settings_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3024
static void wm_block_file_close_discard(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4555
void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *)
Definition wm_files.cc:2387
static void wm_history_file_write()
Definition wm_files.cc:1673
static void set_next_operator_state(wmOperator *op, int state)
Definition wm_files.cc:3111
static struct @250313253105251274362100312377155072337054270112 wm_test_autorun_revert_action_data
void WM_file_autoexec_init(const char *filepath)
Definition wm_files.cc:638
static wmOperatorStatus wm_recover_auto_save_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3540
void wm_homefile_read_post(bContext *C, const wmFileReadPost_Params *params_file_read_post)
Definition wm_files.cc:1590
static wmOperatorStatus wm_open_mainfile__discard_changes_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3156
static void wm_file_read_post(bContext *C, const char *filepath, const wmFileReadPost_Params *params)
Definition wm_files.cc:719
static void wm_userpref_update_when_changed(bContext *C, Main *bmain, UserDef *userdef_prev, UserDef *userdef_curr)
Definition wm_files.cc:2691
static const char * close_file_dialog_name
Definition wm_files.cc:4674
static void wm_autosave_location(char filepath[FILE_MAX])
Definition wm_files.cc:2289
static wmOperatorStatus wm_clear_recent_files_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition wm_files.cc:3990
static std::string wm_open_mainfile_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition wm_files.cc:3256
static void save_catalogs_when_file_is_closed_set_fn(bContext *, void *arg1, void *)
Definition wm_files.cc:4676
static wmOperatorStatus operator_state_dispatch(bContext *C, wmOperator *op, OperatorDispatchTarget *targets)
Definition wm_files.cc:3121
static void wm_filepath_default(const Main *bmain, char *filepath)
Definition wm_files.cc:3613
void WM_OT_read_factory_settings(wmOperatorType *ot)
Definition wm_files.cc:3056
static wmOperatorStatus wm_homefile_write_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2566
static bool wm_autosave_write_try(Main *bmain, wmWindowManager *wm)
Definition wm_files.cc:2312
static int wm_read_exotic(const char *filepath)
Definition wm_files.cc:569
static void wm_block_autorun_warning_reload_with_scripts(bContext *C, uiBlock *block)
Definition wm_files.cc:4062
static wmOperatorStatus wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3672
static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bmain)
Definition wm_files.cc:4312
void wm_test_autorun_warning(bContext *C)
Definition wm_files.cc:4262
static void wm_block_file_close_cancel(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4549
static wmOperatorStatus wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3883
wmOperatorType * ot
Definition wm_files.cc:4225
static wmOperatorStatus wm_recover_last_session_impl(bContext *C, wmOperator *op, const bool use_scripts_autoexec_check)
Definition wm_files.cc:3476
static void wm_file_read_setup_wm_substitute_old_window(wmWindowManager *oldwm, wmWindowManager *wm, wmWindow *oldwin, wmWindow *win)
Definition wm_files.cc:271
static wmOperatorStatus wm_userpref_read_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2708
void wm_open_init_load_ui(wmOperator *op, bool use_prefs)
Definition wm_files.cc:2463
static wmOperatorStatus wm_open_mainfile_dispatch(bContext *C, wmOperator *op)
Definition wm_files.cc:3239
static void read_factory_reset_props(wmOperatorType *ot)
Definition wm_files.cc:2440
static void wm_read_callback_post_wrapper(bContext *C, const char *filepath, const bool success)
Definition wm_files.cc:892
static wmOperatorStatus wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2952
static struct @213226156245272227266077231255056145011327364147 wm_init_state_app_template
static void wm_clear_recent_files_ui(bContext *, wmOperator *op)
Definition wm_files.cc:4018
static void wm_block_autorun_warning_ignore(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4053
static void wm_history_file_free(RecentFile *recent)
Definition wm_files.cc:1649
static void wm_autosave_timer_begin_ex(wmWindowManager *wm, double timestep)
Definition wm_files.cc:2360
static const EnumPropertyItem prop_clear_recent_types[]
Definition wm_files.cc:3984
static void save_file_overwrite_confirm_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4395
static void wm_open_mainfile_def_property_use_scripts(wmOperatorType *ot)
Definition wm_files.cc:3352
#define BKE_READ_EXOTIC_FAIL_PATH
Definition wm_files.cc:551
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:2121
static void file_read_reports_finalize(BlendFileReadReport *bf_reports)
Definition wm_files.cc:921
static void wm_free_operator_properties_callback(void *user_data)
Definition wm_files.cc:4304
static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4569
static void save_set_filepath(bContext *C, wmOperator *op)
Definition wm_files.cc:3638
static wmOperatorStatus wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3691
static uiBlock * block_create_save_file_overwrite_dialog(bContext *C, ARegion *region, void *arg1)
Definition wm_files.cc:4443
static void wm_file_read_setup_wm_use_new(bContext *C, Main *, BlendFileReadWMSetupData *wm_setup_data, wmWindowManager *wm)
Definition wm_files.cc:368
void WM_OT_read_homefile(wmOperatorType *ot)
Definition wm_files.cc:2980
static std::string wm_save_as_mainfile_get_name(wmOperatorType *ot, PointerRNA *ptr)
Definition wm_files.cc:3827
static wmOperatorStatus wm_homefile_write_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2500
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:1764
static void wm_block_file_close_discard_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4631
static void wm_open_mainfile_ui(bContext *, wmOperator *op)
Definition wm_files.cc:3331
static void wm_history_file_update()
Definition wm_files.cc:1699
bool write_crash_blend()
Definition wm_files.cc:2070
static wmOperatorStatus wm_revert_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3401
static void wm_gpu_backend_override_from_userdef()
Definition wm_files.cc:495
void WM_OT_save_userpref(wmOperatorType *ot)
Definition wm_files.cc:2624
static void save_file_overwrite_confirm(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4372
void WM_OT_clear_recent_files(wmOperatorType *ot)
Definition wm_files.cc:4029
bool WM_file_recover_last_session(bContext *C, const bool use_scripts_autoexec_check, ReportList *reports)
Definition wm_files.cc:3464
static void read_homefile_props(wmOperatorType *ot)
Definition wm_files.cc:2962
void wm_save_file_overwrite_dialog(bContext *C, wmOperator *op)
Definition wm_files.cc:4528
static const char * save_file_overwrite_dialog_name
Definition wm_files.cc:4310
static void save_file_overwrite_cancel(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4358
static wmOperatorStatus wm_clear_recent_files_exec(bContext *, wmOperator *op)
Definition wm_files.cc:3998
static void save_file_overwrite_saveas_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4423
void WM_OT_revert_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3445
void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action)
Definition wm_files.cc:4853
bool wm_open_init_use_scripts(wmOperator *op, bool use_prefs)
Definition wm_files.cc:2473
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:577
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_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)
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)
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:433
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)