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 true,
2018 err_out);
2019 }
2020
2021 U.pixelsize = pixelsize_old;
2022
2023 /* Reset to old drawable. */
2024 if (windrawable_old) {
2025 wm_window_make_drawable(wm, windrawable_old);
2026 }
2027 else {
2029 }
2030
2031 if (ibuf) {
2032 /* Dirty oversampling. */
2033 ImBuf *thumb_ibuf;
2034 thumb_ibuf = IMB_dupImBuf(ibuf);
2035
2036 /* Save metadata for quick access. */
2037 char version_str[10];
2038 SNPRINTF(version_str, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100);
2040 IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_str);
2041
2042 /* BLEN_THUMB_SIZE is size of thumbnail inside blend file: 128x128. */
2044 thumb = BKE_main_thumbnail_from_imbuf(nullptr, thumb_ibuf);
2045 IMB_freeImBuf(thumb_ibuf);
2046 /* Thumbnail saved to file-system should be 256x256. */
2047 IMB_scale(ibuf,
2051 false);
2052 }
2053 else {
2054 /* '*r_thumb' needs to stay nullptr to prevent a bad thumbnail from being handled. */
2055 CLOG_WARN(&LOG, "failed to create thumbnail: %s", err_out);
2056 thumb = nullptr;
2057 }
2058
2059 /* Must be freed by caller. */
2060 *r_thumb = thumb;
2061
2062 return ibuf;
2063}
2064
2066
2067/* -------------------------------------------------------------------- */
2070
2072{
2073 char filepath[FILE_MAX];
2074
2076 BLI_path_extension_replace(filepath, sizeof(filepath), "_crash.blend");
2078 const bool success = BLO_write_file(G_MAIN, filepath, G.fileflags, &params, nullptr);
2079 printf("%s: \"%s\"\n", success ? "written" : "failed", filepath);
2080 return success;
2081}
2082
2088 const char *filepath,
2090{
2091 const int filepath_len = strlen(filepath);
2092 if (filepath_len == 0) {
2093 BKE_report(reports, RPT_ERROR, "Path is empty, cannot save");
2094 return false;
2095 }
2096
2097 if (filepath_len >= FILE_MAX) {
2098 BKE_report(reports, RPT_ERROR, "Path too long, cannot save");
2099 return false;
2100 }
2101
2102 if (bmain->is_asset_edit_file &&
2104 {
2105 BKE_report(reports, RPT_ERROR, "Cannot overwrite files that are managed by the asset system");
2106 return false;
2107 }
2108
2109 LISTBASE_FOREACH (Library *, li, &bmain->libraries) {
2110 if (BLI_path_cmp(li->runtime->filepath_abs, filepath) == 0) {
2111 BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath);
2112 return false;
2113 }
2114 }
2115
2116 return true;
2117}
2118
2123 const char *filepath,
2124 int fileflags,
2125 eBLO_WritePathRemap remap_mode,
2126 bool use_save_as_copy,
2128{
2129 Main *bmain = CTX_data_main(C);
2130 BlendThumbnail *thumb = nullptr, *main_thumb = nullptr;
2131 ImBuf *ibuf_thumb = nullptr;
2132
2133 /* NOTE: used to replace the file extension (to ensure `.blend`),
2134 * no need to now because the operator ensures,
2135 * its handy for scripts to save to a predefined name without blender editing it. */
2136
2137 if (!wm_file_write_check_with_report_on_failure(bmain, filepath, reports)) {
2138 return false;
2139 }
2140
2141 /* Call pre-save callbacks before writing preview,
2142 * that way you can generate custom file thumbnail. */
2143
2144 /* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
2145 * Runs at the end of this function, don't return beforehand. */
2147
2148 /* Check if file write permission is OK. */
2149 if (const int st_mode = BLI_exists(filepath)) {
2150 bool ok = true;
2151
2152 if (!BLI_file_is_writable(filepath)) {
2154 reports, RPT_ERROR, "Cannot save blend file, path \"%s\" is not writable", filepath);
2155 ok = false;
2156 }
2157 else if (S_ISDIR(st_mode)) {
2158 /* While the UI mostly prevents this, it's possible to save to an existing
2159 * directory from Python because the path is used unmodified.
2160 * Besides it being unlikely the user wants to replace a directory,
2161 * the file versioning logic (to create `*.blend1` files)
2162 * would rename the directory with a `1` suffix, see #134101. */
2164 reports, RPT_ERROR, "Cannot save blend file, path \"%s\" is a directory", filepath);
2165 ok = false;
2166 }
2167
2168 if (!ok) {
2170 return false;
2171 }
2172 }
2173
2175
2176 /* Enforce full override check/generation on file save. */
2178
2179 /* NOTE: Ideally we would call `WM_redraw_windows` here to remove any open menus.
2180 * But we can crash if saving from a script, see #92704 & #97627.
2181 * Just checking `!G.background && BLI_thread_is_main()` is not sufficient to fix this.
2182 * Additionally some EGL configurations don't support reading the front-buffer
2183 * immediately after drawing, see: #98462. In that case off-screen drawing is necessary. */
2184
2185 /* Don't forget not to return without! */
2186 WM_cursor_wait(true);
2187
2188 if (U.file_preview_type != USER_FILE_PREVIEW_NONE) {
2189 /* Blend file thumbnail.
2190 *
2191 * - Save before exiting edit-mode, otherwise evaluated-mesh for shared data gets corrupted.
2192 * See #27765.
2193 * - Main can store a `.blend` thumbnail,
2194 * useful for background-mode or thumbnail customization.
2195 */
2196 main_thumb = thumb = bmain->blen_thumb;
2197 if (thumb != nullptr) {
2198 /* In case we are given a valid thumbnail data, just generate image from it. */
2199 ibuf_thumb = BKE_main_thumbnail_to_imbuf(nullptr, thumb);
2200 }
2201 else if (BLI_thread_is_main()) {
2202 int file_preview_type = U.file_preview_type;
2203
2204 if (file_preview_type == USER_FILE_PREVIEW_AUTO) {
2205 Scene *scene = CTX_data_scene(C);
2206 bScreen *screen = CTX_wm_screen(C);
2207 bool do_render = (scene != nullptr && scene->camera != nullptr && screen != nullptr &&
2208 (BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0) != nullptr));
2209 file_preview_type = do_render ? USER_FILE_PREVIEW_CAMERA : USER_FILE_PREVIEW_SCREENSHOT;
2210 }
2211
2212 switch (file_preview_type) {
2214 ibuf_thumb = blend_file_thumb_from_screenshot(C, &thumb);
2215 break;
2216 }
2218 ibuf_thumb = blend_file_thumb_from_camera(
2219 C, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
2220 break;
2221 }
2222 default:
2224 }
2225 }
2226 }
2227
2228 /* Operator now handles overwrite checks. */
2229
2230 if (G.fileflags & G_FILE_AUTOPACK) {
2231 BKE_packedfile_pack_all(bmain, reports, false);
2232 }
2233
2235
2236 /* XXX(ton): temp solution to solve bug, real fix coming. */
2237 bmain->recovered = false;
2238
2239 BlendFileWriteParams blend_write_params{};
2240 blend_write_params.remap_mode = remap_mode;
2241 blend_write_params.use_save_versions = true;
2242 blend_write_params.use_save_as_copy = use_save_as_copy;
2243 blend_write_params.thumb = thumb;
2244
2245 const bool success = BLO_write_file(bmain, filepath, fileflags, &blend_write_params, reports);
2246
2247 if (success) {
2248 const bool do_history_file_update = (G.background == false) &&
2249 (CTX_wm_manager(C)->op_undo_depth == 0);
2250
2251 if (use_save_as_copy == false) {
2252 STRNCPY(bmain->filepath, filepath); /* Is guaranteed current file. */
2253 }
2254
2255 SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
2256
2257 /* Prevent background mode scripts from clobbering history. */
2258 if (do_history_file_update) {
2260 }
2261
2262 /* Run this function after because the file can't be written before the blend is. */
2263 if (ibuf_thumb) {
2264 IMB_thumb_delete(filepath, THB_FAIL); /* Without this a failed thumb overrides. */
2265 ibuf_thumb = IMB_thumb_create(filepath, THB_LARGE, THB_SOURCE_BLEND, ibuf_thumb);
2266 }
2267 }
2268
2270 bmain, success ? BKE_CB_EVT_SAVE_POST : BKE_CB_EVT_SAVE_POST_FAIL, filepath);
2271
2272 if (ibuf_thumb) {
2273 IMB_freeImBuf(ibuf_thumb);
2274 }
2275 if (thumb && thumb != main_thumb) {
2276 MEM_freeN(thumb);
2277 }
2278
2279 WM_cursor_wait(false);
2280
2281 return success;
2282}
2283
2285
2286/* -------------------------------------------------------------------- */
2289
2290static void wm_autosave_location(char filepath[FILE_MAX])
2291{
2292 const int pid = abs(getpid());
2293 char filename[1024];
2294
2295 /* Normally there is no need to check for this to be nullptr,
2296 * however this runs on exit when it may be cleared. */
2297 Main *bmain = G_MAIN;
2298 const char *blendfile_path = bmain ? BKE_main_blendfile_path(bmain) : nullptr;
2299
2300 if (blendfile_path && (blendfile_path[0] != '\0')) {
2301 const char *basename = BLI_path_basename(blendfile_path);
2302 int len = strlen(basename) - 6;
2303 SNPRINTF(filename, "%.*s_%d_autosave.blend", len, basename, pid);
2304 }
2305 else {
2306 SNPRINTF(filename, "%d_autosave.blend", pid);
2307 }
2308
2309 const char *tempdir_base = BKE_tempdir_base();
2310 BLI_path_join(filepath, FILE_MAX, tempdir_base, filename);
2311}
2312
2314{
2315 char filepath[FILE_MAX];
2316
2317 wm_autosave_location(filepath);
2318
2319 /* Technically, we could always just save here, but that would cause performance regressions
2320 * compared to when the #MemFile undo step was used for saving undo-steps. So for now just skip
2321 * auto-save when we are in a mode where auto-save wouldn't have worked previously anyway. This
2322 * check can be removed once the performance regressions have been solved. */
2323 if (ED_undosys_stack_memfile_get_if_active(wm->undo_stack) != nullptr) {
2324 WM_autosave_write(wm, bmain);
2325 return true;
2326 }
2327 if ((U.uiflag & USER_GLOBALUNDO) == 0) {
2328 WM_autosave_write(wm, bmain);
2329 return true;
2330 }
2331 /* Can't auto-save with MemFile right now, try again later. */
2332 return false;
2333}
2334
2336{
2337 return wm->autosave_scheduled;
2338}
2339
2341{
2343
2344 char filepath[FILE_MAX];
2345 wm_autosave_location(filepath);
2346 /* Save as regular blend file with recovery information and always compress them, see: !132685.
2347 */
2348 const int fileflags = G.fileflags | G_FILE_RECOVER_WRITE | G_FILE_COMPRESS;
2349
2350 /* Error reporting into console. */
2352 BLO_write_file(bmain, filepath, fileflags, &params, nullptr);
2353
2354 /* Restart auto-save timer. */
2357
2358 wm->autosave_scheduled = false;
2359}
2360
2361static void wm_autosave_timer_begin_ex(wmWindowManager *wm, double timestep)
2362{
2364
2365 if (U.flag & USER_AUTOSAVE) {
2366 wm->autosavetimer = WM_event_timer_add(wm, nullptr, TIMERAUTOSAVE, timestep);
2367 }
2368}
2369
2374
2376{
2377 if (wm->autosavetimer) {
2378 WM_event_timer_remove(wm, nullptr, wm->autosavetimer);
2379 wm->autosavetimer = nullptr;
2380 }
2381}
2382
2387
2389{
2391
2392 /* If a modal operator is running, don't autosave because we might not be in
2393 * a valid state to save. But try again in 10ms. */
2394 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
2395 LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
2396 if (handler_base->type == WM_HANDLER_TYPE_OP) {
2397 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
2398 if (handler->op) {
2400 return;
2401 }
2402 }
2403 }
2404 }
2405
2406 wm->autosave_scheduled = false;
2407 if (!wm_autosave_write_try(bmain, wm)) {
2408 wm->autosave_scheduled = true;
2409 }
2410 /* Restart the timer after file write, just in case file write takes a long time. */
2412}
2413
2415{
2416 char filepath[FILE_MAX];
2417
2418 wm_autosave_location(filepath);
2419
2420 if (BLI_exists(filepath)) {
2421 char filepath_quit[FILE_MAX];
2422 BLI_path_join(filepath_quit, sizeof(filepath_quit), BKE_tempdir_base(), BLENDER_QUIT_FILE);
2423
2424 /* For global undo; remove temporarily saved file, otherwise rename. */
2425 if (U.uiflag & USER_GLOBALUNDO) {
2426 BLI_delete(filepath, false, false);
2427 }
2428 else {
2429 BLI_rename_overwrite(filepath, filepath_quit);
2430 }
2431 }
2432}
2433
2435
2436/* -------------------------------------------------------------------- */
2439
2442{
2443 PropertyRNA *prop;
2444
2445 /* So it's possible to reset app-template settings without resetting other defaults. */
2446 prop = RNA_def_boolean(ot->srna,
2447 "use_factory_startup_app_template_only",
2448 false,
2449 "Factory Startup App-Template Only",
2450 "");
2452}
2453
2455
2456/* -------------------------------------------------------------------- */
2463
2464void wm_open_init_load_ui(wmOperator *op, bool use_prefs)
2465{
2466 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "load_ui");
2467 if (!RNA_property_is_set(op->ptr, prop)) {
2468 bool value = use_prefs ? ((U.flag & USER_FILENOUI) == 0) : ((G.fileflags & G_FILE_NO_UI) == 0);
2469
2470 RNA_property_boolean_set(op->ptr, prop, value);
2471 }
2472}
2473
2474bool wm_open_init_use_scripts(wmOperator *op, bool use_prefs)
2475{
2476 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
2477 bool use_scripts_autoexec_check = false;
2478 if (!RNA_property_is_set(op->ptr, prop)) {
2479 /* Use #G_FLAG_SCRIPT_AUTOEXEC rather than the userpref because this means if
2480 * the flag has been disabled from the command line, then opening
2481 * from the menu won't enable this setting. */
2482 bool value = use_prefs ? ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) :
2483 ((G.f & G_FLAG_SCRIPT_AUTOEXEC) != 0);
2484
2485 RNA_property_boolean_set(op->ptr, prop, value);
2486 use_scripts_autoexec_check = true;
2487 }
2488 return use_scripts_autoexec_check;
2489}
2490
2492
2493/* -------------------------------------------------------------------- */
2496
2502{
2503 Main *bmain = CTX_data_main(C);
2505 wmWindow *win = CTX_wm_window(C);
2506 char filepath[FILE_MAX];
2507 int fileflags;
2508
2509 const char *app_template = U.app_template[0] ? U.app_template : nullptr;
2510 const std::optional<std::string> cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG,
2511 app_template);
2512 if (!cfgdir.has_value()) {
2513 BKE_report(op->reports, RPT_ERROR, "Unable to create user config path");
2514 return OPERATOR_CANCELLED;
2515 }
2516
2517 /* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
2518 * Runs at the end of this function, don't return beforehand. */
2521
2522 /* Check current window and close it if temp. */
2523 if (win && WM_window_is_temp_screen(win)) {
2524 wm_window_close(C, wm, win);
2525 }
2526
2527 /* Update keymaps in user preferences. */
2529
2530 BLI_path_join(filepath, sizeof(filepath), cfgdir->c_str(), BLENDER_STARTUP_FILE);
2531
2532 if (!G.quiet) {
2533 printf("Writing homefile: \"%s\" ", filepath);
2534 }
2535
2537
2538 /* Force save as regular blend file. */
2539 fileflags = G.fileflags & ~G_FILE_COMPRESS;
2540
2541 BlendFileWriteParams blend_write_params{};
2542 /* Make all paths absolute when saving the startup file.
2543 * On load the `G.main->filepath` will be empty so the paths
2544 * won't have a base for resolving the relative paths. */
2545 blend_write_params.remap_mode = BLO_WRITE_PATH_REMAP_ABSOLUTE;
2546 /* Don't apply any path changes to the current blend file. */
2547 blend_write_params.use_save_as_copy = true;
2548
2549 const bool success = BLO_write_file(
2550 bmain, filepath, fileflags, &blend_write_params, op->reports);
2551
2553
2554 if (success) {
2555 if (!G.quiet) {
2556 printf("ok\n");
2557 }
2558 BKE_report(op->reports, RPT_INFO, "Startup file saved");
2559 return OPERATOR_FINISHED;
2560 }
2561 if (!G.quiet) {
2562 printf("fail\n");
2563 }
2564 return OPERATOR_CANCELLED;
2565}
2566
2568 wmOperator *op,
2569 const wmEvent * /*event*/)
2570{
2571 if (!U.app_template[0]) {
2573 op,
2574 IFACE_("Overwrite Startup File"),
2575 IFACE_("Blender will start next time as it is now."),
2576 IFACE_("Overwrite"),
2578 false);
2579 }
2580
2581 /* A different message if this is overriding a specific template startup file. */
2582 char display_name[FILE_MAX];
2583 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
2584 std::string message = fmt::format(
2585 fmt::runtime(IFACE_("Template \"{}\" will start next time as it is now.")),
2586 IFACE_(display_name));
2588 op,
2589 IFACE_("Overwrite Template Startup File"),
2590 message.c_str(),
2591 IFACE_("Overwrite"),
2593 false);
2594}
2595
2597{
2598 ot->name = "Save Startup File";
2599 ot->idname = "WM_OT_save_homefile";
2600 ot->description = "Make the current file the default startup file";
2601
2602 ot->invoke = wm_homefile_write_invoke;
2603 ot->exec = wm_homefile_write_exec;
2604}
2605
2607
2608/* -------------------------------------------------------------------- */
2611
2612/* Only save the prefs block. operator entry. */
2614{
2616
2617 /* Update keymaps in user preferences. */
2619
2620 const bool success = BKE_blendfile_userdef_write_all(op->reports);
2621
2622 return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2623}
2624
2626{
2627 ot->name = "Save Preferences";
2628 ot->idname = "WM_OT_save_userpref";
2629 ot->description = "Make the current preferences default";
2630
2631 ot->invoke = WM_operator_confirm;
2632 ot->exec = wm_userpref_write_exec;
2633}
2634
2636
2637/* -------------------------------------------------------------------- */
2640
2644static void wm_userpref_read_exceptions(UserDef *userdef_curr, const UserDef *userdef_prev)
2645{
2646#define USERDEF_RESTORE(member) \
2647 { \
2648 userdef_curr->member = userdef_prev->member; \
2649 } \
2650 ((void)0)
2651
2652 /* Current visible preferences category. */
2653 USERDEF_RESTORE(space_data.section_active);
2654
2655#undef USERDEF_RESTORE
2656}
2657
2659 Main *bmain,
2660 PointerRNA *ptr_a,
2661 PointerRNA *ptr_b)
2662{
2664 PropertyRNA *iterprop = RNA_struct_iterator_property(ptr_a->type);
2665 BLI_assert(ptr_a->type == ptr_b->type);
2666 RNA_property_collection_begin(ptr_a, iterprop, &iter);
2667 for (; iter.valid; RNA_property_collection_next(&iter)) {
2668 PropertyRNA *prop = static_cast<PropertyRNA *>(iter.ptr.data);
2669 if (STREQ(RNA_property_identifier(prop), "rna_type")) {
2670 continue;
2671 }
2672 switch (RNA_property_type(prop)) {
2673 case PROP_POINTER: {
2674 PointerRNA ptr_sub_a = RNA_property_pointer_get(ptr_a, prop);
2675 PointerRNA ptr_sub_b = RNA_property_pointer_get(ptr_b, prop);
2676 rna_struct_update_when_changed(C, bmain, &ptr_sub_a, &ptr_sub_b);
2677 break;
2678 }
2679 case PROP_COLLECTION:
2680 /* Don't handle collections. */
2681 break;
2682 default: {
2683 if (!RNA_property_equals(bmain, ptr_a, ptr_b, prop, RNA_EQ_STRICT)) {
2684 RNA_property_update(C, ptr_b, prop);
2685 }
2686 }
2687 }
2688 }
2690}
2691
2693 Main *bmain,
2694 UserDef *userdef_prev,
2695 UserDef *userdef_curr)
2696{
2697 PointerRNA ptr_a = RNA_pointer_create_discrete(nullptr, &RNA_Preferences, userdef_prev);
2698 PointerRNA ptr_b = RNA_pointer_create_discrete(nullptr, &RNA_Preferences, userdef_curr);
2699 const bool is_dirty = userdef_curr->runtime.is_dirty;
2700
2701 rna_struct_update_when_changed(C, bmain, &ptr_a, &ptr_b);
2702
2705
2706 userdef_curr->runtime.is_dirty = is_dirty;
2707}
2708
2710{
2711 Main *bmain = CTX_data_main(C);
2712 const bool use_data = false;
2713 const bool use_userdef = true;
2714 const bool use_factory_settings = STREQ(op->type->idname, "WM_OT_read_factory_userpref");
2715 const bool use_factory_settings_app_template_only =
2716 (use_factory_settings && RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only"));
2717
2719
2720 UserDef U_backup = blender::dna::shallow_copy(U);
2721
2722 wmHomeFileRead_Params read_homefile_params{};
2723 read_homefile_params.use_data = use_data;
2724 read_homefile_params.use_userdef = use_userdef;
2725 read_homefile_params.use_factory_settings = use_factory_settings;
2726 read_homefile_params.use_factory_settings_app_template_only =
2727 use_factory_settings_app_template_only;
2728 read_homefile_params.use_empty_data = false;
2729 read_homefile_params.filepath_startup_override = nullptr;
2731 wm_homefile_read(C, &read_homefile_params, op->reports);
2732
2733 wm_userpref_read_exceptions(&U, &U_backup);
2734 SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT);
2735
2736 wm_userpref_update_when_changed(C, bmain, &U_backup, &U);
2737
2738 if (use_factory_settings) {
2739 U.runtime.is_dirty = true;
2740 }
2741
2743
2744 /* Needed to recalculate UI scaling values (eg, #UserDef.inv_dpi_fac). */
2745 wm_window_clear_drawable(static_cast<wmWindowManager *>(bmain->wm.first));
2746
2748
2749 return OPERATOR_FINISHED;
2750}
2751
2753{
2754 ot->name = "Load Preferences";
2755 ot->idname = "WM_OT_read_userpref";
2756 ot->description = "Load last saved preferences";
2757
2758 ot->invoke = WM_operator_confirm;
2759 ot->exec = wm_userpref_read_exec;
2760}
2761
2763 wmOperator *op,
2764 const wmEvent * /*event*/)
2765{
2766 std::string title;
2767
2768 const bool template_only = U.app_template[0] &&
2769 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only");
2770
2771 if (template_only) {
2772 char display_name[FILE_MAX];
2773 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
2774 title = fmt::format(fmt::runtime(IFACE_("Load Factory \"{}\" Preferences.")),
2775 IFACE_(display_name));
2776 }
2777 else {
2778 title = IFACE_("Load Factory Blender Preferences");
2779 }
2780
2782 C,
2783 op,
2784 title.c_str(),
2785 IFACE_("To make changes to Preferences permanent, use \"Save Preferences\""),
2786 IFACE_("Load"),
2788 false);
2789}
2790
2792{
2793 ot->name = "Load Factory Preferences";
2794 ot->idname = "WM_OT_read_factory_userpref";
2795 ot->description =
2796 "Load factory default preferences. "
2797 "To make changes to preferences permanent, use \"Save Preferences\"";
2798
2799 ot->invoke = wm_userpref_read_invoke;
2800 ot->exec = wm_userpref_read_exec;
2801
2803}
2804
2806
2807/* -------------------------------------------------------------------- */
2810
2817
2819{
2820 ot->name = "Reload History File";
2821 ot->idname = "WM_OT_read_history";
2822 ot->description = "Reloads history and bookmarks";
2823
2824 ot->invoke = WM_operator_confirm;
2826
2827 /* This operator is only used for loading settings from a previous blender install. */
2828 ot->flag = OPTYPE_INTERNAL;
2829}
2830
2832
2833/* -------------------------------------------------------------------- */
2838
2840{
2841 const bool use_factory_startup_and_userdef = STREQ(op->type->idname,
2842 "WM_OT_read_factory_settings");
2843 const bool use_factory_settings = use_factory_startup_and_userdef ||
2844 RNA_boolean_get(op->ptr, "use_factory_startup");
2845 const bool use_factory_settings_app_template_only =
2846 (use_factory_startup_and_userdef &&
2847 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only"));
2848
2849 bool use_userdef = false;
2850 char filepath_buf[FILE_MAX];
2851 const char *filepath = nullptr;
2852 UserDef U_backup = blender::dna::shallow_copy(U);
2853
2854 if (!use_factory_settings) {
2855 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath");
2856
2857 /* This can be used when loading of a start-up file should only change
2858 * the scene content but keep the blender UI as it is. */
2859 wm_open_init_load_ui(op, true);
2860 SET_FLAG_FROM_TEST(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
2861
2862 if (RNA_property_is_set(op->ptr, prop)) {
2863 RNA_property_string_get(op->ptr, prop, filepath_buf);
2864 filepath = filepath_buf;
2865 if (BLI_access(filepath, R_OK)) {
2867 op->reports, RPT_ERROR, "Can't read alternative start-up file: \"%s\"", filepath);
2868 return OPERATOR_CANCELLED;
2869 }
2870 }
2871 }
2872 else {
2873 if (use_factory_startup_and_userdef) {
2874 /* Always load UI for factory settings (prefs will re-init). */
2875 G.fileflags &= ~G_FILE_NO_UI;
2876 /* Always load preferences with factory settings. */
2877 use_userdef = true;
2878 }
2879 }
2880
2881 /* Close any user-loaded fonts. */
2883
2884 char app_template_buf[sizeof(U.app_template)];
2885 const char *app_template;
2886 PropertyRNA *prop_app_template = RNA_struct_find_property(op->ptr, "app_template");
2887 const bool use_splash = !use_factory_settings && RNA_boolean_get(op->ptr, "use_splash");
2888 const bool use_empty_data = RNA_boolean_get(op->ptr, "use_empty");
2889
2890 if (prop_app_template && RNA_property_is_set(op->ptr, prop_app_template)) {
2891 RNA_property_string_get(op->ptr, prop_app_template, app_template_buf);
2892 app_template = app_template_buf;
2893
2894 if (!use_factory_settings) {
2895 /* Always load preferences when switching templates with own preferences. */
2898 }
2899
2900 /* Turn override off, since we're explicitly loading a different app-template. */
2902 }
2903 else {
2904 /* Normally nullptr, only set when overriding from the command-line. */
2906 }
2907
2908 if (use_userdef) {
2910 }
2911
2912 wmHomeFileRead_Params read_homefile_params{};
2913 read_homefile_params.use_data = true;
2914 read_homefile_params.use_userdef = use_userdef;
2915 read_homefile_params.use_factory_settings = use_factory_settings;
2916 read_homefile_params.use_factory_settings_app_template_only =
2917 use_factory_settings_app_template_only;
2918 read_homefile_params.use_empty_data = use_empty_data;
2919 read_homefile_params.filepath_startup_override = filepath;
2920 read_homefile_params.app_template_override = app_template;
2921 wm_homefile_read(C, &read_homefile_params, op->reports);
2922
2923 if (use_splash) {
2925 }
2926
2927 if (use_userdef) {
2928 wm_userpref_read_exceptions(&U, &U_backup);
2929 SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT);
2930
2931 if (use_factory_settings) {
2932 U.runtime.is_dirty = true;
2933 }
2934 }
2935
2936 if (use_userdef) {
2938 }
2939
2940 if (G.fileflags & G_FILE_NO_UI) {
2942 }
2943
2944 return OPERATOR_FINISHED;
2945}
2946
2948{
2950 C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data, nullptr);
2951}
2952
2962
2964{
2965 PropertyRNA *prop;
2966
2967 prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", "");
2969
2970 prop = RNA_def_boolean(
2971 ot->srna,
2972 "use_empty",
2973 false,
2974 "Empty",
2975 "After loading, remove everything except scenes, windows, and workspaces. This makes it "
2976 "possible to load the startup file with its scene configuration and window layout intact, "
2977 "but no objects, materials, animations, ...");
2979}
2980
2982{
2983 PropertyRNA *prop;
2984 ot->name = "Reload Start-Up File";
2985 ot->idname = "WM_OT_read_homefile";
2986 ot->description = "Open the default file";
2987
2988 ot->invoke = wm_homefile_read_invoke;
2989 ot->exec = wm_homefile_read_exec;
2990
2991 prop = RNA_def_string_file_path(ot->srna,
2992 "filepath",
2993 nullptr,
2994 FILE_MAX,
2995 "File Path",
2996 "Path to an alternative start-up file");
2998
2999 /* So scripts can use an alternative start-up file without the UI. */
3000 prop = RNA_def_boolean(
3001 ot->srna, "load_ui", true, "Load UI", "Load user interface setup from the .blend file");
3003
3004 /* So the splash can be kept open after loading a file (for templates). */
3005 prop = RNA_def_boolean(ot->srna, "use_splash", false, "Splash", "");
3007
3008 /* So scripts can load factory-startup without resetting preferences
3009 * (which has other implications such as reloading all add-ons).
3010 * Match naming for `--factory-startup` command line argument. */
3011 prop = RNA_def_boolean(ot->srna,
3012 "use_factory_startup",
3013 false,
3014 "Factory Startup",
3015 "Load the default ('factory startup') blend file. "
3016 "This is independent of the normal start-up file that the user can save");
3019
3021
3022 /* Omit poll to run in background mode. */
3023}
3024
3026 wmOperator *op,
3027 const wmEvent * /*event*/)
3028{
3030 CTX_wm_manager(C));
3031 std::string title;
3032 const bool template_only = U.app_template[0] &&
3033 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only");
3034
3035 if (template_only) {
3036 char display_name[FILE_MAX];
3037 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
3038 title = fmt::format(fmt::runtime(IFACE_("Load Factory \"{}\" Startup File and Preferences")),
3039 IFACE_(display_name));
3040 }
3041 else {
3042 title = IFACE_("Load Factory Default Startup File and Preferences");
3043 }
3044
3046 C,
3047 op,
3048 title.c_str(),
3049 unsaved ? IFACE_("To make changes to Preferences permanent, use \"Save Preferences\".\n"
3050 "Warning: Your file is unsaved! Proceeding will abandon your changes.") :
3051 IFACE_("To make changes to Preferences permanent, use \"Save Preferences\"."),
3052 IFACE_("Load"),
3054 false);
3055}
3056
3058{
3059 ot->name = "Load Factory Settings";
3060 ot->idname = "WM_OT_read_factory_settings";
3061 ot->description =
3062 "Load factory default startup file and preferences. "
3063 "To make changes permanent, use \"Save Startup File\" and \"Save Preferences\"";
3064
3066 ot->exec = wm_homefile_read_exec;
3067 /* Omit poll to run in background mode. */
3068
3070
3072}
3073
3075
3076/* -------------------------------------------------------------------- */
3079
3084 const char *filepath,
3085 const bool use_scripts_autoexec_check,
3087{
3088 /* XXX: wm in context is not set correctly after #WM_file_read -> crash. */
3089 /* Do it before for now, but is this correct with multiple windows? */
3091
3092 const bool success = WM_file_read(C, filepath, use_scripts_autoexec_check, reports);
3093
3094 return success;
3095}
3096
3097/* Generic operator state utilities. */
3098
3099static void create_operator_state(wmOperatorType *ot, int first_state)
3100{
3101 PropertyRNA *prop = RNA_def_int(
3102 ot->srna, "state", first_state, INT32_MIN, INT32_MAX, "State", "", INT32_MIN, INT32_MAX);
3105}
3106
3108{
3109 return RNA_int_get(op->ptr, "state");
3110}
3111
3113{
3114 RNA_int_set(op->ptr, "state", state);
3115}
3116
3121
3123 wmOperator *op,
3124 OperatorDispatchTarget *targets)
3125{
3126 int state = get_operator_state(op);
3127 for (int i = 0; targets[i].run; i++) {
3128 OperatorDispatchTarget target = targets[i];
3129 if (target.state == state) {
3130 return target.run(C, op);
3131 }
3132 }
3134 return OPERATOR_CANCELLED;
3135}
3136
3138
3139/* -------------------------------------------------------------------- */
3142
3143enum {
3147};
3148
3150
3152{
3154 C, "WM_OT_open_mainfile", WM_OP_INVOKE_DEFAULT, (IDProperty *)user_data, nullptr);
3155}
3156
3171
3173{
3175
3176 Main *bmain = CTX_data_main(C);
3177 const char *blendfile_path = BKE_main_blendfile_path(bmain);
3178
3179 if (CTX_wm_window(C) == nullptr) {
3180 /* In rare cases this could happen, when trying to invoke in background
3181 * mode on load for example. Don't use poll for this because exec()
3182 * can still run without a window. */
3183 BKE_report(op->reports, RPT_ERROR, "Context window not set");
3184 return OPERATOR_CANCELLED;
3185 }
3186
3187 /* If possible, get the name of the most recently used `.blend` file. */
3188 if (G.recent_files.first) {
3189 RecentFile *recent = static_cast<RecentFile *>(G.recent_files.first);
3190 blendfile_path = recent->filepath;
3191 }
3192
3193 RNA_string_set(op->ptr, "filepath", blendfile_path);
3194 wm_open_init_load_ui(op, true);
3195 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3196 UNUSED_VARS(use_scripts_autoexec_check); /* The user can set this in the UI. */
3197 op->customdata = nullptr;
3198
3200
3202}
3203
3205{
3206 char filepath[FILE_MAX];
3207 bool success;
3208
3209 RNA_string_get(op->ptr, "filepath", filepath);
3210 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3211
3212 /* For file opening, also print in console for warnings, not only errors. */
3214
3215 /* Re-use last loaded setting so we can reload a file without changing. */
3216 wm_open_init_load_ui(op, false);
3217 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, false);
3218
3219 SET_FLAG_FROM_TEST(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
3221 success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, op->reports);
3222
3223 if (success) {
3224 if (G.fileflags & G_FILE_NO_UI) {
3226 }
3228 return OPERATOR_FINISHED;
3229 }
3230 return OPERATOR_CANCELLED;
3231}
3232
3239
3244
3246 wmOperator *op,
3247 const wmEvent * /*event*/)
3248{
3249 return wm_open_mainfile_dispatch(C, op);
3250}
3251
3256
3258 wmOperatorType * /*ot*/,
3259 PointerRNA *ptr)
3260{
3261 if (!RNA_struct_property_is_set(ptr, "filepath")) {
3262 return "";
3263 }
3264
3265 char filepath[FILE_MAX];
3266 RNA_string_get(ptr, "filepath", filepath);
3267
3268 BLI_stat_t stats;
3269 if (BLI_stat(filepath, &stats) == -1) {
3270 return fmt::format("{}\n\n{}", filepath, TIP_("File Not Found"));
3271 }
3272
3273 /* Date. */
3274 char date_str[FILELIST_DIRENTRY_DATE_LEN];
3275 char time_str[FILELIST_DIRENTRY_TIME_LEN];
3276 bool is_today, is_yesterday;
3278 nullptr, int64_t(stats.st_mtime), false, time_str, date_str, &is_today, &is_yesterday);
3279 if (is_today || is_yesterday) {
3280 STRNCPY(date_str, is_today ? TIP_("Today") : TIP_("Yesterday"));
3281 }
3282
3283 /* Size. */
3284 char size_str[FILELIST_DIRENTRY_SIZE_LEN];
3285 BLI_filelist_entry_size_to_string(nullptr, uint64_t(stats.st_size), false, size_str);
3286
3287 return fmt::format("{}\n\n{}: {} {}\n{}: {}",
3288 filepath,
3289 TIP_("Modified"),
3290 date_str,
3291 time_str,
3292 TIP_("Size"),
3293 size_str);
3294}
3295
3296/* Currently fits in a pointer. */
3299};
3300BLI_STATIC_ASSERT(sizeof(FileRuntime) <= sizeof(void *), "Struct must not exceed pointer size");
3301
3303{
3304 FileRuntime *file_info = (FileRuntime *)&op->customdata;
3305 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
3306 bool is_untrusted = false;
3307 char filepath[FILE_MAX];
3308 char *lslash;
3309
3310 RNA_string_get(op->ptr, "filepath", filepath);
3311
3312 /* Get the directory. */
3313 lslash = (char *)BLI_path_slash_rfind(filepath);
3314 if (lslash) {
3315 *(lslash + 1) = '\0';
3316 }
3317
3318 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
3319 if (BKE_autoexec_match(filepath) == true) {
3320 RNA_property_boolean_set(op->ptr, prop, false);
3321 is_untrusted = true;
3322 }
3323 }
3324
3325 if (file_info) {
3326 file_info->is_untrusted = is_untrusted;
3327 }
3328
3329 return is_untrusted;
3330}
3331
3332static void wm_open_mainfile_ui(bContext * /*C*/, wmOperator *op)
3333{
3334 FileRuntime *file_info = (FileRuntime *)&op->customdata;
3335 uiLayout *layout = op->layout;
3336 const char *autoexec_text;
3337
3338 layout->prop(op->ptr, "load_ui", UI_ITEM_NONE, std::nullopt, ICON_NONE);
3339
3340 uiLayout *col = &layout->column(false);
3341 if (file_info->is_untrusted) {
3342 autoexec_text = IFACE_("Trusted Source [Untrusted Path]");
3343 uiLayoutSetActive(col, false);
3344 uiLayoutSetEnabled(col, false);
3345 }
3346 else {
3347 autoexec_text = IFACE_("Trusted Source");
3348 }
3349
3350 col->prop(op->ptr, "use_scripts", UI_ITEM_NONE, autoexec_text, ICON_NONE);
3351}
3352
3354{
3355 RNA_def_boolean(ot->srna,
3356 "use_scripts",
3357 false,
3358 "Trusted Source",
3359 "Allow .blend file to execute scripts automatically, default available from "
3360 "system preferences");
3361}
3362
3364{
3365 ot->name = "Open";
3366 ot->idname = "WM_OT_open_mainfile";
3367 ot->description = "Open a Blender file";
3368 ot->get_description = wm_open_mainfile_get_description;
3369
3370 ot->invoke = wm_open_mainfile_invoke;
3371 ot->exec = wm_open_mainfile_exec;
3372 ot->check = wm_open_mainfile_check;
3373 ot->ui = wm_open_mainfile_ui;
3374 /* Omit window poll so this can work in background mode. */
3375
3383
3385 ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file");
3386
3388
3390 ot->srna, "display_file_selector", true, "Display File Selector", "");
3392
3394}
3395
3397
3398/* -------------------------------------------------------------------- */
3401
3403 wmOperator *op,
3404 const wmEvent * /*event*/)
3405{
3406 std::string message = IFACE_("Any unsaved changes will be lost.");
3408 message += "\n";
3409 message += IFACE_("Warning: There are unsaved external image(s).");
3410 }
3411
3413 op,
3414 IFACE_("Revert to the Saved File"),
3415 message.c_str(),
3416 IFACE_("Revert"),
3418 false);
3419}
3420
3422{
3423 Main *bmain = CTX_data_main(C);
3424 bool success;
3425 char filepath[FILE_MAX];
3426
3427 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, false);
3428
3430
3431 STRNCPY(filepath, BKE_main_blendfile_path(bmain));
3432 success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, op->reports);
3433
3434 if (success) {
3435 return OPERATOR_FINISHED;
3436 }
3437 return OPERATOR_CANCELLED;
3438}
3439
3441{
3442 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3443 return (blendfile_path[0] != '\0');
3444}
3445
3447{
3448 ot->name = "Revert";
3449 ot->idname = "WM_OT_revert_mainfile";
3450 ot->description = "Reload the saved file";
3451
3452 ot->invoke = wm_revert_mainfile_invoke;
3455
3457}
3458
3460
3461/* -------------------------------------------------------------------- */
3464
3466 const bool use_scripts_autoexec_check,
3468{
3469 char filepath[FILE_MAX];
3470 BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE);
3471 G.fileflags |= G_FILE_RECOVER_READ;
3472 const bool success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, reports);
3473 G.fileflags &= ~G_FILE_RECOVER_READ;
3474 return success;
3475}
3476
3478 wmOperator *op,
3479 const bool use_scripts_autoexec_check)
3480{
3482 if (WM_file_recover_last_session(C, use_scripts_autoexec_check, op->reports)) {
3483 if (!G.background) {
3484 wmOperatorType *ot = op->type;
3485 PointerRNA *props_ptr = MEM_new<PointerRNA>(__func__);
3487 RNA_boolean_set(props_ptr, "use_scripts", true);
3489 }
3490 return OPERATOR_FINISHED;
3491 }
3492 return OPERATOR_CANCELLED;
3493}
3494
3496{
3497 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3498 return wm_recover_last_session_impl(C, op, use_scripts_autoexec_check);
3499}
3500
3502{
3504 C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data, nullptr);
3505}
3506
3508 wmOperator *op,
3509 const wmEvent * /*event*/)
3510{
3511 /* Keep the current setting instead of using the preferences since a file selector
3512 * doesn't give us the option to change the setting. */
3513 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, false);
3514
3517 {
3518 return OPERATOR_INTERFACE;
3519 }
3520 return wm_recover_last_session_impl(C, op, use_scripts_autoexec_check);
3521}
3522
3524{
3525 ot->name = "Recover Last Session";
3526 ot->idname = "WM_OT_recover_last_session";
3527 ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")";
3528
3531
3533}
3534
3536
3537/* -------------------------------------------------------------------- */
3540
3542{
3543 char filepath[FILE_MAX];
3544 bool success;
3545
3546 RNA_string_get(op->ptr, "filepath", filepath);
3547 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3548
3549 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3551
3552 G.fileflags |= G_FILE_RECOVER_READ;
3553
3554 success = wm_file_read_opwrap(C, filepath, use_scripts_autoexec_check, op->reports);
3555
3556 G.fileflags &= ~G_FILE_RECOVER_READ;
3557
3558 if (success) {
3559 if (!G.background) {
3560 wmOperatorType *ot = op->type;
3561 PointerRNA *props_ptr = MEM_new<PointerRNA>(__func__);
3563 RNA_boolean_set(props_ptr, "use_scripts", true);
3565 }
3566 return OPERATOR_FINISHED;
3567 }
3568 return OPERATOR_CANCELLED;
3569}
3570
3572 wmOperator *op,
3573 const wmEvent * /*event*/)
3574{
3575 char filepath[FILE_MAX];
3576
3577 wm_autosave_location(filepath);
3578 RNA_string_set(op->ptr, "filepath", filepath);
3579 const bool use_scripts_autoexec_check = wm_open_init_use_scripts(op, true);
3580 UNUSED_VARS(use_scripts_autoexec_check); /* The user can set this in the UI. */
3582
3584}
3585
3587{
3588 ot->name = "Recover Auto Save";
3589 ot->idname = "WM_OT_recover_auto_save";
3590 ot->description = "Open an automatically saved file to recover it";
3591
3594
3602
3604}
3605
3607
3608/* -------------------------------------------------------------------- */
3613
3614static void wm_filepath_default(const Main *bmain, char *filepath)
3615{
3616 if (bmain->filepath[0] == '\0') {
3617 char filename_untitled[FILE_MAXFILE];
3618 SNPRINTF(filename_untitled, "%s.blend", DATA_("Untitled"));
3619 BLI_path_filename_ensure(filepath, FILE_MAX, filename_untitled);
3620 }
3621}
3622
3624{
3625 PropertyRNA *prop;
3626
3627 prop = RNA_struct_find_property(op->ptr, "compress");
3628 if (!RNA_property_is_set(op->ptr, prop)) {
3629 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3630 if (blendfile_path[0] != '\0') { /* Keep flag for existing file. */
3631 RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0);
3632 }
3633 else { /* Use userdef for new file. */
3634 RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0);
3635 }
3636 }
3637}
3638
3640{
3641 Main *bmain = CTX_data_main(C);
3642 PropertyRNA *prop;
3643 char filepath[FILE_MAX];
3644
3645 prop = RNA_struct_find_property(op->ptr, "filepath");
3646 if (!RNA_property_is_set(op->ptr, prop)) {
3647 const char *blendfile_path = BKE_main_blendfile_path(bmain);
3648 /* If not saved before, get the name of the most recently used `.blend` file. */
3649 if ((blendfile_path[0] == '\0') && G.recent_files.first) {
3650 RecentFile *recent = static_cast<RecentFile *>(G.recent_files.first);
3651 STRNCPY(filepath, recent->filepath);
3652 }
3653 else {
3654 STRNCPY(filepath, blendfile_path);
3655 }
3656
3657 /* For convenience when using "Save As" on asset system files:
3658 * Replace `.asset.blend` extension with just `.blend`.
3659 * Asset system files must not be overridden (except by the asset system),
3660 * there are further checks to prevent this entirely. */
3661 if (bmain->is_asset_edit_file &&
3663 {
3664 filepath[strlen(filepath) - strlen(BLENDER_ASSET_FILE_SUFFIX)] = '\0';
3665 BLI_path_extension_ensure(filepath, FILE_MAX, ".blend");
3666 }
3667
3668 wm_filepath_default(bmain, filepath);
3669 RNA_property_string_set(op->ptr, prop, filepath);
3670 }
3671}
3672
3674 wmOperator *op,
3675 const wmEvent * /*event*/)
3676{
3677
3679 save_set_filepath(C, op);
3680
3681 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
3682 if (!RNA_property_is_set(op->ptr, prop)) {
3683 RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_RELPATHS));
3684 }
3685
3687
3689}
3690
3691/* Function used for #WM_OT_save_mainfile too. */
3693{
3694 Main *bmain = CTX_data_main(C);
3695 char filepath[FILE_MAX];
3696 const bool is_save_as = (op->type->invoke == wm_save_as_mainfile_invoke);
3697 const bool use_save_as_copy = is_save_as && RNA_boolean_get(op->ptr, "copy");
3698
3699 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "incremental");
3700 const bool is_incremental = prop ? RNA_property_boolean_get(op->ptr, prop) : false;
3701
3702 /* We could expose all options to the users however in most cases remapping
3703 * existing relative paths is a good default.
3704 * Users can manually make their paths relative & absolute if they wish. */
3705 const eBLO_WritePathRemap remap_mode = RNA_boolean_get(op->ptr, "relative_remap") ?
3709
3710 const bool is_filepath_set = RNA_struct_property_is_set(op->ptr, "filepath");
3711 if (is_filepath_set) {
3712 RNA_string_get(op->ptr, "filepath", filepath);
3713 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3714 }
3715 else {
3716 STRNCPY(filepath, BKE_main_blendfile_path(bmain));
3717 }
3718
3719 if (filepath[0] == '\0') {
3720 BKE_report(op->reports,
3721 RPT_ERROR,
3722 "Unable to save an unsaved file with an empty or unset \"filepath\" property");
3723 return OPERATOR_CANCELLED;
3724 }
3725
3726 if ((is_save_as == false) && is_incremental) {
3727 char head[FILE_MAXFILE], tail[FILE_MAXFILE];
3728 ushort digits;
3729 int num = BLI_path_sequence_decode(filepath, head, sizeof(head), tail, sizeof(tail), &digits);
3730 /* Numbers greater than INT_MAX return 0, resulting in always appending "1" to the name. */
3731 if (num == 0 && digits == 0) {
3732 /* This does nothing if there are no numbers at the end of the head. */
3734 }
3735
3736 const int tries_limit = 1000;
3737 int tries = 0;
3738 bool in_use = true;
3739 do {
3740 num++;
3741 tries++;
3742 BLI_path_sequence_encode(filepath, sizeof(filepath), head, tail, digits, num);
3743 in_use = BLI_exists(filepath);
3744 } while (in_use && tries < tries_limit && num < INT_MAX);
3745 if (in_use) {
3746 BKE_report(op->reports, RPT_ERROR, "Unable to find an available incremented file name");
3747 return OPERATOR_CANCELLED;
3748 }
3749 }
3750
3751 const int fileflags_orig = G.fileflags;
3752 int fileflags = G.fileflags;
3753
3754 /* Set compression flag. */
3755 SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "compress"), G_FILE_COMPRESS);
3756
3757 const bool success = wm_file_write(
3758 C, filepath, fileflags, remap_mode, use_save_as_copy, op->reports);
3759
3760 if ((op->flag & OP_IS_INVOKE) == 0) {
3761 /* OP_IS_INVOKE is set when the operator is called from the GUI.
3762 * If it is not set, the operator is called from a script and
3763 * shouldn't influence G.fileflags. */
3764 G.fileflags = fileflags_orig;
3765 }
3766
3767 if (success == false) {
3768 return OPERATOR_CANCELLED;
3769 }
3770
3771 const char *filename = BLI_path_basename(filepath);
3772
3773 if (is_incremental) {
3774 BKE_reportf(op->reports, RPT_INFO, "Saved incremental as \"%s\"", filename);
3775 }
3776 else if (is_save_as) {
3777 /* use_save_as_copy depends upon is_save_as. */
3778 if (use_save_as_copy) {
3779 BKE_reportf(op->reports, RPT_INFO, "Saved copy as \"%s\"", filename);
3780 }
3781 else {
3782 BKE_reportf(op->reports, RPT_INFO, "Saved as \"%s\"", filename);
3783 }
3784 }
3785 else {
3786 BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", filename);
3787 }
3788
3789 if (!use_save_as_copy) {
3790 /* If saved file is the active one, there are technically no more compatibility issues, the
3791 * file on disk now matches the currently opened data version-wise. */
3792 bmain->has_forward_compatibility_issues = false;
3793
3794 /* If saved file is the active one, notify WM so that saved status and window title can be
3795 * updated. */
3798 /* Restart auto-save timer to avoid unnecessary unexpected freezing (because of auto-save)
3799 * when often saving manually. */
3802 wm->autosave_scheduled = false;
3803 }
3804 }
3805
3806 if (!is_save_as && RNA_boolean_get(op->ptr, "exit")) {
3808 }
3809
3810 return OPERATOR_FINISHED;
3811}
3812
3814{
3815 char filepath[FILE_MAX];
3816 RNA_string_get(op->ptr, "filepath", filepath);
3817 if (!BKE_blendfile_extension_check(filepath)) {
3818 /* NOTE(@ideasman42): some users would prefer #BLI_path_extension_replace(),
3819 * we have had some nitpicking bug reports about this.
3820 * Always adding the extension as users may use '.' as part of the file-name. */
3821 BLI_path_extension_ensure(filepath, FILE_MAX, ".blend");
3822 RNA_string_set(op->ptr, "filepath", filepath);
3823 return true;
3824 }
3825 return false;
3826}
3827
3829{
3830 if (RNA_boolean_get(ptr, "copy")) {
3831 return CTX_IFACE_(ot->translation_context, "Save Copy");
3832 }
3833 return "";
3834}
3835
3837 wmOperatorType * /*ot*/,
3838 PointerRNA *ptr)
3839{
3840 if (RNA_boolean_get(ptr, "copy")) {
3841 return BLI_strdup(TIP_(
3842 "Save the current file in the desired location but do not make the saved file active"));
3843 }
3844 return "";
3845}
3846
3848{
3849 PropertyRNA *prop;
3850
3851 ot->name = "Save As";
3852 ot->idname = "WM_OT_save_as_mainfile";
3853 ot->description = "Save the current file in the desired location";
3854
3857 ot->get_name = wm_save_as_mainfile_get_name;
3858 ot->get_description = wm_save_as_mainfile_get_description;
3859 ot->check = wm_save_mainfile_check;
3860 /* Omit window poll so this can work in background mode. */
3861
3865 FILE_SAVE,
3869 RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
3870 RNA_def_boolean(ot->srna,
3871 "relative_remap",
3872 true,
3873 "Remap Relative",
3874 "Remap relative paths when saving to a different directory");
3875 prop = RNA_def_boolean(
3876 ot->srna,
3877 "copy",
3878 false,
3879 "Save Copy",
3880 "Save a copy of the actual working state but does not make saved file active");
3882}
3883
3885 wmOperator *op,
3886 const wmEvent * /*event*/)
3887{
3889
3890 /* Cancel if no active window. */
3891 if (CTX_wm_window(C) == nullptr) {
3892 return OPERATOR_CANCELLED;
3893 }
3894
3896 save_set_filepath(C, op);
3897
3898 /* If we're saving for the first time and prefer relative paths -
3899 * any existing paths will be absolute,
3900 * enable the option to remap paths to avoid confusion, see: #37240. */
3901 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3902 if ((blendfile_path[0] == '\0') && (U.flag & USER_RELPATHS)) {
3903 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
3904 if (!RNA_property_is_set(op->ptr, prop)) {
3905 RNA_property_boolean_set(op->ptr, prop, true);
3906 }
3907 }
3908
3909 if (blendfile_path[0] != '\0') {
3913 }
3914 else {
3916 }
3917 }
3918 else {
3921 }
3922
3923 return ret;
3924}
3925
3927 wmOperatorType * /*ot*/,
3928 PointerRNA *ptr)
3929{
3930 if (RNA_boolean_get(ptr, "incremental")) {
3931 return TIP_(
3932 "Save the current Blender file with a numerically incremented name that does not "
3933 "overwrite any existing files");
3934 }
3935 return "";
3936}
3937
3939{
3940 ot->name = "Save Blender File";
3941 ot->idname = "WM_OT_save_mainfile";
3942 ot->description = "Save the current Blender file";
3943
3944 ot->invoke = wm_save_mainfile_invoke;
3946 ot->check = wm_save_mainfile_check;
3947 ot->get_description = wm_save_mainfile_get_description;
3948 /* Omit window poll so this can work in background mode. */
3949
3950 PropertyRNA *prop;
3954 FILE_SAVE,
3958 RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
3959 RNA_def_boolean(ot->srna,
3960 "relative_remap",
3961 false,
3962 "Remap Relative",
3963 "Remap relative paths when saving to a different directory");
3964
3965 prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "Exit Blender after saving");
3967
3968 prop = RNA_def_boolean(ot->srna,
3969 "incremental",
3970 false,
3971 "Incremental",
3972 "Save the current Blender file with a numerically incremented name that "
3973 "does not overwrite any existing files");
3975}
3976
3978
3979/* -------------------------------------------------------------------- */
3982
3984
3986 {CLEAR_RECENT_ALL, "ALL", 0, "All Items", ""},
3987 {CLEAR_RECENT_MISSING, "MISSING", 0, "Items Not Found", ""},
3988 {0, nullptr, 0, nullptr, nullptr},
3989};
3990
3992 wmOperator *op,
3993 const wmEvent *event)
3994{
3996 C, op, event, IFACE_("Clear Recent Files List"), IFACE_("Remove"));
3997}
3998
4000{
4001 ClearRecentInclude include = static_cast<ClearRecentInclude>(RNA_enum_get(op->ptr, "remove"));
4002
4003 if (include == CLEAR_RECENT_ALL) {
4005 }
4006 else if (include == CLEAR_RECENT_MISSING) {
4007 LISTBASE_FOREACH_MUTABLE (RecentFile *, recent, &G.recent_files) {
4008 if (!BLI_exists(recent->filepath)) {
4009 wm_history_file_free(recent);
4010 }
4011 }
4012 }
4013
4015
4016 return OPERATOR_FINISHED;
4017}
4018
4020{
4021 uiLayout *layout = op->layout;
4022 uiLayoutSetPropSep(layout, true);
4023 uiLayoutSetPropDecorate(layout, false);
4024
4025 layout->separator();
4026 layout->prop(op->ptr, "remove", UI_ITEM_R_TOGGLE, std::nullopt, ICON_NONE);
4027 layout->separator();
4028}
4029
4031{
4032 ot->name = "Clear Recent Files List";
4033 ot->idname = "WM_OT_clear_recent_files";
4034 ot->description = "Clear the recent files list";
4035
4039
4040 /* flags */
4041 ot->flag = OPTYPE_REGISTER;
4042
4043 /* props */
4044 ot->prop = RNA_def_enum(
4045 ot->srna, "remove", prop_clear_recent_types, CLEAR_RECENT_ALL, "Remove", "");
4046}
4047
4049
4050/* -------------------------------------------------------------------- */
4053
4054static void wm_block_autorun_warning_ignore(bContext *C, void *arg_block, void * /*arg*/)
4055{
4056 wmWindow *win = CTX_wm_window(C);
4057 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4058
4059 /* Free the data as it's no longer needed. */
4060 wm_test_autorun_revert_action_set(nullptr, nullptr);
4061}
4062
4064{
4065 wmWindow *win = CTX_wm_window(C);
4066
4067 UI_popup_block_close(C, win, block);
4068
4069 /* Save user preferences for permanent execution. */
4070 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
4071 WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, nullptr, nullptr);
4072 }
4073
4074 /* Load file again with scripts enabled.
4075 * The reload is necessary to allow scripts to run when the files loads. */
4077}
4078
4080{
4081 wmWindow *win = CTX_wm_window(C);
4082 Main *bmain = CTX_data_main(C);
4083
4084 UI_popup_block_close(C, win, block);
4085
4086 /* Save user preferences for permanent execution. */
4087 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
4088 WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, nullptr, nullptr);
4089 }
4090
4091 /* Force a full refresh, but without reloading the file. */
4092 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
4094 }
4095}
4096
4097/* Build the auto-run warning dialog UI. */
4098static uiBlock *block_create_autorun_warning(bContext *C, ARegion *region, void * /*arg1*/)
4099{
4100 const char *blendfile_path = BKE_main_blendfile_path_from_global();
4102
4103 uiBlock *block = UI_block_begin(
4104 C, region, "autorun_warning_popup", blender::ui::EmbossType::Emboss);
4109
4110 const char *title = RPT_(
4111 "For security reasons, automatic execution of Python scripts "
4112 "in this file was disabled:");
4113 const char *message = RPT_("This may lead to unexpected behavior");
4114 const char *checkbox_text = RPT_("Permanently allow execution of scripts");
4115
4116 /* Measure strings to find the longest. */
4117 const uiStyle *style = UI_style_get_dpi();
4118 UI_fontstyle_set(&style->widget);
4119 int text_width = int(BLF_width(style->widget.uifont_id, title, BLF_DRAW_STR_DUMMY_MAX));
4120 text_width = std::max(text_width,
4121 int(BLF_width(style->widget.uifont_id, message, BLF_DRAW_STR_DUMMY_MAX)));
4122 text_width = std::max(
4123 text_width,
4124 int(BLF_width(style->widget.uifont_id, checkbox_text, BLF_DRAW_STR_DUMMY_MAX) +
4125 (UI_SCALE_FAC * 25.0f)));
4126
4127 const int dialog_width = std::max(int(400.0f * UI_SCALE_FAC),
4128 text_width + int(style->columnspace * 2.5));
4129 const short icon_size = 40 * UI_SCALE_FAC;
4130 uiLayout *layout = uiItemsAlertBox(
4131 block, style, dialog_width + icon_size, ALERT_ICON_ERROR, icon_size);
4132
4133 /* Title and explanation text. */
4134 uiLayout *col = &layout->column(true);
4135 uiItemL_ex(col, title, ICON_NONE, true, false);
4136 uiItemL_ex(col, G.autoexec_fail, ICON_NONE, false, true);
4137 col->label(message, ICON_NONE);
4138
4139 layout->separator();
4140
4141 PointerRNA pref_ptr = RNA_pointer_create_discrete(nullptr, &RNA_PreferencesFilePaths, &U);
4142 layout->prop(&pref_ptr, "use_scripts_auto_execute", UI_ITEM_NONE, checkbox_text, ICON_NONE);
4143
4144 layout->separator(2.0f);
4145
4146 /* Buttons. */
4147 uiBut *but;
4148 uiLayout *split = &layout->split(0.0f, true);
4149 uiLayoutSetScaleY(split, 1.2f);
4150
4151 /* Empty space. */
4152 col = &split->column(false);
4153 col->separator();
4154
4155 col = &split->column(false);
4156
4157 /* Allow reload if we have a saved file.
4158 * Otherwise just enable scripts and reset the depsgraphs. */
4159 if ((blendfile_path[0] != '\0') && wm->file_saved) {
4160 but = uiDefIconTextBut(block,
4162 0,
4163 ICON_NONE,
4164 IFACE_("Allow Execution"),
4165 0,
4166 0,
4167 50,
4168 UI_UNIT_Y,
4169 nullptr,
4170 0,
4171 0,
4172 TIP_("Reload file with execution of Python scripts enabled"));
4174 but, [block](bContext &C) { wm_block_autorun_warning_reload_with_scripts(&C, block); });
4175 }
4176 else {
4177 but = uiDefIconTextBut(block,
4179 0,
4180 ICON_NONE,
4181 IFACE_("Allow Execution"),
4182 0,
4183 0,
4184 50,
4185 UI_UNIT_Y,
4186 nullptr,
4187 0,
4188 0,
4189 TIP_("Enable scripts"));
4190 UI_but_func_set(but,
4191 [block](bContext &C) { wm_block_autorun_warning_enable_scripts(&C, block); });
4192 }
4194
4195 col = &split->column(false);
4196 but = uiDefIconTextBut(block,
4198 0,
4199 ICON_NONE,
4200 IFACE_("Ignore"),
4201 0,
4202 0,
4203 50,
4204 UI_UNIT_Y,
4205 nullptr,
4206 0,
4207 0,
4208 TIP_("Continue using file without Python scripts"));
4212
4214
4215 return block;
4216}
4217
4225static struct {
4229
4242
4244{
4247
4248 /* Use regular revert. */
4249 if (ot == nullptr) {
4250 ot = WM_operatortype_find("WM_OT_revert_mainfile", false);
4251 ptr = MEM_new<PointerRNA>(__func__);
4253 RNA_boolean_set(ptr, "use_scripts", true);
4254
4255 /* Set state, so it's freed correctly. */
4257 }
4258
4260 wm_test_autorun_revert_action_set(nullptr, nullptr);
4261}
4262
4264{
4265 /* Test if any auto-execution of scripts failed. */
4266 if ((G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL) == 0) {
4267 return;
4268 }
4269
4270 /* Only show the warning once. */
4272 return;
4273 }
4274
4276
4278 wmWindow *win = (wm->winactive) ? wm->winactive : static_cast<wmWindow *>(wm->windows.first);
4279
4280 if (win) {
4281 /* We want this warning on the Main window, not a child window even if active. See #118765. */
4282 if (win->parent) {
4283 win = win->parent;
4284 }
4285
4286 wmWindow *prevwin = CTX_wm_window(C);
4287 CTX_wm_window_set(C, win);
4289 CTX_wm_window_set(C, prevwin);
4290 }
4291}
4292
4294
4295/* -------------------------------------------------------------------- */
4298
4299static void free_post_file_close_action(void *arg)
4300{
4301 wmGenericCallback *action = (wmGenericCallback *)arg;
4303}
4304
4305static void wm_free_operator_properties_callback(void *user_data)
4306{
4307 IDProperty *properties = (IDProperty *)user_data;
4308 IDP_FreeProperty(properties);
4309}
4310
4311static const char *save_file_overwrite_dialog_name = "save_file_overwrite_popup";
4312
4313static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bmain)
4314{
4315 uiLayout *layout = &parent_layout->column(true);
4316 /* Trick to make both lines of text below close enough to look like they are part of a same
4317 * block. */
4318 uiLayoutSetScaleY(layout, 0.70f);
4319
4321 char writer_ver_str[16];
4322 char current_ver_str[16];
4323 if (bmain->versionfile == BLENDER_VERSION) {
4325 writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile);
4327 current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION);
4328 }
4329 else {
4331 writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
4333 current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1);
4334 }
4335
4336 char message_line1[256];
4337 char message_line2[256];
4338 SNPRINTF(message_line1,
4339 RPT_("This file was saved by a newer version of Blender (%s)."),
4340 writer_ver_str);
4341 SNPRINTF(message_line2,
4342 RPT_("Saving it with this Blender (%s) may cause loss of data."),
4343 current_ver_str);
4344 layout->label(message_line1, ICON_NONE);
4345 layout->label(message_line2, ICON_NONE);
4346 }
4347
4348 if (bmain->is_asset_edit_file) {
4350 layout->separator(1.4f);
4351 }
4352
4353 layout->label(RPT_("This file is managed by the Blender asset system. It can only be"),
4354 ICON_NONE);
4355 layout->label(RPT_("saved as a new, regular file."), ICON_NONE);
4356 }
4357}
4358
4359static void save_file_overwrite_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
4360{
4361 wmWindow *win = CTX_wm_window(C);
4362 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4363}
4364
4366{
4367 uiBut *but = uiDefIconTextBut(
4368 block, UI_BTYPE_BUT, 0, ICON_NONE, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, nullptr, 0, 0, "");
4369 UI_but_func_set(but, save_file_overwrite_cancel, block, post_action);
4371}
4372
4373static void save_file_overwrite_confirm(bContext *C, void *arg_block, void *arg_data)
4374{
4375 wmWindow *win = CTX_wm_window(C);
4376
4377 /* Re-use operator properties as defined for the initial "Save" operator,
4378 * which triggered this "Forward Compatibility" popup. */
4380 static_cast<wmGenericCallback *>(arg_data));
4381
4382 /* Needs to be done after stealing the callback data above, otherwise it would cause a
4383 * use-after-free. */
4384 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4385
4386 PointerRNA operator_propptr = {};
4387 PointerRNA *operator_propptr_p = &operator_propptr;
4388 IDProperty *operator_idproperties = static_cast<IDProperty *>(callback->user_data);
4389 WM_operator_properties_alloc(&operator_propptr_p, &operator_idproperties, "WM_OT_save_mainfile");
4390
4391 WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, operator_propptr_p, nullptr);
4392
4393 WM_generic_callback_free(callback);
4394}
4395
4397{
4398 uiBut *but = uiDefIconTextBut(block,
4400 0,
4401 ICON_NONE,
4402 IFACE_("Overwrite"),
4403 0,
4404 0,
4405 0,
4406 UI_UNIT_Y,
4407 nullptr,
4408 0,
4409 0,
4410 "");
4411 UI_but_func_set(but, save_file_overwrite_confirm, block, post_action);
4414}
4415
4416static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*arg_data*/)
4417{
4418 wmWindow *win = CTX_wm_window(C);
4419 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4420
4421 WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
4422}
4423
4425{
4426 uiBut *but = uiDefIconTextBut(block,
4428 0,
4429 ICON_NONE,
4430 IFACE_("Save As..."),
4431 0,
4432 0,
4433 0,
4434 UI_UNIT_Y,
4435 nullptr,
4436 0,
4437 0,
4438 "");
4439 UI_but_func_set(but, save_file_overwrite_saveas, block, post_action);
4442}
4443
4445{
4446 wmGenericCallback *post_action = static_cast<wmGenericCallback *>(arg1);
4447 Main *bmain = CTX_data_main(C);
4448
4449 uiBlock *block = UI_block_begin(
4454
4455 uiLayout *layout = uiItemsAlertBox(block, 44, ALERT_ICON_WARNING);
4456
4457 /* Title. */
4459 if (bmain->is_asset_edit_file) {
4460 uiItemL_ex(layout,
4461 RPT_("Cannot overwrite asset system files. Save as new file"),
4462 ICON_NONE,
4463 true,
4464 false);
4465 uiItemL_ex(layout, RPT_("with an older Blender version?"), ICON_NONE, true, false);
4466 }
4467 else {
4468 uiItemL_ex(
4469 layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
4470 }
4471 }
4472 else if (bmain->is_asset_edit_file) {
4473 uiItemL_ex(layout,
4474 RPT_("Cannot overwrite asset system files. Save as new file?"),
4475 ICON_NONE,
4476 true,
4477 false);
4478 }
4479 else {
4481 }
4482
4483 /* Filename. */
4484 const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
4485 char filename[FILE_MAX];
4486 if (blendfile_path[0] != '\0') {
4487 BLI_path_split_file_part(blendfile_path, filename, sizeof(filename));
4488 }
4489 else {
4490 SNPRINTF(filename, "%s.blend", DATA_("Untitled"));
4491 /* Since this dialog should only be shown when re-saving an existing file, current filepath
4492 * should never be empty. */
4494 }
4495 layout->label(filename, ICON_NONE);
4496
4497 /* Detailed message info. */
4498 file_overwrite_detailed_info_show(layout, bmain);
4499
4500 layout->separator(4.0f);
4501
4502 /* Buttons. */
4503
4504 uiLayout *split = &layout->split(0.3f, true);
4505 uiLayoutSetScaleY(split, 1.2f);
4506
4507 split->column(false);
4508 /* Asset files don't actually allow overriding. */
4509 const bool allow_overwrite = !bmain->is_asset_edit_file;
4510 if (allow_overwrite) {
4511 save_file_overwrite_confirm_button(block, post_action);
4512 }
4513
4514 uiLayout *split_right = &split->split(0.1f, true);
4515
4516 split_right->column(false);
4517 /* Empty space. */
4518
4519 split_right->column(false);
4520 save_file_overwrite_cancel_button(block, post_action);
4521
4522 split_right->column(false);
4523 save_file_overwrite_saveas_button(block, post_action);
4524
4526 return block;
4527}
4528
4541
4543
4544/* -------------------------------------------------------------------- */
4547
4549
4550static void wm_block_file_close_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
4551{
4552 wmWindow *win = CTX_wm_window(C);
4553 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4554}
4555
4556static void wm_block_file_close_discard(bContext *C, void *arg_block, void *arg_data)
4557{
4559
4560 /* Close the popup before executing the callback. Otherwise
4561 * the popup might be closed by the callback, which will lead
4562 * to a crash. */
4563 wmWindow *win = CTX_wm_window(C);
4564 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4565
4566 callback->exec(C, callback->user_data);
4567 WM_generic_callback_free(callback);
4568}
4569
4570static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_data)
4571{
4572 const Main *bmain = CTX_data_main(C);
4573 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
4575 bool execute_callback = true;
4576
4577 wmWindow *win = CTX_wm_window(C);
4578 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4579
4580 int modified_images_count = ED_image_save_all_modified_info(CTX_data_main(C), nullptr);
4581 if (modified_images_count > 0 && save_images_when_file_is_closed) {
4582 if (ED_image_should_save_modified(bmain)) {
4586 }
4587 else {
4588 execute_callback = false;
4589 }
4590 }
4591
4592 bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0';
4593
4594 if (file_has_been_saved_before) {
4596 /* Need to invoke to get the file-browser and choose where to save the new file.
4597 * This also makes it impossible to keep on going with current operation, which is why
4598 * callback cannot be executed anymore.
4599 *
4600 * This is the same situation as what happens when the file has never been saved before
4601 * (outer `else` statement, below). */
4602 WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
4603 execute_callback = false;
4604 }
4605 else {
4606 if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, nullptr, nullptr) &
4608 {
4609 execute_callback = false;
4610 }
4611 }
4612 }
4613 else {
4614 WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
4615 execute_callback = false;
4616 }
4617
4618 if (execute_callback) {
4619 callback->exec(C, callback->user_data);
4620 }
4621 WM_generic_callback_free(callback);
4622}
4623
4625{
4626 uiBut *but = uiDefIconTextBut(
4627 block, UI_BTYPE_BUT, 0, ICON_NONE, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, nullptr, 0, 0, "");
4628 UI_but_func_set(but, wm_block_file_close_cancel, block, post_action);
4630}
4631
4633{
4634 uiBut *but = uiDefIconTextBut(block,
4636 0,
4637 ICON_NONE,
4638 IFACE_("Don't Save"),
4639 0,
4640 0,
4641 0,
4642 UI_UNIT_Y,
4643 nullptr,
4644 0,
4645 0,
4646 "");
4647 UI_but_func_set(but, wm_block_file_close_discard, block, post_action);
4649}
4650
4652 wmGenericCallback *post_action,
4653 const bool needs_overwrite_confirm)
4654{
4655 uiBut *but = uiDefIconTextBut(
4656 block,
4658 0,
4659 ICON_NONE,
4660 /* Forward compatibility issues force using 'save as' operator instead of 'save' one. */
4661 needs_overwrite_confirm ? IFACE_("Save As...") : IFACE_("Save"),
4662 0,
4663 0,
4664 0,
4665 UI_UNIT_Y,
4666 nullptr,
4667 0,
4668 0,
4669 "");
4670 UI_but_func_set(but, wm_block_file_close_save, block, post_action);
4673}
4674
4675static const char *close_file_dialog_name = "file_close_popup";
4676
4677static void save_catalogs_when_file_is_closed_set_fn(bContext * /*C*/, void *arg1, void * /*arg2*/)
4678{
4679 char *save_catalogs_when_file_is_closed = static_cast<char *>(arg1);
4681 *save_catalogs_when_file_is_closed != 0);
4682}
4683
4685{
4686 using namespace blender;
4687 wmGenericCallback *post_action = (wmGenericCallback *)arg1;
4688 Main *bmain = CTX_data_main(C);
4689
4690 uiBlock *block = UI_block_begin(
4695
4696 uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION);
4697
4698 const bool needs_overwrite_confirm = BKE_main_needs_overwrite_confirm(bmain);
4699
4700 /* Title. */
4701 uiItemL_ex(layout, RPT_("Save changes before closing?"), ICON_NONE, true, false);
4702
4703 /* Filename. */
4704 const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
4705 char filename[FILE_MAX];
4706 if (blendfile_path[0] != '\0') {
4707 BLI_path_split_file_part(blendfile_path, filename, sizeof(filename));
4708 }
4709 else {
4710 SNPRINTF(filename, "%s.blend", DATA_("Untitled"));
4711 }
4712 layout->label(filename, ICON_NONE);
4713
4714 /* Potential forward compatibility issues message. */
4715 if (needs_overwrite_confirm) {
4716 file_overwrite_detailed_info_show(layout, bmain);
4717 }
4718
4719 /* Image Saving Warnings. */
4722 uint modified_images_count = ED_image_save_all_modified_info(bmain, &reports);
4723
4724 LISTBASE_FOREACH (Report *, report, &reports.list) {
4725 uiLayout *row = &layout->column(false);
4726 uiLayoutSetScaleY(row, 0.6f);
4727 row->separator();
4728
4729 /* Error messages created in ED_image_save_all_modified_info() can be long,
4730 * but are made to separate into two parts at first colon between text and paths.
4731 */
4732 char *message = BLI_strdupn(report->message, report->len);
4733 char *path_info = strstr(message, ": ");
4734 if (path_info) {
4735 /* Terminate message string at colon. */
4736 path_info[1] = '\0';
4737 /* Skip over the ": ". */
4738 path_info += 2;
4739 }
4740 uiItemL_ex(row, message, ICON_NONE, false, true);
4741 if (path_info) {
4742 uiItemL_ex(row, path_info, ICON_NONE, false, true);
4743 }
4744 MEM_freeN(message);
4745 }
4746
4747 /* Used to determine if extra separators are needed. */
4748 bool has_extra_checkboxes = false;
4749
4750 /* Modified Images Checkbox. */
4751 if (modified_images_count > 0) {
4752 char message[64];
4753 SNPRINTF(message, "Save %u modified image(s)", modified_images_count);
4754 /* Only the first checkbox should get extra separation. */
4755 if (!has_extra_checkboxes) {
4756 layout->separator();
4757 }
4758 uiDefButBitC(block,
4760 1,
4761 0,
4762 message,
4763 0,
4764 0,
4765 0,
4766 UI_UNIT_Y,
4768 0,
4769 0,
4770 "");
4771 has_extra_checkboxes = true;
4772 }
4773
4775 static char save_catalogs_when_file_is_closed;
4776
4777 save_catalogs_when_file_is_closed = ed::asset::catalogs_get_save_catalogs_when_file_is_saved();
4778
4779 /* Only the first checkbox should get extra separation. */
4780 if (!has_extra_checkboxes) {
4781 layout->separator();
4782 }
4783 uiBut *but = uiDefButBitC(block,
4785 1,
4786 0,
4787 "Save modified asset catalogs",
4788 0,
4789 0,
4790 0,
4791 UI_UNIT_Y,
4792 &save_catalogs_when_file_is_closed,
4793 0,
4794 0,
4795 "");
4796 UI_but_func_set(but,
4798 &save_catalogs_when_file_is_closed,
4799 nullptr);
4800 has_extra_checkboxes = true;
4801 }
4802
4804
4805 layout->separator(2.0f);
4806
4807 /* Buttons. */
4808#ifdef _WIN32
4809 const bool windows_layout = true;
4810#else
4811 const bool windows_layout = false;
4812#endif
4813
4814 if (windows_layout) {
4815 /* Windows standard layout. */
4816
4817 uiLayout *split = &layout->split(0.0f, true);
4818 uiLayoutSetScaleY(split, 1.2f);
4819
4820 split->column(false);
4821 wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm);
4822
4823 split->column(false);
4824 wm_block_file_close_discard_button(block, post_action);
4825
4826 split->column(false);
4827 wm_block_file_close_cancel_button(block, post_action);
4828 }
4829 else {
4830 /* Non-Windows layout (macOS and Linux). */
4831
4832 uiLayout *split = &layout->split(0.3f, true);
4833 uiLayoutSetScaleY(split, 1.2f);
4834
4835 split->column(false);
4836 wm_block_file_close_discard_button(block, post_action);
4837
4838 uiLayout *split_right = &split->split(0.1f, true);
4839
4840 split_right->column(false);
4841 /* Empty space. */
4842
4843 split_right->column(false);
4844 wm_block_file_close_cancel_button(block, post_action);
4845
4846 split_right->column(false);
4847 wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm);
4848 }
4849
4851 return block;
4852}
4853
4864
4866 wmOperator *op,
4867 wmGenericCallbackFn post_action_fn)
4868{
4869 if (U.uiflag & USER_SAVE_PROMPT &&
4871 {
4873 callback->exec = post_action_fn;
4874 callback->user_data = IDP_CopyProperty(op->properties);
4876 wm_close_file_dialog(C, callback);
4877 return true;
4878 }
4879
4880 return false;
4881}
4882
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
@ BLENDER_USER_CONFIG
#define BLENDER_USERPREF_FILE
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_BACKGROUND_NO_DEPSGRAPH
@ G_FILE_RECOVER_READ
@ G_FILE_AUTOPACK
@ G_FILE_RECOVER_WRITE
@ G_FILE_NO_UI
@ G_FILE_COMPRESS
@ 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
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:827
bool BKE_main_needs_overwrite_confirm(const Main *bmain)
Definition main.cc:479
#define BLEN_THUMB_SIZE
Definition BKE_main.hh:657
ImBuf * BKE_main_thumbnail_to_imbuf(Main *bmain, BlendThumbnail *data)
Definition main.cc:851
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:877
BlendThumbnail * BKE_main_thumbnail_from_buffer(Main *bmain, const uint8_t *rect, const int size[2])
Definition main.cc:805
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:882
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, bool use_camera_view_bounds, 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)
#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)
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)
@ UI_BLOCK_THEME_STYLE_POPUP
void UI_fontstyle_set(const uiFontStyle *fs)
void UI_but_drawflag_disable(uiBut *but, int flag)
@ UI_BUT_TEXT_LEFT
void UI_block_bounds_set_centered(uiBlock *block, int addval)
Definition interface.cc:667
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BTYPE_BUT
@ UI_BTYPE_CHECKBOX
@ UI_BUT_REDALERT
@ UI_BUT_ACTIVE_DEFAULT
@ UI_BLOCK_NUMSELECT
@ UI_BLOCK_LOOP
@ UI_BLOCK_KEEP_OPEN
@ UI_BLOCK_NO_WIN_CLIP
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
#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
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#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
struct BlendFileReadReport::@142320212234231245113115306176140270124216310033 duration
double lib_overrides_recursive_resync
struct BlendFileReadReport::@322005145130211163057315230271062050320025262277 count
int resynced_lib_overrides_libraries_count
wmWindowManager * old_wm
const BlendThumbnail * thumb
eBLO_WritePathRemap remap_mode
FileReaderSeekFn seek
FileReaderCloseFn close
FileReaderReadFn read
bool is_untrusted
Definition wm_files.cc:3298
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:3119
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
static struct @065355016347034316223361120114104026105157201273 wm_test_autorun_revert_action_data
void WM_autosave_write(wmWindowManager *wm, Main *bmain)
Definition wm_files.cc:2340
static void free_post_file_close_action(void *arg)
Definition wm_files.cc:4299
void WM_file_autosave_init(wmWindowManager *wm)
Definition wm_files.cc:2383
bool WM_autosave_is_scheduled(wmWindowManager *wm)
Definition wm_files.cc:2335
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:2375
static void create_operator_state(wmOperatorType *ot, int first_state)
Definition wm_files.cc:3099
#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:4865
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:3926
static wmOperatorStatus wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3507
static bool wm_save_mainfile_check(bContext *, wmOperator *op)
Definition wm_files.cc:3813
static wmOperatorStatus wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3245
static wmOperatorStatus wm_homefile_read_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2839
static uiBlock * block_create__close_file_dialog(bContext *C, ARegion *region, void *arg1)
Definition wm_files.cc:4684
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:2818
static wmOperatorStatus wm_history_file_read_exec(bContext *, wmOperator *)
Definition wm_files.cc:2811
static void wm_open_mainfile_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:3151
static bool wm_file_write_check_with_report_on_failure(Main *bmain, const char *filepath, ReportList *reports)
Definition wm_files.cc:2087
#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:4230
#define BKE_READ_EXOTIC_FAIL_OPEN
Definition wm_files.cc:553
ClearRecentInclude
Definition wm_files.cc:3983
@ CLEAR_RECENT_MISSING
Definition wm_files.cc:3983
@ CLEAR_RECENT_ALL
Definition wm_files.cc:3983
static uiBlock * block_create_autorun_warning(bContext *C, ARegion *region, void *)
Definition wm_files.cc:4098
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:3083
void WM_OT_save_as_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3847
void WM_OT_open_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3363
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:3495
static void wm_block_file_close_cancel_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4624
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:4227
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:2370
static void rna_struct_update_when_changed(bContext *C, Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b)
Definition wm_files.cc:2658
void WM_OT_recover_last_session(wmOperatorType *ot)
Definition wm_files.cc:3523
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:3172
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:4079
static void save_file_overwrite_saveas(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4416
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:3440
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:3571
void wm_history_file_read()
Definition wm_files.cc:1610
void wm_autosave_delete()
Definition wm_files.cc:2414
static bool wm_open_mainfile_check(bContext *, wmOperator *op)
Definition wm_files.cc:3302
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:2947
static OperatorDispatchTarget wm_open_mainfile_dispatch_targets[]
Definition wm_files.cc:3233
#define USERDEF_RESTORE(member)
static void wm_userpref_read_exceptions(UserDef *userdef_curr, const UserDef *userdef_prev)
Definition wm_files.cc:2644
static wmOperatorStatus wm_open_mainfile__open(bContext *C, wmOperator *op)
Definition wm_files.cc:3204
void WM_OT_read_userpref(wmOperatorType *ot)
Definition wm_files.cc:2752
static void wm_test_autorun_revert_action_exec(bContext *C)
Definition wm_files.cc:4243
static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:3501
static char save_images_when_file_is_closed
Definition wm_files.cc:4548
static void save_file_overwrite_cancel_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4365
static void wm_block_file_close_save_button(uiBlock *block, wmGenericCallback *post_action, const bool needs_overwrite_confirm)
Definition wm_files.cc:4651
static wmOperatorStatus wm_userpref_read_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2762
void WM_OT_save_homefile(wmOperatorType *ot)
Definition wm_files.cc:2596
void WM_OT_read_factory_userpref(wmOperatorType *ot)
Definition wm_files.cc:2791
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:3107
static wmOperatorStatus wm_userpref_write_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2613
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:3623
static wmOperatorStatus wm_revert_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3421
void WM_OT_save_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3938
void WM_OT_recover_auto_save(wmOperatorType *ot)
Definition wm_files.cc:3586
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:3836
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:3252
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:3025
static void wm_block_file_close_discard(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4556
void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *)
Definition wm_files.cc:2388
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:3112
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:3541
static struct @242156356073304241142344134225003355320250223123 wm_init_state_app_template
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:3157
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:2692
static const char * close_file_dialog_name
Definition wm_files.cc:4675
static void wm_autosave_location(char filepath[FILE_MAX])
Definition wm_files.cc:2290
static wmOperatorStatus wm_clear_recent_files_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition wm_files.cc:3991
static std::string wm_open_mainfile_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition wm_files.cc:3257
static void save_catalogs_when_file_is_closed_set_fn(bContext *, void *arg1, void *)
Definition wm_files.cc:4677
static wmOperatorStatus operator_state_dispatch(bContext *C, wmOperator *op, OperatorDispatchTarget *targets)
Definition wm_files.cc:3122
static void wm_filepath_default(const Main *bmain, char *filepath)
Definition wm_files.cc:3614
void WM_OT_read_factory_settings(wmOperatorType *ot)
Definition wm_files.cc:3057
static wmOperatorStatus wm_homefile_write_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2567
static bool wm_autosave_write_try(Main *bmain, wmWindowManager *wm)
Definition wm_files.cc:2313
@ OPEN_MAINFILE_STATE_OPEN
Definition wm_files.cc:3146
@ OPEN_MAINFILE_STATE_DISCARD_CHANGES
Definition wm_files.cc:3144
@ OPEN_MAINFILE_STATE_SELECT_FILE_PATH
Definition wm_files.cc:3145
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:4063
static wmOperatorStatus wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3673
static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bmain)
Definition wm_files.cc:4313
void wm_test_autorun_warning(bContext *C)
Definition wm_files.cc:4263
static void wm_block_file_close_cancel(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4550
static wmOperatorStatus wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3884
wmOperatorType * ot
Definition wm_files.cc:4226
static wmOperatorStatus wm_recover_last_session_impl(bContext *C, wmOperator *op, const bool use_scripts_autoexec_check)
Definition wm_files.cc:3477
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:2709
void wm_open_init_load_ui(wmOperator *op, bool use_prefs)
Definition wm_files.cc:2464
static wmOperatorStatus wm_open_mainfile_dispatch(bContext *C, wmOperator *op)
Definition wm_files.cc:3240
static void read_factory_reset_props(wmOperatorType *ot)
Definition wm_files.cc:2441
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:2953
static void wm_clear_recent_files_ui(bContext *, wmOperator *op)
Definition wm_files.cc:4019
static void wm_block_autorun_warning_ignore(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4054
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:2361
static const EnumPropertyItem prop_clear_recent_types[]
Definition wm_files.cc:3985
static void save_file_overwrite_confirm_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4396
static void wm_open_mainfile_def_property_use_scripts(wmOperatorType *ot)
Definition wm_files.cc:3353
#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:2122
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:4305
static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4570
static void save_set_filepath(bContext *C, wmOperator *op)
Definition wm_files.cc:3639
static wmOperatorStatus wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3692
static uiBlock * block_create_save_file_overwrite_dialog(bContext *C, ARegion *region, void *arg1)
Definition wm_files.cc:4444
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:2981
static std::string wm_save_as_mainfile_get_name(wmOperatorType *ot, PointerRNA *ptr)
Definition wm_files.cc:3828
static wmOperatorStatus wm_homefile_write_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2501
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:4632
static void wm_open_mainfile_ui(bContext *, wmOperator *op)
Definition wm_files.cc:3332
static void wm_history_file_update()
Definition wm_files.cc:1699
bool write_crash_blend()
Definition wm_files.cc:2071
static wmOperatorStatus wm_revert_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3402
static void wm_gpu_backend_override_from_userdef()
Definition wm_files.cc:495
void WM_OT_save_userpref(wmOperatorType *ot)
Definition wm_files.cc:2625
static void save_file_overwrite_confirm(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4373
void WM_OT_clear_recent_files(wmOperatorType *ot)
Definition wm_files.cc:4030
bool WM_file_recover_last_session(bContext *C, const bool use_scripts_autoexec_check, ReportList *reports)
Definition wm_files.cc:3465
static void read_homefile_props(wmOperatorType *ot)
Definition wm_files.cc:2963
void wm_save_file_overwrite_dialog(bContext *C, wmOperator *op)
Definition wm_files.cc:4529
static const char * save_file_overwrite_dialog_name
Definition wm_files.cc:4311
static void save_file_overwrite_cancel(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4359
static wmOperatorStatus wm_clear_recent_files_exec(bContext *, wmOperator *op)
Definition wm_files.cc:3999
static void save_file_overwrite_saveas_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4424
void WM_OT_revert_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3446
void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action)
Definition wm_files.cc:4854
bool wm_open_init_use_scripts(wmOperator *op, bool use_prefs)
Definition wm_files.cc:2474
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)