Blender V4.3
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
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_blenlib.h"
37#include "BLI_fileops_types.h"
38#include "BLI_filereader.h"
39#include "BLI_linklist.h"
40#include "BLI_math_time.h"
41#include "BLI_memory_cache.hh"
42#include "BLI_system.h"
43#include "BLI_threads.h"
44#include "BLI_time.h"
45#include "BLI_timer.h"
46#include "BLI_utildefines.h"
47#include BLI_SYSTEM_PID_H
48
49#include "BLO_readfile.hh"
50#include "BLT_translation.hh"
51
52#include "BLF_api.hh"
53
54#include "DNA_object_types.h"
55#include "DNA_scene_types.h"
56#include "DNA_screen_types.h"
57#include "DNA_sequence_types.h"
58#include "DNA_space_types.h"
59#include "DNA_userdef_types.h"
61#include "DNA_workspace_types.h"
62
63#include "AS_asset_library.hh"
64
65#include "BKE_addon.h"
66#include "BKE_appdir.hh"
67#include "BKE_autoexec.hh"
68#include "BKE_blender.hh"
69#include "BKE_blender_version.h"
70#include "BKE_blendfile.hh"
71#include "BKE_callbacks.hh"
72#include "BKE_context.hh"
73#include "BKE_global.hh"
74#include "BKE_idprop.hh"
75#include "BKE_lib_id.hh"
76#include "BKE_lib_override.hh"
77#include "BKE_lib_remap.hh"
78#include "BKE_main.hh"
79#include "BKE_main_namemap.hh"
80#include "BKE_node.hh"
81#include "BKE_packedFile.hh"
82#include "BKE_report.hh"
83#include "BKE_scene.hh"
84#include "BKE_screen.hh"
85#include "BKE_sound.h"
86#include "BKE_undo_system.hh"
87#include "BKE_workspace.hh"
88
89#include "BLO_writefile.hh"
90
91#include "RNA_access.hh"
92#include "RNA_define.hh"
93
94#include "IMB_imbuf.hh"
95#include "IMB_imbuf_types.hh"
96#include "IMB_metadata.hh"
97#include "IMB_thumbs.hh"
98
99#include "ED_asset.hh"
100#include "ED_datafiles.h"
101#include "ED_fileselect.hh"
102#include "ED_image.hh"
103#include "ED_outliner.hh"
104#include "ED_render.hh"
105#include "ED_screen.hh"
106#include "ED_undo.hh"
107#include "ED_util.hh"
108#include "ED_view3d.hh"
109#include "ED_view3d_offscreen.hh"
110
111#include "NOD_composite.hh"
112
113#include "GHOST_C-api.h"
114#include "GHOST_Path-api.hh"
115
116#include "GPU_context.hh"
117
118#include "SEQ_sequencer.hh"
119
120#include "UI_interface.hh"
121#include "UI_resources.hh"
122#include "UI_view2d.hh"
123
124/* Only to report a missing engine. */
125#include "RE_engine.h"
126
127#ifdef WITH_PYTHON
128# include "BPY_extern_python.hh"
129# include "BPY_extern_run.hh"
130#endif
131
132#include "DEG_depsgraph.hh"
133
134#include "WM_api.hh"
135#include "WM_message.hh"
136#include "WM_toolsystem.hh"
137#include "WM_types.hh"
138
139#include "wm.hh"
140#include "wm_event_system.hh"
141#include "wm_files.hh"
142#include "wm_window.hh"
143
144#include "CLG_log.h"
145
146static RecentFile *wm_file_history_find(const char *filepath);
147static void wm_history_file_free(RecentFile *recent);
148static void wm_history_files_free();
149static void wm_history_file_update();
150static void wm_history_file_write();
151
153
154static CLG_LogRef LOG = {"wm.files"};
155
165#define USE_THUMBNAIL_FAST_DOWNSCALE
166
167/* -------------------------------------------------------------------- */
172{
173 wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
174 if (wm->file_saved) {
175 wm->file_saved = 0;
176 /* Notifier that data changed, for save-over warning or header. */
178 }
179}
180
186
189/* -------------------------------------------------------------------- */
206 Main *bmain,
207 const bool is_read_homefile)
208{
209 using namespace blender;
210 BLI_assert(BLI_listbase_count_at_most(&bmain->wm, 2) <= 1);
211 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
212 BlendFileReadWMSetupData *wm_setup_data = MEM_cnew<BlendFileReadWMSetupData>(__func__);
213 wm_setup_data->is_read_homefile = is_read_homefile;
214 /* This info is not always known yet when this function is called. */
215 wm_setup_data->is_factory_startup = false;
216
217 if (wm == nullptr) {
218 return wm_setup_data;
219 }
220
221 /* First wrap up running stuff.
222 *
223 * Code copied from `wm_init_exit.cc`. */
225
226 wmWindow *active_win = CTX_wm_window(C);
227 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
228 CTX_wm_window_set(C, win); /* Needed by operator close callbacks. */
229 WM_event_remove_handlers(C, &win->handlers);
230 WM_event_remove_handlers(C, &win->modalhandlers);
232 }
233 /* Reset active window. */
234 CTX_wm_window_set(C, active_win);
235
236 /* NOTE(@ideasman42): Clear the message bus so it's always cleared on file load.
237 * Otherwise it's cleared when "Load UI" is set (see #USER_FILENOUI and #wm_close_and_free).
238 * However it's _not_ cleared when the UI is kept. This complicates use from add-ons
239 * which can re-register subscribers on file-load. To support this use case,
240 * it's best to have predictable behavior - always clear. */
241 if (wm->message_bus != nullptr) {
243 wm->message_bus = nullptr;
244 }
245
246 /* XXX Hack! We have to clear context popup-region here, because removing all
247 * #wmWindow::modalhandlers above frees the active menu (at least, in the 'startup splash' case),
248 * causing use-after-free error in later handling of the button callbacks in UI code
249 * (see #ui_apply_but_funcs_after()).
250 * Tried solving this by always nullptr-ing context's menu when setting wm/win/etc.,
251 * but it broke popups refreshing (see #47632),
252 * so for now just handling this specific case here. */
253 CTX_wm_region_popup_set(C, nullptr);
254
255 ED_editors_exit(bmain, true);
256
257 /* Asset loading is done by the UI/editors and they keep pointers into it. So make sure to clear
258 * it after UI/editors. */
259 ed::asset::list::storage_exit();
261
262 /* NOTE: `wm_setup_data->old_wm` cannot be set here, as this pointer may be swapped with the
263 * newly read one in `setup_app_data` process (See #swap_wm_data_for_blendfile). */
264
265 return wm_setup_data;
266}
267
269 wmWindowManager *wm,
270 wmWindow *oldwin,
271 wmWindow *win)
272{
273 win->ghostwin = oldwin->ghostwin;
274 win->gpuctx = oldwin->gpuctx;
275 win->active = oldwin->active;
276 if (win->active) {
277 wm->winactive = win;
278 }
279 if (oldwm->windrawable == oldwin) {
280 oldwm->windrawable = nullptr;
281 wm->windrawable = win;
282 }
283
284 /* File loading in background mode still calls this. */
285 if (!G.background) {
286 /* Pointer back. */
287 GHOST_SetWindowUserData(static_cast<GHOST_WindowHandle>(win->ghostwin), win);
288 }
289
290 oldwin->ghostwin = nullptr;
291 oldwin->gpuctx = nullptr;
292
293 win->eventstate = oldwin->eventstate;
295 oldwin->eventstate = nullptr;
296 oldwin->event_last_handled = nullptr;
297
298 /* Ensure proper screen re-scaling. */
299 win->sizex = oldwin->sizex;
300 win->sizey = oldwin->sizey;
301 win->posx = oldwin->posx;
302 win->posy = oldwin->posy;
303}
304
315 Main *bmain,
316 BlendFileReadWMSetupData *wm_setup_data,
317 wmWindowManager *wm,
318 const bool load_ui)
319{
320 /* This data is not needed here, besides detecting that old WM has been kept (in caller code).
321 * Since `old_wm` is kept, do not free it, just clear the pointer as clean-up. */
322 wm_setup_data->old_wm = nullptr;
323
324 if (!load_ui) {
325 /* When loading without UI (i.e. keeping existing UI), no matching needed.
326 *
327 * The other UI data (workspaces, layouts, screens) has also been re-used from old Main, and
328 * newly read one from file has already been discarded in #setup_app_data. */
329 return;
330 }
331
332 /* Old WM is being reused, but other UI data (workspaces, layouts, screens) comes from the new
333 * file, so the WM needs to be updated to use these. */
334 bScreen *screen = CTX_wm_screen(C);
335 if (screen != nullptr) {
336 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
337 WorkSpace *workspace;
338
339 WorkSpaceLayout *layout_ref = BKE_workspace_layout_find_global(bmain, screen, &workspace);
340 BKE_workspace_active_set(win->workspace_hook, workspace);
341 win->scene = CTX_data_scene(C);
342
343 /* All windows get active screen from file. */
344 if (screen->winid == 0) {
345 WM_window_set_active_screen(win, workspace, screen);
346 }
347 else {
348#if 0
349 /* NOTE(@ideasman42): The screen referenced from the window has been freed,
350 * see: #107525. */
352#endif
354 bmain, workspace, layout_ref, win);
355
356 WM_window_set_active_layout(win, workspace, layout_new);
357 }
358
359 bScreen *win_screen = WM_window_get_active_screen(win);
360 win_screen->winid = win->winid;
361 }
362 }
363}
364
366 Main * /*bmain*/,
367 BlendFileReadWMSetupData *wm_setup_data,
368 wmWindowManager *wm)
369{
370 wmWindowManager *old_wm = wm_setup_data->old_wm;
371
372 wm->op_undo_depth = old_wm->op_undo_depth;
373
374 /* Move existing key configurations into the new WM. */
375 wm->keyconfigs = old_wm->keyconfigs;
376 wm->addonconf = old_wm->addonconf;
377 wm->defaultconf = old_wm->defaultconf;
378 wm->userconf = old_wm->userconf;
379
381 old_wm->addonconf = nullptr;
382 old_wm->defaultconf = nullptr;
383 old_wm->userconf = nullptr;
384
385 /* Ensure new keymaps are made, and space types are set. */
386 wm->init_flag = 0;
387 wm->winactive = nullptr;
388
389 /* Clearing drawable of old WM before deleting any context to avoid clearing the wrong wm. */
391
392 bool has_match = false;
393 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
394 LISTBASE_FOREACH (wmWindow *, old_win, &old_wm->windows) {
395 if (old_win->winid == win->winid) {
396 has_match = true;
397
398 wm_file_read_setup_wm_substitute_old_window(old_wm, wm, old_win, win);
399 }
400 }
401 }
402 /* Ensure that at least one window is kept open so we don't lose the context, see #42303. */
403 if (!has_match) {
405 wm,
406 static_cast<wmWindow *>(old_wm->windows.first),
407 static_cast<wmWindow *>(wm->windows.first));
408 }
409
410 wm_setup_data->old_wm = nullptr;
411 wm_close_and_free(C, old_wm);
412 /* Don't handle user counts as this is only ever called once #G_MAIN has already been freed via
413 * #BKE_main_free so any access to ID's referenced by the window-manager (from ID properties)
414 * will crash. See: #100703. */
415 BKE_libblock_free_data(&old_wm->id, false);
417 MEM_freeN(old_wm);
418}
419
427 Main *bmain,
428 BlendFileReadWMSetupData *wm_setup_data)
429{
430 BLI_assert(BLI_listbase_count_at_most(&bmain->wm, 2) <= 1);
431 BLI_assert(wm_setup_data != nullptr);
432 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
433
434 /* If reading factory startup file, and there was no previous WM, clear the size of the windows
435 * in newly read WM so that they get resized to occupy the whole available space on current
436 * monitor.
437 */
438 if (wm_setup_data->is_read_homefile && wm_setup_data->is_factory_startup &&
439 wm_setup_data->old_wm == nullptr)
440 {
442 }
443
444 if (wm == nullptr) {
445 /* Add a default WM in case none exists in newly read main (should only happen when opening
446 * an old pre-2.5 .blend file at startup). */
447 wm_add_default(bmain, C);
448 }
449 else if (wm_setup_data->old_wm != nullptr) {
450 if (wm_setup_data->old_wm == wm) {
451 /* Old WM was kept, update it with new workspaces/layouts/screens read from file.
452 *
453 * Happens when not loading UI, or when the newly read file has no WM (pre-2.5 files). */
455 C, bmain, wm_setup_data, wm, (G.fileflags & G_FILE_NO_UI) == 0);
456 }
457 else {
458 /* Using new WM from read file, try to keep current GHOST windows, transfer keymaps, etc.,
459 * from old WM.
460 *
461 * Also takes care of clearing old WM data (temporarily stored in `wm_setup_data->old_wm`).
462 */
463 wm_file_read_setup_wm_use_new(C, bmain, wm_setup_data, wm);
464 }
465 }
466 /* Else just using the new WM read from file, nothing to do. */
467 BLI_assert(wm_setup_data->old_wm == nullptr);
468 MEM_delete(wm_setup_data);
469
470 /* UI Updates. */
471 /* Flag local View3D's to check and exit if they are empty. */
472 LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
473 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
474 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
475 if (sl->spacetype == SPACE_VIEW3D) {
476 View3D *v3d = reinterpret_cast<View3D *>(sl);
477 if (v3d->localvd) {
479 }
480 }
481 }
482 }
483 }
484}
485
488/* -------------------------------------------------------------------- */
493{
494 /* Check if GPU backend is already set from the command line arguments. The command line
495 * arguments have higher priority than user preferences. */
497 return;
498 }
499
501}
502
506static void wm_init_userdef(Main *bmain)
507{
508 /* Not versioning, just avoid errors. */
509#ifndef WITH_CYCLES
510 BKE_addon_remove_safe(&U.addons, "cycles");
511#endif
512
514
515 /* Needed so loading a file from the command line respects user-pref #26156. */
516 SET_FLAG_FROM_TEST(G.fileflags, U.flag & USER_FILENOUI, G_FILE_NO_UI);
517
518 /* Set the python auto-execute setting from user prefs. */
519 /* Enabled by default, unless explicitly enabled in the command line which overrides. */
520 if ((G.f & G_FLAG_SCRIPT_OVERRIDE_PREF) == 0) {
522 }
523
524 /* Only reset "offline mode" if they weren't passes via command line arguments. */
525 if ((G.f & G_FLAG_INTERNET_OVERRIDE_PREF_ANY) == 0) {
527 }
528
529 const int64_t cache_limit = int64_t(U.memcachelimit) * 1024 * 1024;
530 MEM_CacheLimiter_set_maximum(cache_limit);
532
533 BKE_sound_init(bmain);
534
535 /* Update the temporary directory from the preferences or fallback to the system default. */
536 BKE_tempdir_init(U.tempdir);
537
538 /* Update input device preference. */
540
542
545}
546
547/* Return codes. */
548#define BKE_READ_EXOTIC_FAIL_PATH -3 /* File format is not supported. */
549#define BKE_READ_EXOTIC_FAIL_FORMAT -2 /* File format is not supported. */
550#define BKE_READ_EXOTIC_FAIL_OPEN -1 /* Can't open the file. */
551#define BKE_READ_EXOTIC_OK_BLEND 0 /* `.blend` file. */
552#if 0
553# define BKE_READ_EXOTIC_OK_OTHER 1 /* Other supported formats. */
554#endif
555
558/* -------------------------------------------------------------------- */
565/* Intended to check for non-blender formats but for now it only reads blends. */
566static int wm_read_exotic(const char *filepath)
567{
568 /* Make sure we're not trying to read a directory. */
569
570 int filepath_len = strlen(filepath);
571 if (filepath_len > 0 && ELEM(filepath[filepath_len - 1], '/', '\\')) {
573 }
574
575 /* Open the file. */
576 const int filedes = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
577 if (filedes == -1) {
579 }
580
581 FileReader *rawfile = BLI_filereader_new_file(filedes);
582 if (rawfile == nullptr) {
584 }
585
586 /* Read the header (7 bytes are enough to identify all known types). */
587 char header[7];
588 if (rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) {
589 rawfile->close(rawfile);
591 }
592 rawfile->seek(rawfile, 0, SEEK_SET);
593
594 /* Check for uncompressed `.blend`. */
595 if (STREQLEN(header, "BLENDER", 7)) {
596 rawfile->close(rawfile);
598 }
599
600 /* Check for compressed `.blend`. */
601 FileReader *compressed_file = nullptr;
602 if (BLI_file_magic_is_gzip(header)) {
603 /* In earlier versions of Blender (before 3.0), compressed files used `Gzip` instead of `Zstd`.
604 * While these files will no longer be written, there still needs to be reading support. */
605 compressed_file = BLI_filereader_new_gzip(rawfile);
606 }
607 else if (BLI_file_magic_is_zstd(header)) {
608 compressed_file = BLI_filereader_new_zstd(rawfile);
609 }
610
611 /* If a compression signature matches,
612 * try decompressing the start and check if it's a `.blend`. */
613 if (compressed_file != nullptr) {
614 size_t len = compressed_file->read(compressed_file, header, sizeof(header));
615 compressed_file->close(compressed_file);
616 if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) {
618 }
619 }
620 else {
621 rawfile->close(rawfile);
622 }
623
624 /* Add check for future file formats here. */
625
627}
628
631/* -------------------------------------------------------------------- */
635void WM_file_autoexec_init(const char *filepath)
636{
638 return;
639 }
640
641 if (G.f & G_FLAG_SCRIPT_AUTOEXEC) {
642 char dirpath[FILE_MAX];
643 BLI_path_split_dir_part(filepath, dirpath, sizeof(dirpath));
644 if (BKE_autoexec_match(dirpath)) {
645 G.f &= ~G_FLAG_SCRIPT_AUTOEXEC;
646 }
647 }
648}
649
651{
652 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
653 ReportList *reports = &wm->runtime->reports;
654 bool found = false;
655 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
656 if (scene->r.engine[0] &&
657 BLI_findstring(&R_engines, scene->r.engine, offsetof(RenderEngineType, idname)) == nullptr)
658 {
659 BKE_reportf(reports,
660 RPT_ERROR,
661 "Engine '%s' not available for scene '%s' (an add-on may need to be installed "
662 "or enabled)",
663 scene->r.engine,
664 scene->id.name + 2);
665 found = true;
666 }
667 }
668
669 if (found) {
670 if (!G.background) {
671 WM_report_banner_show(wm, win);
672 }
673 }
674}
675
682static void wm_file_read_pre(bool use_data, bool /*use_userdef*/)
683{
684 if (use_data) {
686 }
687
688 /* Always do this as both startup and preferences may have loaded in many font's
689 * at a different zoom level to the file being loaded. */
691
693}
694
711
717 const char *filepath,
719{
721
722 const bool use_data = params->use_data;
723 const bool use_userdef = params->use_userdef;
724 const bool is_startup_file = params->is_startup_file;
725 const bool is_factory_startup = params->is_factory_startup;
726 const bool reset_app_template = params->reset_app_template;
727
728 bool addons_loaded = false;
729
730 if (use_data) {
731 if (!G.background) {
732 /* Remove windows which failed to be added via #WM_check. */
734 }
735 CTX_wm_window_set(C, static_cast<wmWindow *>(wm->windows.first));
736 }
737
738#ifdef WITH_PYTHON
739 if (is_startup_file) {
740 /* The following block handles data & preferences being reloaded
741 * which requires resetting some internal variables. */
742 if (!params->is_first_time) {
744 bool reset_all = use_userdef;
745 if (use_userdef || reset_app_template) {
746 /* Only run when we have a template path found. */
748 const char *imports[] = {"bl_app_template_utils", nullptr};
749 BPY_run_string_eval(C, imports, "bl_app_template_utils.reset()");
750 reset_all = true;
751 }
752 }
753 if (reset_all) {
754 const char *imports[] = {"bpy", "addon_utils", nullptr};
756 C,
757 imports,
758 /* Refresh scripts as the preferences may have changed the user-scripts path.
759 *
760 * This is needed when loading settings from the previous version,
761 * otherwise the script path stored in the preferences would be ignored. */
762 "bpy.utils.refresh_script_paths()\n"
763 /* Sync add-ons, these may have changed from the defaults. */
764 "addon_utils.reset_all()");
765 }
766 if (use_data) {
768 }
769 addons_loaded = true;
770 }
771 }
772 else {
773 /* Run any texts that were loaded in and flagged as modules. */
774 if (use_data) {
776 }
777 addons_loaded = true;
778 }
779#else
780 UNUSED_VARS(is_startup_file, reset_app_template);
781#endif /* WITH_PYTHON */
782
783 Main *bmain = CTX_data_main(C);
784
785 if (use_userdef) {
786 if (is_factory_startup) {
788 }
789 }
790
791 if (is_factory_startup && BLT_translate_new_dataname()) {
792 /* Translate workspace names. */
793 LISTBASE_FOREACH_MUTABLE (WorkSpace *, workspace, &bmain->workspaces) {
795 *bmain, workspace->id, CTX_DATA_(BLT_I18NCONTEXT_ID_WORKSPACE, workspace->id.name + 2));
796 }
797 }
798
799 if (use_data) {
800 /* Important to do before nulling the context. */
802
803 /* Load-post must run before evaluating drivers & depsgraph, see: #109720.
804 * On failure, the caller handles #BKE_CB_EVT_LOAD_POST_FAIL. */
805 if (params->success) {
807 }
808
809 if (is_factory_startup) {
811 }
812 }
813
814 if (use_data) {
816
817 /* After load post, so for example the driver namespace can be filled
818 * before evaluating the depsgraph. */
819 wm_event_do_depsgraph(C, true);
820
822
823 /* Add-ons are disabled when loading the startup file, so the Render Layer node in compositor
824 * node trees might be wrong due to missing render engines that are available as add-ons, like
825 * Cycles. So we need to update compositor node trees after reading the file when add-ons are
826 * now loaded. */
827 if (is_startup_file) {
828 FOREACH_NODETREE_BEGIN (bmain, node_tree, owner_id) {
829 if (node_tree->type == NTREE_COMPOSIT) {
831 }
832 }
834 }
835
836#if 1
838 /* Clear static filtered asset tree caches. */
840#else
842#endif
843 }
844
845 /* Report any errors.
846 * Currently disabled if add-ons aren't yet loaded. */
847 if (addons_loaded) {
848 wm_file_read_report(bmain, static_cast<wmWindow *>(wm->windows.first));
849 }
850
851 if (use_data) {
852 if (!G.background) {
853 if (wm->undo_stack == nullptr) {
855 }
856 else {
858 }
861 }
862 }
863
864 if (use_data) {
865 if (!G.background) {
866 /* In background mode this makes it hard to load
867 * a blend file and do anything since the screen
868 * won't be set to a valid value again. */
869 CTX_wm_window_set(C, nullptr); /* Exits queues. */
870
871 /* Ensure auto-run action is not used from a previous blend file load. */
872 wm_test_autorun_revert_action_set(nullptr, nullptr);
873
874 /* Ensure tools are registered. */
876 }
877 }
878}
879
880static void wm_read_callback_pre_wrapper(bContext *C, const char *filepath)
881{
882 /* NOTE: either #BKE_CB_EVT_LOAD_POST or #BKE_CB_EVT_LOAD_POST_FAIL must run.
883 * Runs at the end of this function, don't return beforehand. */
885}
886
887static void wm_read_callback_post_wrapper(bContext *C, const char *filepath, const bool success)
888{
889 Main *bmain = CTX_data_main(C);
890 /* Temporarily set the window context as this was once supported, see: #107759.
891 * If the window is already set, don't change it. */
892 bool has_window = CTX_wm_window(C) != nullptr;
893 if (!has_window) {
894 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
895 wmWindow *win = static_cast<wmWindow *>(wm->windows.first);
896 CTX_wm_window_set(C, win);
897 }
898
899 /* On success: #BKE_CB_EVT_LOAD_POST runs from #wm_file_read_post. */
900 if (success == false) {
902 }
903
904 /* This function should leave the window null when the function entered. */
905 if (!has_window) {
906 CTX_wm_window_set(C, nullptr);
907 }
908}
909
912/* -------------------------------------------------------------------- */
917{
918 double duration_whole_minutes, duration_whole_seconds;
919 double duration_libraries_minutes, duration_libraries_seconds;
920 double duration_lib_override_minutes, duration_lib_override_seconds;
921 double duration_lib_override_resync_minutes, duration_lib_override_resync_seconds;
922 double duration_lib_override_recursive_resync_minutes,
923 duration_lib_override_recursive_resync_seconds;
924
926 nullptr,
927 nullptr,
928 &duration_whole_minutes,
929 &duration_whole_seconds,
930 nullptr);
932 nullptr,
933 nullptr,
934 &duration_libraries_minutes,
935 &duration_libraries_seconds,
936 nullptr);
938 nullptr,
939 nullptr,
940 &duration_lib_override_minutes,
941 &duration_lib_override_seconds,
942 nullptr);
944 nullptr,
945 nullptr,
946 &duration_lib_override_resync_minutes,
947 &duration_lib_override_resync_seconds,
948 nullptr);
950 nullptr,
951 nullptr,
952 &duration_lib_override_recursive_resync_minutes,
953 &duration_lib_override_recursive_resync_seconds,
954 nullptr);
955
956 CLOG_INFO(
957 &LOG, 0, "Blender file read in %.0fm%.2fs", duration_whole_minutes, duration_whole_seconds);
958 CLOG_INFO(&LOG,
959 0,
960 " * Loading libraries: %.0fm%.2fs",
961 duration_libraries_minutes,
962 duration_libraries_seconds);
963 CLOG_INFO(&LOG,
964 0,
965 " * Applying overrides: %.0fm%.2fs",
966 duration_lib_override_minutes,
967 duration_lib_override_seconds);
968 CLOG_INFO(&LOG,
969 0,
970 " * Resyncing overrides: %.0fm%.2fs (%d root overrides), including recursive "
971 "resyncs: %.0fm%.2fs)",
972 duration_lib_override_resync_minutes,
973 duration_lib_override_resync_seconds,
974 bf_reports->count.resynced_lib_overrides,
975 duration_lib_override_recursive_resync_minutes,
976 duration_lib_override_recursive_resync_seconds);
977
978 if (bf_reports->resynced_lib_overrides_libraries_count != 0) {
979 for (LinkNode *node_lib = bf_reports->resynced_lib_overrides_libraries; node_lib != nullptr;
980 node_lib = node_lib->next)
981 {
982 Library *library = static_cast<Library *>(node_lib->link);
983 BKE_reportf(bf_reports->reports,
984 RPT_INFO,
985 "Library \"%s\" needs overrides resync",
986 library->filepath);
987 }
988 }
989
990 if (bf_reports->count.missing_libraries != 0 || bf_reports->count.missing_linked_id != 0) {
991 BKE_reportf(bf_reports->reports,
993 "%d libraries and %d linked data-blocks are missing (including %d ObjectData and "
994 "%d Proxies), please check the Info and Outliner editors for details",
995 bf_reports->count.missing_libraries,
996 bf_reports->count.missing_linked_id,
997 bf_reports->count.missing_obdata,
998 bf_reports->count.missing_obproxies);
999 }
1000 else {
1001 if (bf_reports->count.missing_obdata != 0 || bf_reports->count.missing_obproxies != 0) {
1002 CLOG_ERROR(&LOG,
1003 "%d local ObjectData and %d local Object proxies are reported to be missing, "
1004 "this should never happen",
1005 bf_reports->count.missing_obdata,
1006 bf_reports->count.missing_obproxies);
1007 }
1008 }
1009
1010 if (bf_reports->resynced_lib_overrides_libraries_count != 0) {
1011 BKE_reportf(bf_reports->reports,
1013 "%d libraries have overrides needing resync (auto resynced in %.0fm%.2fs), "
1014 "please check the Info editor for details",
1016 duration_lib_override_recursive_resync_minutes,
1017 duration_lib_override_recursive_resync_seconds);
1018 }
1019
1020 if (bf_reports->count.proxies_to_lib_overrides_success != 0 ||
1022 {
1023 BKE_reportf(bf_reports->reports,
1025 "Proxies have been removed from Blender (%d proxies were automatically converted "
1026 "to library overrides, %d proxies could not be converted and were cleared). "
1027 "Consider re-saving any library .blend file with the newest Blender version",
1030 }
1031
1032 if (bf_reports->count.sequence_strips_skipped != 0) {
1033 BKE_reportf(bf_reports->reports,
1034 RPT_ERROR,
1035 "%d sequence strips were not read because they were in a channel larger than %d",
1036 bf_reports->count.sequence_strips_skipped,
1038 }
1039
1041 bf_reports->resynced_lib_overrides_libraries = nullptr;
1042}
1043
1044bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
1045{
1046 /* Assume automated tasks with background, don't write recent file list. */
1047 const bool do_history_file_update = (G.background == false) &&
1048 (CTX_wm_manager(C)->op_undo_depth == 0);
1049 bool success = false;
1050
1051 const bool use_data = true;
1052 const bool use_userdef = false;
1053
1054 /* NOTE: a matching #wm_read_callback_post_wrapper must be called. */
1055 wm_read_callback_pre_wrapper(C, filepath);
1056
1057 Main *bmain = CTX_data_main(C);
1058
1059 /* So we can get the error message. */
1060 errno = 0;
1061
1062 WM_cursor_wait(true);
1063
1064 /* First try to append data from exotic file formats. */
1065 /* It throws error box when file doesn't exist and returns -1. */
1066 /* NOTE(ton): it should set some error message somewhere. */
1067 const int retval = wm_read_exotic(filepath);
1068
1069 /* We didn't succeed, now try to read Blender file. */
1070 if (retval == BKE_READ_EXOTIC_OK_BLEND) {
1072 params.is_startup = false;
1073 /* Loading preferences when the user intended to load a regular file is a security
1074 * risk, because the excluded path list is also loaded. Further it's just confusing
1075 * if a user loads a file and various preferences change. */
1076 params.skip_flags = BLO_READ_SKIP_USERDEF;
1077
1078 BlendFileReadReport bf_reports{};
1079 bf_reports.reports = reports;
1080 bf_reports.duration.whole = BLI_time_now_seconds();
1081 BlendFileData *bfd = BKE_blendfile_read(filepath, &params, &bf_reports);
1082 if (bfd != nullptr) {
1083 wm_file_read_pre(use_data, use_userdef);
1084
1085 /* Close any user-loaded fonts. */
1087
1088 /* Put WM into a stable state for post-readfile processes (kill jobs, removes event handlers,
1089 * message bus, and so on). */
1090 BlendFileReadWMSetupData *wm_setup_data = wm_file_read_setup_wm_init(C, bmain, false);
1091
1092 /* This flag is initialized by the operator but overwritten on read.
1093 * need to re-enable it here else drivers and registered scripts won't work. */
1094 const int G_f_orig = G.f;
1095
1096 /* Frees the current main and replaces it with the new one read from file. */
1098 C, bfd, &params, wm_setup_data, &bf_reports, false, nullptr);
1099 bmain = CTX_data_main(C);
1100
1101 /* Finalize handling of WM, using the read WM and/or the current WM depending on things like
1102 * whether the UI is loaded from the .blend file or not, etc. */
1103 wm_file_read_setup_wm_finalize(C, bmain, wm_setup_data);
1104
1105 if (G.f != G_f_orig) {
1106 const int flags_keep = G_FLAG_ALL_RUNTIME;
1108 G.f = (G.f & ~flags_keep) | (G_f_orig & flags_keep);
1109 }
1110
1111 WM_check(C); /* Opens window(s), checks keymaps. */
1112
1113 if (do_history_file_update) {
1115 }
1116
1117 wmFileReadPost_Params read_file_post_params{};
1118 read_file_post_params.use_data = use_data;
1119 read_file_post_params.use_userdef = use_userdef;
1120 read_file_post_params.is_startup_file = false;
1121 read_file_post_params.is_factory_startup = false;
1122 read_file_post_params.reset_app_template = false;
1123 read_file_post_params.success = true;
1124 read_file_post_params.is_alloc = false;
1125 wm_file_read_post(C, filepath, &read_file_post_params);
1126
1127 bf_reports.duration.whole = BLI_time_now_seconds() - bf_reports.duration.whole;
1128 file_read_reports_finalize(&bf_reports);
1129
1130 success = true;
1131 }
1132 }
1133#if 0
1134 else if (retval == BKE_READ_EXOTIC_OK_OTHER) {
1135 BKE_undo_write(C, "Import file");
1136 }
1137#endif
1138 else if (retval == BKE_READ_EXOTIC_FAIL_OPEN) {
1139 BKE_reportf(reports,
1140 RPT_ERROR,
1141 "Cannot read file \"%s\": %s",
1142 filepath,
1143 errno ? strerror(errno) : RPT_("unable to open the file"));
1144 }
1145 else if (retval == BKE_READ_EXOTIC_FAIL_FORMAT) {
1146 BKE_reportf(reports, RPT_ERROR, "File format is not supported in file \"%s\"", filepath);
1147 }
1148 else if (retval == BKE_READ_EXOTIC_FAIL_PATH) {
1149 BKE_reportf(reports, RPT_ERROR, "File path \"%s\" invalid", filepath);
1150 }
1151 else {
1152 BKE_reportf(reports, RPT_ERROR, "Unknown error loading \"%s\"", filepath);
1153 BLI_assert_msg(0, "invalid 'retval'");
1154 }
1155
1156 WM_cursor_wait(false);
1157
1158 wm_read_callback_post_wrapper(C, filepath, success);
1159
1161
1162 return success;
1163}
1164
1165static struct {
1167 bool override;
1169
1171{
1172 if (app_template) {
1174 wm_init_state_app_template.override = true;
1175 }
1176 else {
1177 wm_init_state_app_template.app_template[0] = '\0';
1178 wm_init_state_app_template.override = false;
1179 }
1180}
1181
1183{
1184 return wm_init_state_app_template.override ? wm_init_state_app_template.app_template : nullptr;
1185}
1186
1189/* -------------------------------------------------------------------- */
1194 const wmHomeFileRead_Params *params_homefile,
1195 ReportList *reports,
1196 wmFileReadPost_Params **r_params_file_read_post)
1197{
1198 /* NOTE: unlike #WM_file_read, don't set the wait cursor when reading the home-file.
1199 * While technically both are reading a file and could use the wait cursor,
1200 * avoid doing so for the following reasons.
1201 *
1202 * - When loading blend with a file (command line or external file browser)
1203 * the home-file is read before the file being loaded.
1204 * Toggling the wait cursor twice causes the cursor to flicker which looks like a glitch.
1205 * - In practice it's not that useful as users tend not to set scenes with slow loading times
1206 * as their startup.
1207 */
1208
1209/* UNUSED, keep as this may be needed later & the comment below isn't self evident. */
1210#if 0
1211 /* Context does not always have valid main pointer here. */
1212 Main *bmain = G_MAIN;
1213#endif
1214 bool success = false;
1215
1216 /* May be enabled, when the user configuration doesn't exist. */
1217 const bool use_data = params_homefile->use_data;
1218 const bool use_userdef = params_homefile->use_userdef;
1219 bool use_factory_settings = params_homefile->use_factory_settings;
1220 /* Currently this only impacts preferences as it doesn't make much sense to keep the default
1221 * startup open in the case the app-template doesn't happen to define its own startup.
1222 * Unlike preferences where we might want to only reset the app-template part of the preferences
1223 * so as not to reset the preferences for all other Blender instances, see: #96427. */
1224 const bool use_factory_settings_app_template_only =
1226 const bool use_empty_data = params_homefile->use_empty_data;
1227 const char *filepath_startup_override = params_homefile->filepath_startup_override;
1228 const char *app_template_override = params_homefile->app_template_override;
1229
1230 bool filepath_startup_is_factory = true;
1231 char filepath_startup[FILE_MAX];
1232 char filepath_userdef[FILE_MAX];
1233
1234 /* When 'app_template' is set:
1235 * `{BLENDER_USER_CONFIG}/{app_template}`. */
1236 char app_template_system[FILE_MAX];
1237 /* When 'app_template' is set:
1238 * `{BLENDER_SYSTEM_SCRIPTS}/startup/bl_app_templates_system/{app_template}`. */
1239 char app_template_config[FILE_MAX];
1240
1241 eBLOReadSkip skip_flags = eBLOReadSkip(0);
1242
1243 if (use_data == false) {
1244 skip_flags |= BLO_READ_SKIP_DATA;
1245 }
1246 if (use_userdef == false) {
1247 skip_flags |= BLO_READ_SKIP_USERDEF;
1248 }
1249
1250 /* True if we load startup.blend from memory
1251 * or use app-template startup.blend which the user hasn't saved. */
1252 bool is_factory_startup = true;
1253
1254 const char *app_template = nullptr;
1255 bool update_defaults = false;
1256
1257 /* Current Main is not always available in context here. */
1258 Main *bmain = G_MAIN;
1259
1260 if (filepath_startup_override != nullptr) {
1261 /* Pass. */
1262 }
1263 else if (app_template_override) {
1264 /* This may be clearing the current template by setting to an empty string. */
1265 app_template = app_template_override;
1266 }
1267 else if (!use_factory_settings && U.app_template[0]) {
1268 app_template = U.app_template;
1269 }
1270
1271 const bool reset_app_template = ((!app_template && U.app_template[0]) ||
1272 (app_template && !STREQ(app_template, U.app_template)));
1273
1274 /* Options exclude each other. */
1275 BLI_assert((use_factory_settings && filepath_startup_override) == 0);
1276
1277 if ((G.f & G_FLAG_SCRIPT_OVERRIDE_PREF) == 0) {
1279 }
1280
1281 if (use_data) {
1282 if (reset_app_template) {
1283 /* Always load UI when switching to another template. */
1284 G.fileflags &= ~G_FILE_NO_UI;
1285 }
1286 }
1287
1288 if (use_userdef || reset_app_template) {
1289#ifdef WITH_PYTHON
1290 /* This only runs once Blender has already started. */
1291 if (!params_homefile->is_first_time) {
1293 /* This is restored by 'wm_file_read_post', disable before loading any preferences
1294 * so an add-on can read their own preferences when un-registering,
1295 * and use new preferences if/when re-registering, see #67577.
1296 *
1297 * Note that this fits into 'wm_file_read_pre' function but gets messy
1298 * since we need to know if 'reset_app_template' is true. */
1299 const char *imports[] = {"addon_utils", nullptr};
1300 BPY_run_string_eval(C, imports, "addon_utils.disable_all()");
1301 }
1302#endif /* WITH_PYTHON */
1303 }
1304
1305 if (use_data) {
1306 /* NOTE: a matching #wm_read_callback_post_wrapper must be called.
1307 * This runs from #wm_homefile_read_post. */
1309 }
1310
1311 /* For regular file loading this only runs after the file is successfully read.
1312 * In the case of the startup file, the in-memory startup file is used as a fallback
1313 * so we know this will work if all else fails. */
1314 wm_file_read_pre(use_data, use_userdef);
1315
1316 BlendFileReadWMSetupData *wm_setup_data = nullptr;
1317 if (use_data) {
1318 /* Put WM into a stable state for post-readfile processes (kill jobs, removes event handlers,
1319 * message bus, and so on). */
1320 wm_setup_data = wm_file_read_setup_wm_init(C, bmain, true);
1321 }
1322
1323 filepath_startup[0] = '\0';
1324 filepath_userdef[0] = '\0';
1325 app_template_system[0] = '\0';
1326 app_template_config[0] = '\0';
1327
1328 const std::optional<std::string> cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, nullptr);
1329 if (!use_factory_settings) {
1330 if (cfgdir.has_value()) {
1332 filepath_startup, sizeof(filepath_startup), cfgdir->c_str(), BLENDER_STARTUP_FILE);
1333 filepath_startup_is_factory = false;
1334 if (use_userdef) {
1336 filepath_userdef, sizeof(filepath_startup), cfgdir->c_str(), BLENDER_USERPREF_FILE);
1337 }
1338 }
1339 else {
1340 use_factory_settings = true;
1341 }
1342
1343 if (filepath_startup_override) {
1344 STRNCPY(filepath_startup, filepath_startup_override);
1345 filepath_startup_is_factory = false;
1346 }
1347 }
1348
1349 /* Load preferences before `startup.blend`. */
1350 if (use_userdef) {
1351 if (use_factory_settings_app_template_only) {
1352 /* Use the current preferences as-is (only load in the app_template preferences). */
1353 skip_flags |= BLO_READ_SKIP_USERDEF;
1354 }
1355 else if (!use_factory_settings && BLI_exists(filepath_userdef)) {
1356 UserDef *userdef = BKE_blendfile_userdef_read(filepath_userdef, nullptr);
1357 if (userdef != nullptr) {
1358 CLOG_INFO(&LOG, 0, "read prefs: \"%s\"", filepath_userdef);
1359
1361 userdef = nullptr;
1362
1363 skip_flags |= BLO_READ_SKIP_USERDEF;
1364 }
1365 }
1366 }
1367
1368 if ((app_template != nullptr) && (app_template[0] != '\0')) {
1370 app_template, app_template_system, sizeof(app_template_system)))
1371 {
1372 /* Can safely continue with code below, just warn it's not found. */
1373 BKE_reportf(reports, RPT_WARNING, "Application Template \"%s\" not found", app_template);
1374 }
1375
1376 /* Insert template name into startup file. */
1377
1378 /* Note that the path is being set even when `use_factory_settings == true`
1379 * this is done so we can load a templates factory-settings. */
1380 if (!use_factory_settings && cfgdir.has_value()) {
1382 app_template_config, sizeof(app_template_config), cfgdir->c_str(), app_template);
1384 filepath_startup, sizeof(filepath_startup), app_template_config, BLENDER_STARTUP_FILE);
1385 filepath_startup_is_factory = false;
1386 if (BLI_access(filepath_startup, R_OK) != 0) {
1387 filepath_startup[0] = '\0';
1388 }
1389 }
1390 else {
1391 filepath_startup[0] = '\0';
1392 }
1393
1394 if (filepath_startup[0] == '\0') {
1396 filepath_startup, sizeof(filepath_startup), app_template_system, BLENDER_STARTUP_FILE);
1397 filepath_startup_is_factory = true;
1398
1399 /* Update defaults only for system templates. */
1400 update_defaults = true;
1401 }
1402 }
1403
1404 if (!use_factory_settings || (filepath_startup[0] != '\0')) {
1405 if (BLI_access(filepath_startup, R_OK) == 0) {
1407 params.is_startup = true;
1408 params.is_factory_settings = use_factory_settings;
1409 params.skip_flags = skip_flags | BLO_READ_SKIP_USERDEF;
1410 BlendFileReadReport bf_reports{};
1411 bf_reports.reports = reports;
1412 BlendFileData *bfd = BKE_blendfile_read(filepath_startup, &params, &bf_reports);
1413
1414 if (bfd != nullptr) {
1415 CLOG_INFO(&LOG, 0, "read startup: \"%s\"", filepath_startup);
1416
1417 /* Frees the current main and replaces it with the new one read from file. */
1419 bfd,
1420 &params,
1421 wm_setup_data,
1422 &bf_reports,
1423 update_defaults && use_data,
1424 app_template);
1425 success = true;
1426 bmain = CTX_data_main(C);
1427 }
1428 }
1429 if (success) {
1430 is_factory_startup = filepath_startup_is_factory;
1431 }
1432 }
1433
1434 if (use_userdef) {
1435 if ((skip_flags & BLO_READ_SKIP_USERDEF) == 0) {
1436 UserDef *userdef_default = BKE_blendfile_userdef_from_defaults();
1438 skip_flags |= BLO_READ_SKIP_USERDEF;
1439 }
1440 }
1441
1442 if (success == false && filepath_startup_override && reports) {
1443 /* We can not return from here because wm is already reset. */
1444 BKE_reportf(reports, RPT_ERROR, "Could not read \"%s\"", filepath_startup_override);
1445 }
1446
1447 bool loaded_factory_settings = false;
1448 if (success == false) {
1449 BlendFileReadParams read_file_params{};
1450 read_file_params.is_startup = true;
1451 read_file_params.is_factory_settings = use_factory_settings;
1452 read_file_params.skip_flags = skip_flags;
1454 datatoc_startup_blend, datatoc_startup_blend_size, &read_file_params, nullptr);
1455 if (bfd != nullptr) {
1456 BlendFileReadReport read_report{};
1457 /* Frees the current main and replaces it with the new one read from file. */
1459 C, bfd, &read_file_params, wm_setup_data, &read_report, true, nullptr);
1460 success = true;
1461 loaded_factory_settings = true;
1462 bmain = CTX_data_main(C);
1463 }
1464 }
1465
1466 if (use_empty_data) {
1468 }
1469
1470 /* Load template preferences,
1471 * unlike regular preferences we only use some of the settings,
1472 * see: #BKE_blender_userdef_set_app_template. */
1473 if (app_template_system[0] != '\0') {
1474 char temp_path[FILE_MAX];
1475 temp_path[0] = '\0';
1476 if (!use_factory_settings) {
1477 BLI_path_join(temp_path, sizeof(temp_path), app_template_config, BLENDER_USERPREF_FILE);
1478 if (BLI_access(temp_path, R_OK) != 0) {
1479 temp_path[0] = '\0';
1480 }
1481 }
1482
1483 if (temp_path[0] == '\0') {
1484 BLI_path_join(temp_path, sizeof(temp_path), app_template_system, BLENDER_USERPREF_FILE);
1485 }
1486
1487 if (use_userdef) {
1488 UserDef *userdef_template = nullptr;
1489 /* Just avoids missing file warning. */
1490 if (BLI_exists(temp_path)) {
1491 userdef_template = BKE_blendfile_userdef_read(temp_path, nullptr);
1492 if (userdef_template) {
1493 CLOG_INFO(&LOG, 0, "read prefs from app-template: \"%s\"", temp_path);
1494 }
1495 }
1496 if (userdef_template == nullptr) {
1497 /* We need to have preferences load to overwrite preferences from previous template. */
1498 userdef_template = BKE_blendfile_userdef_from_defaults();
1499 }
1500 if (userdef_template) {
1502 userdef_template = nullptr;
1503 }
1504 }
1505 }
1506
1507 if (app_template_override) {
1508 STRNCPY(U.app_template, app_template_override);
1509 }
1510
1511 if (use_userdef) {
1512 /* Check userdef before open window, keymaps etc. */
1513 wm_init_userdef(bmain);
1514 }
1515
1516 if (use_data) {
1517 /* Finalize handling of WM, using the read WM and/or the current WM depending on things like
1518 * whether the UI is loaded from the .blend file or not, etc. */
1519 wm_setup_data->is_factory_startup = loaded_factory_settings;
1520 wm_file_read_setup_wm_finalize(C, bmain, wm_setup_data);
1521 }
1522
1523 if (use_userdef) {
1524 /* Clear keymaps because the current default keymap may have been initialized
1525 * from user preferences, which have been reset. */
1526 LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
1527 if (wm->defaultconf) {
1528 wm->defaultconf->flag &= ~KEYCONF_INIT_DEFAULT;
1529 }
1530 }
1531 }
1532
1533 if (use_data) {
1534 WM_check(C); /* Opens window(s), checks keymaps. */
1535
1536 bmain->filepath[0] = '\0';
1537 }
1538
1539 {
1540 wmFileReadPost_Params params_file_read_post{};
1541 params_file_read_post.use_data = use_data;
1542 params_file_read_post.use_userdef = use_userdef;
1543 params_file_read_post.is_startup_file = true;
1544 params_file_read_post.is_factory_startup = is_factory_startup;
1545 params_file_read_post.is_first_time = params_homefile->is_first_time;
1546
1547 params_file_read_post.reset_app_template = reset_app_template;
1548
1549 params_file_read_post.success = success;
1550 params_file_read_post.is_alloc = false;
1551
1552 if (r_params_file_read_post == nullptr) {
1553 wm_homefile_read_post(C, &params_file_read_post);
1554 }
1555 else {
1556 params_file_read_post.is_alloc = true;
1557 *r_params_file_read_post = static_cast<wmFileReadPost_Params *>(
1558 MEM_mallocN(sizeof(wmFileReadPost_Params), __func__));
1559 **r_params_file_read_post = params_file_read_post;
1560
1561 /* Match #wm_file_read_post which leaves the window cleared too. */
1562 CTX_wm_window_set(C, nullptr);
1563 }
1564 }
1565}
1566
1568 const wmHomeFileRead_Params *params_homefile,
1569 ReportList *reports)
1570{
1571 wm_homefile_read_ex(C, params_homefile, reports, nullptr);
1572}
1573
1574void wm_homefile_read_post(bContext *C, const wmFileReadPost_Params *params_file_read_post)
1575{
1576 const char *filepath = "";
1577 wm_file_read_post(C, filepath, params_file_read_post);
1578
1579 if (params_file_read_post->use_data) {
1580 wm_read_callback_post_wrapper(C, filepath, params_file_read_post->success);
1581 }
1582
1583 if (params_file_read_post->is_alloc) {
1584 MEM_freeN((void *)params_file_read_post);
1585 }
1586}
1587
1590/* -------------------------------------------------------------------- */
1595{
1596 const std::optional<std::string> cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, nullptr);
1597 if (!cfgdir.has_value()) {
1598 return;
1599 }
1600
1601 char filepath[FILE_MAX];
1602 LinkNode *l;
1603 int num;
1604
1605 BLI_path_join(filepath, sizeof(filepath), cfgdir->c_str(), BLENDER_HISTORY_FILE);
1606
1607 LinkNode *lines = BLI_file_read_as_lines(filepath);
1608
1610
1611 /* Read list of recent opened files from #BLENDER_HISTORY_FILE to memory. */
1612 for (l = lines, num = 0; l && (num < U.recent_files); l = l->next) {
1613 const char *line = static_cast<const char *>(l->link);
1614 /* Don't check if files exist, causes slow startup for remote/external drives. */
1615 if (line[0]) {
1616 RecentFile *recent = (RecentFile *)MEM_mallocN(sizeof(RecentFile), "RecentFile");
1617 BLI_addtail(&(G.recent_files), recent);
1618 recent->filepath = BLI_strdup(line);
1619 num++;
1620 }
1621 }
1622
1623 BLI_file_free_lines(lines);
1624}
1625
1626static RecentFile *wm_history_file_new(const char *filepath)
1627{
1628 RecentFile *recent = static_cast<RecentFile *>(MEM_mallocN(sizeof(RecentFile), "RecentFile"));
1629 recent->filepath = BLI_strdup(filepath);
1630 return recent;
1631}
1632
1634{
1635 BLI_assert(BLI_findindex(&G.recent_files, recent) != -1);
1636 MEM_freeN(recent->filepath);
1637 BLI_freelinkN(&G.recent_files, recent);
1638}
1639
1641{
1642 LISTBASE_FOREACH_MUTABLE (RecentFile *, recent, &G.recent_files) {
1643 wm_history_file_free(recent);
1644 }
1645}
1646
1647static RecentFile *wm_file_history_find(const char *filepath)
1648{
1649 return static_cast<RecentFile *>(
1650 BLI_findstring_ptr(&G.recent_files, filepath, offsetof(RecentFile, filepath)));
1651}
1652
1658{
1659 char filepath[FILE_MAX];
1660 FILE *fp;
1661
1662 /* Will be nullptr in background mode. */
1663 const std::optional<std::string> user_config_dir = BKE_appdir_folder_id_create(
1664 BLENDER_USER_CONFIG, nullptr);
1665 if (!user_config_dir.has_value()) {
1666 return;
1667 }
1668
1669 BLI_path_join(filepath, sizeof(filepath), user_config_dir->c_str(), BLENDER_HISTORY_FILE);
1670
1671 fp = BLI_fopen(filepath, "w");
1672 if (fp) {
1673 LISTBASE_FOREACH (RecentFile *, recent, &G.recent_files) {
1674 fprintf(fp, "%s\n", recent->filepath);
1675 }
1676 fclose(fp);
1677 }
1678}
1679
1684{
1685 RecentFile *recent;
1686 const char *blendfile_path = BKE_main_blendfile_path_from_global();
1687
1688 /* No write history for recovered startup files. */
1689 if (blendfile_path[0] == '\0') {
1690 return;
1691 }
1692
1693 recent = static_cast<RecentFile *>(G.recent_files.first);
1694 /* Refresh #BLENDER_HISTORY_FILE of recent opened files, when current file was changed. */
1695 if (!(recent) || (BLI_path_cmp(recent->filepath, blendfile_path) != 0)) {
1696
1697 recent = wm_file_history_find(blendfile_path);
1698 if (recent) {
1699 BLI_remlink(&G.recent_files, recent);
1700 }
1701 else {
1702 RecentFile *recent_next;
1703 for (recent = static_cast<RecentFile *>(BLI_findlink(&G.recent_files, U.recent_files - 1));
1704 recent;
1705 recent = recent_next)
1706 {
1707 recent_next = recent->next;
1708 wm_history_file_free(recent);
1709 }
1710 recent = wm_history_file_new(blendfile_path);
1711 }
1712
1713 /* Add current file to the beginning of list. */
1714 BLI_addhead(&(G.recent_files), recent);
1715
1716 /* Write current file to #BLENDER_HISTORY_FILE. */
1718
1719 /* Also update most recent files on system. */
1720 GHOST_addToSystemRecentFiles(blendfile_path);
1721 }
1722}
1723
1726/* -------------------------------------------------------------------- */
1747#ifdef USE_THUMBNAIL_FAST_DOWNSCALE
1749 const int src_size[2],
1750 const int dst_size[2])
1751{
1752 /* NOTE: this is a faster alternative to #IMBScaleFilter::Box which is
1753 * especially slow in debug builds, normally debug performance isn't a
1754 * consideration however it's slow enough to get in the way of development.
1755 * In release builds this gives ~1.4x speedup. */
1756
1757 /* Scaling using a box-filter where each box uses an integer-rounded region.
1758 * Accept a slightly lower quality scale as this is only for thumbnails.
1759 * In practice the result is visually indistinguishable.
1760 *
1761 * Technically the color accumulation *could* overflow (creating some invalid pixels),
1762 * however this would require the source image to be larger than
1763 * 65,535 pixels squared (when scaling down to 256x256).
1764 * As the source input is a screenshot or a small camera render created for the thumbnail,
1765 * this isn't a concern. */
1766
1767 BLI_assert(dst_size[0] <= src_size[0] && dst_size[1] <= src_size[1]);
1768 uint8_t *dst_rect = static_cast<uint8_t *>(
1769 MEM_mallocN(sizeof(uint8_t[4]) * dst_size[0] * dst_size[1], __func__));
1770
1771 /* A row, the width of the destination to accumulate pixel values into
1772 * before writing into the image. */
1773 uint32_t *accum_row = static_cast<uint32_t *>(
1774 MEM_callocN(sizeof(uint32_t) * dst_size[0] * 4, __func__));
1775
1776# ifndef NDEBUG
1777 /* Assert that samples are calculated correctly. */
1778 uint64_t sample_count_all = 0;
1779# endif
1780
1781 const uint32_t src_size_x = src_size[0];
1782 const uint32_t src_size_y = src_size[1];
1783
1784 const uint32_t dst_size_x = dst_size[0];
1785 const uint32_t dst_size_y = dst_size[1];
1786 const uint8_t *src_px = src_rect;
1787
1788 uint32_t src_y = 0;
1789 for (uint32_t dst_y = 0; dst_y < dst_size_y; dst_y++) {
1790 const uint32_t src_y_beg = src_y;
1791 const uint32_t src_y_end = ((dst_y + 1) * src_size_y) / dst_size_y;
1792 for (; src_y < src_y_end; src_y++) {
1793 uint32_t *accum = accum_row;
1794 uint32_t src_x = 0;
1795 for (uint32_t dst_x = 0; dst_x < dst_size_x; dst_x++, accum += 4) {
1796 const uint32_t src_x_end = ((dst_x + 1) * src_size_x) / dst_size_x;
1797 for (; src_x < src_x_end; src_x++) {
1798 accum[0] += uint32_t(src_px[0]);
1799 accum[1] += uint32_t(src_px[1]);
1800 accum[2] += uint32_t(src_px[2]);
1801 accum[3] += uint32_t(src_px[3]);
1802 src_px += 4;
1803 }
1804 BLI_assert(src_x == src_x_end);
1805 }
1806 BLI_assert(accum == accum_row + (4 * dst_size[0]));
1807 }
1808
1809 uint32_t *accum = accum_row;
1810 uint8_t *dst_px = dst_rect + ((dst_y * dst_size_x) * 4);
1811 uint32_t src_x_beg = 0;
1812 const uint32_t span_y = src_y_end - src_y_beg;
1813 for (uint32_t dst_x = 0; dst_x < dst_size_x; dst_x++) {
1814 const uint32_t src_x_end = ((dst_x + 1) * src_size_x) / dst_size_x;
1815 const uint32_t span_x = src_x_end - src_x_beg;
1816
1817 const uint32_t sample_count = span_x * span_y;
1818 dst_px[0] = uint8_t(accum[0] / sample_count);
1819 dst_px[1] = uint8_t(accum[1] / sample_count);
1820 dst_px[2] = uint8_t(accum[2] / sample_count);
1821 dst_px[3] = uint8_t(accum[3] / sample_count);
1822 accum[0] = accum[1] = accum[2] = accum[3] = 0;
1823 accum += 4;
1824 dst_px += 4;
1825
1826 src_x_beg = src_x_end;
1827# ifndef NDEBUG
1828 sample_count_all += sample_count;
1829# endif
1830 }
1831 }
1832 BLI_assert(src_px == src_rect + (sizeof(uint8_t[4]) * src_size[0] * src_size[1]));
1833 BLI_assert(sample_count_all == size_t(src_size[0]) * size_t(src_size[1]));
1834
1835 MEM_freeN(accum_row);
1836 return dst_rect;
1837}
1838#endif /* USE_THUMBNAIL_FAST_DOWNSCALE */
1839
1840static blender::int2 blend_file_thumb_clamp_size(const int size[2], const int limit)
1841{
1843 if (size[0] > size[1]) {
1844 result.x = limit;
1845 result.y = max_ii(1, int((float(size[1]) / float(size[0])) * limit));
1846 }
1847 else {
1848 result.x = max_ii(1, int((float(size[0]) / float(size[1])) * limit));
1849 result.y = limit;
1850 }
1851 return result;
1852}
1853
1858{
1859 *r_thumb = nullptr;
1860
1861 wmWindow *win = CTX_wm_window(C);
1862 if (G.background || (win == nullptr)) {
1863 return nullptr;
1864 }
1865
1866 /* The window to capture should be a main window (without parent). */
1867 while (win->parent) {
1868 win = win->parent;
1869 }
1870
1872 int win_size[2];
1873 /* NOTE: always read from front-buffer as drawing a window can cause problems while saving,
1874 * even if this means the thumbnail from the screen-shot fails to be created, see: #98462. */
1875 ImBuf *ibuf = nullptr;
1876
1877 if (uint8_t *buffer = WM_window_pixels_read_from_frontbuffer(wm, win, win_size)) {
1878 const blender::int2 thumb_size_2x = blend_file_thumb_clamp_size(win_size, BLEN_THUMB_SIZE * 2);
1879 const blender::int2 thumb_size = blend_file_thumb_clamp_size(win_size, BLEN_THUMB_SIZE);
1880
1881#ifdef USE_THUMBNAIL_FAST_DOWNSCALE
1882 if ((thumb_size_2x[0] <= win_size[0]) && (thumb_size_2x[1] <= win_size[1])) {
1883 uint8_t *rect_2x = blend_file_thumb_fast_downscale(buffer, win_size, thumb_size_2x);
1884 uint8_t *rect = blend_file_thumb_fast_downscale(rect_2x, thumb_size_2x, thumb_size);
1885
1886 MEM_freeN(buffer);
1887 ibuf = IMB_allocFromBufferOwn(rect_2x, nullptr, thumb_size_2x.x, thumb_size_2x.y, 24);
1888
1889 BlendThumbnail *thumb = BKE_main_thumbnail_from_buffer(nullptr, rect, thumb_size);
1890 MEM_freeN(rect);
1891 *r_thumb = thumb;
1892 }
1893 else
1894#endif /* USE_THUMBNAIL_FAST_DOWNSCALE */
1895 {
1896 ibuf = IMB_allocFromBufferOwn(buffer, nullptr, win_size[0], win_size[1], 24);
1897 BLI_assert(ibuf != nullptr); /* Never expected to fail. */
1898
1899 /* File-system thumbnail image can be 256x256. */
1900 IMB_scale(ibuf, thumb_size_2x.x, thumb_size_2x.y, IMBScaleFilter::Box, false);
1901
1902 /* Thumbnail inside blend should be 128x128. */
1903 ImBuf *thumb_ibuf = IMB_dupImBuf(ibuf);
1904 IMB_scale(thumb_ibuf, thumb_size.x, thumb_size.y, IMBScaleFilter::Box, false);
1905
1906 BlendThumbnail *thumb = BKE_main_thumbnail_from_imbuf(nullptr, thumb_ibuf);
1907 IMB_freeImBuf(thumb_ibuf);
1908 *r_thumb = thumb;
1909 }
1910 }
1911
1912 if (ibuf) {
1913 /* Save metadata for quick access. */
1914 char version_str[10];
1915 SNPRINTF(version_str, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100);
1917 IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_str);
1918 }
1919
1920 /* Must be freed by caller. */
1921 return ibuf;
1922}
1923
1930 Scene *scene,
1931 bScreen *screen,
1932 BlendThumbnail **r_thumb)
1933{
1934 *r_thumb = nullptr;
1935
1936 /* Scene can be nullptr if running a script at startup and calling the save operator. */
1937 if (G.background || scene == nullptr) {
1938 return nullptr;
1939 }
1940
1941 /* Will be scaled down, but gives some nice oversampling. */
1942 ImBuf *ibuf;
1943 BlendThumbnail *thumb;
1945 const float pixelsize_old = U.pixelsize;
1946 wmWindow *windrawable_old = wm->windrawable;
1947 char err_out[256] = "unknown";
1948
1949 /* Screen if no camera found. */
1950 ScrArea *area = nullptr;
1951 ARegion *region = nullptr;
1952 View3D *v3d = nullptr;
1953
1954 if (screen != nullptr) {
1955 area = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0);
1956 if (area) {
1957 v3d = static_cast<View3D *>(area->spacedata.first);
1959 }
1960 }
1961
1962 if (scene->camera == nullptr && v3d == nullptr) {
1963 return nullptr;
1964 }
1965
1967
1968 /* Note that with scaling, this ends up being 0.5,
1969 * as it's a thumbnail, we don't need object centers and friends to be 1:1 size. */
1970 U.pixelsize = 1.0f;
1971
1972 if (scene->camera) {
1974 scene,
1975 (v3d) ? &v3d->shading : nullptr,
1976 (v3d) ? eDrawType(v3d->shading.type) : OB_SOLID,
1977 scene->camera,
1980 IB_rect,
1984 nullptr,
1985 nullptr,
1986 nullptr,
1987 err_out);
1988 }
1989 else {
1991 scene,
1992 OB_SOLID,
1993 v3d,
1994 region,
1997 IB_rect,
1999 nullptr,
2000 true,
2001 nullptr,
2002 nullptr,
2003 err_out);
2004 }
2005
2006 U.pixelsize = pixelsize_old;
2007
2008 /* Reset to old drawable. */
2009 if (windrawable_old) {
2010 wm_window_make_drawable(wm, windrawable_old);
2011 }
2012 else {
2014 }
2015
2016 if (ibuf) {
2017 /* Dirty oversampling. */
2018 ImBuf *thumb_ibuf;
2019 thumb_ibuf = IMB_dupImBuf(ibuf);
2020
2021 /* Save metadata for quick access. */
2022 char version_str[10];
2023 SNPRINTF(version_str, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100);
2025 IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_str);
2026
2027 /* BLEN_THUMB_SIZE is size of thumbnail inside blend file: 128x128. */
2029 thumb = BKE_main_thumbnail_from_imbuf(nullptr, thumb_ibuf);
2030 IMB_freeImBuf(thumb_ibuf);
2031 /* Thumbnail saved to file-system should be 256x256. */
2032 IMB_scale(ibuf,
2036 false);
2037 }
2038 else {
2039 /* '*r_thumb' needs to stay nullptr to prevent a bad thumbnail from being handled. */
2040 CLOG_WARN(&LOG, "failed to create thumbnail: %s", err_out);
2041 thumb = nullptr;
2042 }
2043
2044 /* Must be freed by caller. */
2045 *r_thumb = thumb;
2046
2047 return ibuf;
2048}
2049
2052/* -------------------------------------------------------------------- */
2057{
2058 char filepath[FILE_MAX];
2059
2061 BLI_path_extension_replace(filepath, sizeof(filepath), "_crash.blend");
2063 const bool success = BLO_write_file(G_MAIN, filepath, G.fileflags, &params, nullptr);
2064 printf("%s: \"%s\"\n", success ? "written" : "failed", filepath);
2065 return success;
2066}
2067
2073 const char *filepath,
2074 ReportList *reports)
2075{
2076 const int filepath_len = strlen(filepath);
2077 if (filepath_len == 0) {
2078 BKE_report(reports, RPT_ERROR, "Path is empty, cannot save");
2079 return false;
2080 }
2081
2082 if (filepath_len >= FILE_MAX) {
2083 BKE_report(reports, RPT_ERROR, "Path too long, cannot save");
2084 return false;
2085 }
2086
2087 if (bmain->is_asset_edit_file &&
2089 {
2090 BKE_report(reports, RPT_ERROR, "Cannot overwrite files that are managed by the asset system");
2091 return false;
2092 }
2093
2094 LISTBASE_FOREACH (Library *, li, &bmain->libraries) {
2095 if (BLI_path_cmp(li->runtime.filepath_abs, filepath) == 0) {
2096 BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath);
2097 return false;
2098 }
2099 }
2100
2101 return true;
2102}
2103
2108 const char *filepath,
2109 int fileflags,
2110 eBLO_WritePathRemap remap_mode,
2111 bool use_save_as_copy,
2112 ReportList *reports)
2113{
2114 Main *bmain = CTX_data_main(C);
2115 BlendThumbnail *thumb = nullptr, *main_thumb = nullptr;
2116 ImBuf *ibuf_thumb = nullptr;
2117
2118 /* NOTE: used to replace the file extension (to ensure `.blend`),
2119 * no need to now because the operator ensures,
2120 * its handy for scripts to save to a predefined name without blender editing it. */
2121
2122 if (!wm_file_write_check_with_report_on_failure(bmain, filepath, reports)) {
2123 return false;
2124 }
2125
2126 /* Call pre-save callbacks before writing preview,
2127 * that way you can generate custom file thumbnail. */
2128
2129 /* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
2130 * Runs at the end of this function, don't return beforehand. */
2132
2133 /* Check if file write permission is OK. */
2134 if (BLI_exists(filepath) && !BLI_file_is_writable(filepath)) {
2136 reports, RPT_ERROR, "Cannot save blend file, path \"%s\" is not writable", filepath);
2137
2139 return false;
2140 }
2141
2143
2144 /* Enforce full override check/generation on file save. */
2146
2147 /* NOTE: Ideally we would call `WM_redraw_windows` here to remove any open menus.
2148 * But we can crash if saving from a script, see #92704 & #97627.
2149 * Just checking `!G.background && BLI_thread_is_main()` is not sufficient to fix this.
2150 * Additionally some EGL configurations don't support reading the front-buffer
2151 * immediately after drawing, see: #98462. In that case off-screen drawing is necessary. */
2152
2153 /* Don't forget not to return without! */
2154 WM_cursor_wait(true);
2155
2156 if (U.file_preview_type != USER_FILE_PREVIEW_NONE) {
2157 /* Blend file thumbnail.
2158 *
2159 * - Save before exiting edit-mode, otherwise evaluated-mesh for shared data gets corrupted.
2160 * See #27765.
2161 * - Main can store a `.blend` thumbnail,
2162 * useful for background-mode or thumbnail customization.
2163 */
2164 main_thumb = thumb = bmain->blen_thumb;
2165 if (thumb != nullptr) {
2166 /* In case we are given a valid thumbnail data, just generate image from it. */
2167 ibuf_thumb = BKE_main_thumbnail_to_imbuf(nullptr, thumb);
2168 }
2169 else if (BLI_thread_is_main()) {
2170 int file_preview_type = U.file_preview_type;
2171
2172 if (file_preview_type == USER_FILE_PREVIEW_AUTO) {
2173 Scene *scene = CTX_data_scene(C);
2174 bScreen *screen = CTX_wm_screen(C);
2175 bool do_render = (scene != nullptr && scene->camera != nullptr && screen != nullptr &&
2176 (BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0) != nullptr));
2177 file_preview_type = do_render ? USER_FILE_PREVIEW_CAMERA : USER_FILE_PREVIEW_SCREENSHOT;
2178 }
2179
2180 switch (file_preview_type) {
2182 ibuf_thumb = blend_file_thumb_from_screenshot(C, &thumb);
2183 break;
2184 }
2186 ibuf_thumb = blend_file_thumb_from_camera(
2187 C, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
2188 break;
2189 }
2190 default:
2192 }
2193 }
2194 }
2195
2196 /* Operator now handles overwrite checks. */
2197
2198 if (G.fileflags & G_FILE_AUTOPACK) {
2199 BKE_packedfile_pack_all(bmain, reports, false);
2200 }
2201
2203
2204 /* XXX(ton): temp solution to solve bug, real fix coming. */
2205 bmain->recovered = false;
2206
2207 BlendFileWriteParams blend_write_params{};
2208 blend_write_params.remap_mode = remap_mode;
2209 blend_write_params.use_save_versions = true;
2210 blend_write_params.use_save_as_copy = use_save_as_copy;
2211 blend_write_params.thumb = thumb;
2212
2213 const bool success = BLO_write_file(bmain, filepath, fileflags, &blend_write_params, reports);
2214
2215 if (success) {
2216 const bool do_history_file_update = (G.background == false) &&
2217 (CTX_wm_manager(C)->op_undo_depth == 0);
2218
2219 if (use_save_as_copy == false) {
2220 STRNCPY(bmain->filepath, filepath); /* Is guaranteed current file. */
2221 }
2222
2223 SET_FLAG_FROM_TEST(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
2224
2225 /* Prevent background mode scripts from clobbering history. */
2226 if (do_history_file_update) {
2228 }
2229
2230 /* Run this function after because the file can't be written before the blend is. */
2231 if (ibuf_thumb) {
2232 IMB_thumb_delete(filepath, THB_FAIL); /* Without this a failed thumb overrides. */
2233 ibuf_thumb = IMB_thumb_create(filepath, THB_LARGE, THB_SOURCE_BLEND, ibuf_thumb);
2234 }
2235
2236 /* Without this there is no feedback the file was saved. */
2237 BKE_reportf(reports, RPT_INFO, "Saved \"%s\"", BLI_path_basename(filepath));
2238 }
2239
2241 bmain, success ? BKE_CB_EVT_SAVE_POST : BKE_CB_EVT_SAVE_POST_FAIL, filepath);
2242
2243 if (ibuf_thumb) {
2244 IMB_freeImBuf(ibuf_thumb);
2245 }
2246 if (thumb && thumb != main_thumb) {
2247 MEM_freeN(thumb);
2248 }
2249
2250 WM_cursor_wait(false);
2251
2252 return success;
2253}
2254
2257/* -------------------------------------------------------------------- */
2261static void wm_autosave_location(char filepath[FILE_MAX])
2262{
2263 const int pid = abs(getpid());
2264 char filename[1024];
2265
2266 /* Normally there is no need to check for this to be nullptr,
2267 * however this runs on exit when it may be cleared. */
2268 Main *bmain = G_MAIN;
2269 const char *blendfile_path = bmain ? BKE_main_blendfile_path(bmain) : nullptr;
2270
2271 if (blendfile_path && (blendfile_path[0] != '\0')) {
2272 const char *basename = BLI_path_basename(blendfile_path);
2273 int len = strlen(basename) - 6;
2274 SNPRINTF(filename, "%.*s_%d_autosave.blend", len, basename, pid);
2275 }
2276 else {
2277 SNPRINTF(filename, "%d_autosave.blend", pid);
2278 }
2279
2280 const char *tempdir_base = BKE_tempdir_base();
2281 BLI_path_join(filepath, FILE_MAX, tempdir_base, filename);
2282}
2283
2285{
2286 char filepath[FILE_MAX];
2287
2288 wm_autosave_location(filepath);
2289
2290 /* Technically, we could always just save here, but that would cause performance regressions
2291 * compared to when the #MemFile undo step was used for saving undo-steps. So for now just skip
2292 * auto-save when we are in a mode where auto-save wouldn't have worked previously anyway. This
2293 * check can be removed once the performance regressions have been solved. */
2295 WM_autosave_write(wm, bmain);
2296 return true;
2297 }
2298 if ((U.uiflag & USER_GLOBALUNDO) == 0) {
2299 WM_autosave_write(wm, bmain);
2300 return true;
2301 }
2302 /* Can't auto-save with MemFile right now, try again later. */
2303 return false;
2304}
2305
2307{
2308 return wm->autosave_scheduled;
2309}
2310
2312{
2314
2315 char filepath[FILE_MAX];
2316 wm_autosave_location(filepath);
2317 /* Save as regular blend file with recovery information. */
2318 const int fileflags = (G.fileflags & ~G_FILE_COMPRESS) | G_FILE_RECOVER_WRITE;
2319
2320 /* Error reporting into console. */
2322 BLO_write_file(bmain, filepath, fileflags, &params, nullptr);
2323
2324 /* Restart auto-save timer. */
2327
2328 wm->autosave_scheduled = false;
2329}
2330
2331static void wm_autosave_timer_begin_ex(wmWindowManager *wm, double timestep)
2332{
2334
2335 if (U.flag & USER_AUTOSAVE) {
2336 wm->autosavetimer = WM_event_timer_add(wm, nullptr, TIMERAUTOSAVE, timestep);
2337 }
2338}
2339
2341{
2342 wm_autosave_timer_begin_ex(wm, U.savetime * 60.0);
2343}
2344
2346{
2347 if (wm->autosavetimer) {
2348 WM_event_timer_remove(wm, nullptr, wm->autosavetimer);
2349 wm->autosavetimer = nullptr;
2350 }
2351}
2352
2357
2359{
2361
2362 /* If a modal operator is running, don't autosave because we might not be in
2363 * a valid state to save. But try again in 10ms. */
2364 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
2365 LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
2366 if (handler_base->type == WM_HANDLER_TYPE_OP) {
2367 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
2368 if (handler->op) {
2370 return;
2371 }
2372 }
2373 }
2374 }
2375
2376 wm->autosave_scheduled = false;
2377 if (!wm_autosave_write_try(bmain, wm)) {
2378 wm->autosave_scheduled = true;
2379 }
2380 /* Restart the timer after file write, just in case file write takes a long time. */
2382}
2383
2385{
2386 char filepath[FILE_MAX];
2387
2388 wm_autosave_location(filepath);
2389
2390 if (BLI_exists(filepath)) {
2391 char filepath_quit[FILE_MAX];
2392 BLI_path_join(filepath_quit, sizeof(filepath_quit), BKE_tempdir_base(), BLENDER_QUIT_FILE);
2393
2394 /* For global undo; remove temporarily saved file, otherwise rename. */
2395 if (U.uiflag & USER_GLOBALUNDO) {
2396 BLI_delete(filepath, false, false);
2397 }
2398 else {
2399 BLI_rename_overwrite(filepath, filepath_quit);
2400 }
2401 }
2402}
2403
2406/* -------------------------------------------------------------------- */
2412{
2413 PropertyRNA *prop;
2414
2415 /* So it's possible to reset app-template settings without resetting other defaults. */
2416 prop = RNA_def_boolean(ot->srna,
2417 "use_factory_startup_app_template_only",
2418 false,
2419 "Factory Startup App-Template Only",
2420 "");
2422}
2423
2426/* -------------------------------------------------------------------- */
2434void wm_open_init_load_ui(wmOperator *op, bool use_prefs)
2435{
2436 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "load_ui");
2437 if (!RNA_property_is_set(op->ptr, prop)) {
2438 bool value = use_prefs ? ((U.flag & USER_FILENOUI) == 0) : ((G.fileflags & G_FILE_NO_UI) == 0);
2439
2440 RNA_property_boolean_set(op->ptr, prop, value);
2441 }
2442}
2443
2444void wm_open_init_use_scripts(wmOperator *op, bool use_prefs)
2445{
2446 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
2447 if (!RNA_property_is_set(op->ptr, prop)) {
2448 /* Use #G_FLAG_SCRIPT_AUTOEXEC rather than the userpref because this means if
2449 * the flag has been disabled from the command line, then opening
2450 * from the menu won't enable this setting. */
2451 bool value = use_prefs ? ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) :
2452 ((G.f & G_FLAG_SCRIPT_AUTOEXEC) != 0);
2453
2454 RNA_property_boolean_set(op->ptr, prop, value);
2455 }
2456}
2457
2460/* -------------------------------------------------------------------- */
2469{
2470 Main *bmain = CTX_data_main(C);
2472 wmWindow *win = CTX_wm_window(C);
2473 char filepath[FILE_MAX];
2474 int fileflags;
2475
2476 const char *app_template = U.app_template[0] ? U.app_template : nullptr;
2477 const std::optional<std::string> cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG,
2478 app_template);
2479 if (!cfgdir.has_value()) {
2480 BKE_report(op->reports, RPT_ERROR, "Unable to create user config path");
2481 return OPERATOR_CANCELLED;
2482 }
2483
2484 /* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
2485 * Runs at the end of this function, don't return beforehand. */
2488
2489 /* Check current window and close it if temp. */
2490 if (win && WM_window_is_temp_screen(win)) {
2491 wm_window_close(C, wm, win);
2492 }
2493
2494 /* Update keymaps in user preferences. */
2496
2497 BLI_path_join(filepath, sizeof(filepath), cfgdir->c_str(), BLENDER_STARTUP_FILE);
2498
2499 if (!G.quiet) {
2500 printf("Writing homefile: \"%s\" ", filepath);
2501 }
2502
2504
2505 /* Force save as regular blend file. */
2506 fileflags = G.fileflags & ~G_FILE_COMPRESS;
2507
2508 BlendFileWriteParams blend_write_params{};
2509 /* Make all paths absolute when saving the startup file.
2510 * On load the `G.main->filepath` will be empty so the paths
2511 * won't have a base for resolving the relative paths. */
2512 blend_write_params.remap_mode = BLO_WRITE_PATH_REMAP_ABSOLUTE;
2513 /* Don't apply any path changes to the current blend file. */
2514 blend_write_params.use_save_as_copy = true;
2515
2516 const bool success = BLO_write_file(
2517 bmain, filepath, fileflags, &blend_write_params, op->reports);
2518
2520
2521 if (success) {
2522 if (!G.quiet) {
2523 printf("ok\n");
2524 }
2525 BKE_report(op->reports, RPT_INFO, "Startup file saved");
2526 return OPERATOR_FINISHED;
2527 }
2528 if (!G.quiet) {
2529 printf("fail\n");
2530 }
2531 return OPERATOR_CANCELLED;
2532}
2533
2534static int wm_homefile_write_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
2535{
2536 if (!U.app_template[0]) {
2537 return WM_operator_confirm_ex(C,
2538 op,
2539 IFACE_("Overwrite Main Startup File"),
2540 IFACE_("Make the current file the default startup blend file."),
2541 IFACE_("Overwrite"),
2543 false);
2544 }
2545
2546 /* A different message if this is overriding a specific template startup file. */
2547 char display_name[FILE_MAX];
2548 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
2549 std::string message = fmt::format(
2550 IFACE_("Make the current file the default \"{}\" startup file."), IFACE_(display_name));
2551 return WM_operator_confirm_ex(C,
2552 op,
2553 IFACE_("Overwrite Template Startup File"),
2554 message.c_str(),
2555 IFACE_("Overwrite"),
2557 false);
2558}
2559
2561{
2562 ot->name = "Save Startup File";
2563 ot->idname = "WM_OT_save_homefile";
2564 ot->description = "Make the current file the default startup file";
2565
2568}
2569
2572/* -------------------------------------------------------------------- */
2576/* Only save the prefs block. operator entry. */
2578{
2580
2581 /* Update keymaps in user preferences. */
2583
2584 const bool success = BKE_blendfile_userdef_write_all(op->reports);
2585
2586 return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2587}
2588
2590{
2591 ot->name = "Save Preferences";
2592 ot->idname = "WM_OT_save_userpref";
2593 ot->description = "Make the current preferences default";
2594
2597}
2598
2601/* -------------------------------------------------------------------- */
2608static void wm_userpref_read_exceptions(UserDef *userdef_curr, const UserDef *userdef_prev)
2609{
2610#define USERDEF_RESTORE(member) \
2611 { \
2612 userdef_curr->member = userdef_prev->member; \
2613 } \
2614 ((void)0)
2615
2616 /* Current visible preferences category. */
2617 USERDEF_RESTORE(space_data.section_active);
2618
2619#undef USERDEF_RESTORE
2620}
2621
2623 Main *bmain,
2624 PointerRNA *ptr_a,
2625 PointerRNA *ptr_b)
2626{
2628 PropertyRNA *iterprop = RNA_struct_iterator_property(ptr_a->type);
2629 BLI_assert(ptr_a->type == ptr_b->type);
2630 RNA_property_collection_begin(ptr_a, iterprop, &iter);
2631 for (; iter.valid; RNA_property_collection_next(&iter)) {
2632 PropertyRNA *prop = static_cast<PropertyRNA *>(iter.ptr.data);
2633 if (STREQ(RNA_property_identifier(prop), "rna_type")) {
2634 continue;
2635 }
2636 switch (RNA_property_type(prop)) {
2637 case PROP_POINTER: {
2638 PointerRNA ptr_sub_a = RNA_property_pointer_get(ptr_a, prop);
2639 PointerRNA ptr_sub_b = RNA_property_pointer_get(ptr_b, prop);
2640 rna_struct_update_when_changed(C, bmain, &ptr_sub_a, &ptr_sub_b);
2641 break;
2642 }
2643 case PROP_COLLECTION:
2644 /* Don't handle collections. */
2645 break;
2646 default: {
2647 if (!RNA_property_equals(bmain, ptr_a, ptr_b, prop, RNA_EQ_STRICT)) {
2648 RNA_property_update(C, ptr_b, prop);
2649 }
2650 }
2651 }
2652 }
2654}
2655
2657 Main *bmain,
2658 UserDef *userdef_prev,
2659 UserDef *userdef_curr)
2660{
2661 PointerRNA ptr_a = RNA_pointer_create(nullptr, &RNA_Preferences, userdef_prev);
2662 PointerRNA ptr_b = RNA_pointer_create(nullptr, &RNA_Preferences, userdef_curr);
2663 const bool is_dirty = userdef_curr->runtime.is_dirty;
2664
2665 rna_struct_update_when_changed(C, bmain, &ptr_a, &ptr_b);
2666
2669
2670 userdef_curr->runtime.is_dirty = is_dirty;
2671}
2672
2674{
2675 Main *bmain = CTX_data_main(C);
2676 const bool use_data = false;
2677 const bool use_userdef = true;
2678 const bool use_factory_settings = STREQ(op->type->idname, "WM_OT_read_factory_userpref");
2679 const bool use_factory_settings_app_template_only =
2680 (use_factory_settings && RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only"));
2681
2683
2684 UserDef U_backup = blender::dna::shallow_copy(U);
2685
2686 wmHomeFileRead_Params read_homefile_params{};
2687 read_homefile_params.use_data = use_data;
2688 read_homefile_params.use_userdef = use_userdef;
2689 read_homefile_params.use_factory_settings = use_factory_settings;
2690 read_homefile_params.use_factory_settings_app_template_only =
2691 use_factory_settings_app_template_only;
2692 read_homefile_params.use_empty_data = false;
2693 read_homefile_params.filepath_startup_override = nullptr;
2694 read_homefile_params.app_template_override = WM_init_state_app_template_get();
2695 wm_homefile_read(C, &read_homefile_params, op->reports);
2696
2697 wm_userpref_read_exceptions(&U, &U_backup);
2698 SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT);
2699
2700 wm_userpref_update_when_changed(C, bmain, &U_backup, &U);
2701
2702 if (use_factory_settings) {
2703 U.runtime.is_dirty = true;
2704 }
2705
2707
2708 /* Needed to recalculate UI scaling values (eg, #UserDef.inv_dpi_fac). */
2709 wm_window_clear_drawable(static_cast<wmWindowManager *>(bmain->wm.first));
2710
2711 WM_event_add_notifier(C, NC_WINDOW, nullptr);
2712
2713 return OPERATOR_FINISHED;
2714}
2715
2717{
2718 ot->name = "Load Preferences";
2719 ot->idname = "WM_OT_read_userpref";
2720 ot->description = "Load last saved preferences";
2721
2724}
2725
2726static int wm_userpref_read_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
2727{
2728 std::string title;
2729
2730 const bool template_only = U.app_template[0] &&
2731 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only");
2732
2733 if (template_only) {
2734 char display_name[FILE_MAX];
2735 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
2736 title = fmt::format(IFACE_("Load Factory \"{}\" Preferences."), IFACE_(display_name));
2737 }
2738 else {
2739 title = IFACE_("Load Factory Blender Preferences");
2740 }
2741
2743 C,
2744 op,
2745 title.c_str(),
2746 IFACE_("To make changes to Preferences permanent, use \"Save Preferences\""),
2747 IFACE_("Load"),
2749 false);
2750}
2751
2753{
2754 ot->name = "Load Factory Preferences";
2755 ot->idname = "WM_OT_read_factory_userpref";
2756 ot->description =
2757 "Load factory default preferences. "
2758 "To make changes to preferences permanent, use \"Save Preferences\"";
2759
2762
2764}
2765
2768/* -------------------------------------------------------------------- */
2772static int wm_history_file_read_exec(bContext * /*C*/, wmOperator * /*op*/)
2773{
2776 return OPERATOR_FINISHED;
2777}
2778
2780{
2781 ot->name = "Reload History File";
2782 ot->idname = "WM_OT_read_history";
2783 ot->description = "Reloads history and bookmarks";
2784
2787
2788 /* This operator is only used for loading settings from a previous blender install. */
2790}
2791
2794/* -------------------------------------------------------------------- */
2801{
2802 const bool use_factory_startup_and_userdef = STREQ(op->type->idname,
2803 "WM_OT_read_factory_settings");
2804 const bool use_factory_settings = use_factory_startup_and_userdef ||
2805 RNA_boolean_get(op->ptr, "use_factory_startup");
2806 const bool use_factory_settings_app_template_only =
2807 (use_factory_startup_and_userdef &&
2808 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only"));
2809
2810 bool use_userdef = false;
2811 char filepath_buf[FILE_MAX];
2812 const char *filepath = nullptr;
2813 UserDef U_backup = blender::dna::shallow_copy(U);
2814
2815 if (!use_factory_settings) {
2816 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "filepath");
2817
2818 /* This can be used when loading of a start-up file should only change
2819 * the scene content but keep the blender UI as it is. */
2820 wm_open_init_load_ui(op, true);
2821 SET_FLAG_FROM_TEST(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
2822
2823 if (RNA_property_is_set(op->ptr, prop)) {
2824 RNA_property_string_get(op->ptr, prop, filepath_buf);
2825 filepath = filepath_buf;
2826 if (BLI_access(filepath, R_OK)) {
2828 op->reports, RPT_ERROR, "Can't read alternative start-up file: \"%s\"", filepath);
2829 return OPERATOR_CANCELLED;
2830 }
2831 }
2832 }
2833 else {
2834 if (use_factory_startup_and_userdef) {
2835 /* Always load UI for factory settings (prefs will re-init). */
2836 G.fileflags &= ~G_FILE_NO_UI;
2837 /* Always load preferences with factory settings. */
2838 use_userdef = true;
2839 }
2840 }
2841
2842 /* Close any user-loaded fonts. */
2844
2845 char app_template_buf[sizeof(U.app_template)];
2846 const char *app_template;
2847 PropertyRNA *prop_app_template = RNA_struct_find_property(op->ptr, "app_template");
2848 const bool use_splash = !use_factory_settings && RNA_boolean_get(op->ptr, "use_splash");
2849 const bool use_empty_data = RNA_boolean_get(op->ptr, "use_empty");
2850
2851 if (prop_app_template && RNA_property_is_set(op->ptr, prop_app_template)) {
2852 RNA_property_string_get(op->ptr, prop_app_template, app_template_buf);
2853 app_template = app_template_buf;
2854
2855 if (!use_factory_settings) {
2856 /* Always load preferences when switching templates with own preferences. */
2859 }
2860
2861 /* Turn override off, since we're explicitly loading a different app-template. */
2863 }
2864 else {
2865 /* Normally nullptr, only set when overriding from the command-line. */
2867 }
2868
2869 if (use_userdef) {
2871 }
2872
2873 wmHomeFileRead_Params read_homefile_params{};
2874 read_homefile_params.use_data = true;
2875 read_homefile_params.use_userdef = use_userdef;
2876 read_homefile_params.use_factory_settings = use_factory_settings;
2877 read_homefile_params.use_factory_settings_app_template_only =
2878 use_factory_settings_app_template_only;
2879 read_homefile_params.use_empty_data = use_empty_data;
2880 read_homefile_params.filepath_startup_override = filepath;
2881 read_homefile_params.app_template_override = app_template;
2882 wm_homefile_read(C, &read_homefile_params, op->reports);
2883
2884 if (use_splash) {
2885 WM_init_splash(C);
2886 }
2887
2888 if (use_userdef) {
2889 wm_userpref_read_exceptions(&U, &U_backup);
2890 SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT);
2891
2892 if (use_factory_settings) {
2893 U.runtime.is_dirty = true;
2894 }
2895 }
2896
2897 if (use_userdef) {
2899 }
2900
2901 if (G.fileflags & G_FILE_NO_UI) {
2903 }
2904
2905 return OPERATOR_FINISHED;
2906}
2907
2908static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data)
2909{
2911 C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data, nullptr);
2912}
2913
2914static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
2915{
2917 return OPERATOR_INTERFACE;
2918 }
2919 return wm_homefile_read_exec(C, op);
2920}
2921
2923{
2924 PropertyRNA *prop;
2925
2926 prop = RNA_def_string(ot->srna, "app_template", "Template", sizeof(U.app_template), "", "");
2928
2929 prop = RNA_def_boolean(
2930 ot->srna,
2931 "use_empty",
2932 false,
2933 "Empty",
2934 "After loading, remove everything except scenes, windows, and workspaces. This makes it "
2935 "possible to load the startup file with its scene configuration and window layout intact, "
2936 "but no objects, materials, animations, ...");
2938}
2939
2941{
2942 PropertyRNA *prop;
2943 ot->name = "Reload Start-Up File";
2944 ot->idname = "WM_OT_read_homefile";
2945 ot->description = "Open the default file";
2946
2949
2951 "filepath",
2952 nullptr,
2953 FILE_MAX,
2954 "File Path",
2955 "Path to an alternative start-up file");
2957
2958 /* So scripts can use an alternative start-up file without the UI. */
2959 prop = RNA_def_boolean(
2960 ot->srna, "load_ui", true, "Load UI", "Load user interface setup from the .blend file");
2962
2963 /* So the splash can be kept open after loading a file (for templates). */
2964 prop = RNA_def_boolean(ot->srna, "use_splash", false, "Splash", "");
2966
2967 /* So scripts can load factory-startup without resetting preferences
2968 * (which has other implications such as reloading all add-ons).
2969 * Match naming for `--factory-startup` command line argument. */
2970 prop = RNA_def_boolean(ot->srna,
2971 "use_factory_startup",
2972 false,
2973 "Factory Startup",
2974 "Load the default ('factory startup') blend file. "
2975 "This is independent of the normal start-up file that the user can save");
2978
2980
2981 /* Omit poll to run in background mode. */
2982}
2983
2984static int wm_read_factory_settings_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
2985{
2987 CTX_wm_manager(C));
2988 std::string title;
2989 const bool template_only = U.app_template[0] &&
2990 RNA_boolean_get(op->ptr, "use_factory_startup_app_template_only");
2991
2992 if (template_only) {
2993 char display_name[FILE_MAX];
2994 BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(U.app_template));
2995 title = fmt::format(IFACE_("Load Factory \"{}\" Startup File and Preferences"),
2996 IFACE_(display_name));
2997 }
2998 else {
2999 title = IFACE_("Load Factory Default Startup File and Preferences");
3000 }
3001
3003 C,
3004 op,
3005 title.c_str(),
3006 unsaved ? IFACE_("To make changes to Preferences permanent, use \"Save Preferences\".\n"
3007 "Warning: Your file is unsaved! Proceeding will abandon your changes.") :
3008 IFACE_("To make changes to Preferences permanent, use \"Save Preferences\"."),
3009 IFACE_("Load"),
3011 false);
3012}
3013
3015{
3016 ot->name = "Load Factory Settings";
3017 ot->idname = "WM_OT_read_factory_settings";
3018 ot->description =
3019 "Load factory default startup file and preferences. "
3020 "To make changes permanent, use \"Save Startup File\" and \"Save Preferences\"";
3021
3024 /* Omit poll to run in background mode. */
3025
3027
3029}
3030
3033/* -------------------------------------------------------------------- */
3040static bool wm_file_read_opwrap(bContext *C, const char *filepath, ReportList *reports)
3041{
3042 /* XXX: wm in context is not set correctly after #WM_file_read -> crash. */
3043 /* Do it before for now, but is this correct with multiple windows? */
3044 WM_event_add_notifier(C, NC_WINDOW, nullptr);
3045
3046 /* Set by the "use_scripts" property on file load. */
3047 if ((G.f & G_FLAG_SCRIPT_AUTOEXEC) == 0) {
3048 WM_file_autoexec_init(filepath);
3049 }
3050
3051 const bool success = WM_file_read(C, filepath, reports);
3052
3053 return success;
3054}
3055
3056/* Generic operator state utilities. */
3057
3058static void create_operator_state(wmOperatorType *ot, int first_state)
3059{
3060 PropertyRNA *prop = RNA_def_int(
3061 ot->srna, "state", first_state, INT32_MIN, INT32_MAX, "State", "", INT32_MIN, INT32_MAX);
3064}
3065
3067{
3068 return RNA_int_get(op->ptr, "state");
3069}
3070
3072{
3073 RNA_int_set(op->ptr, "state", state);
3074}
3075
3080
3082{
3083 int state = get_operator_state(op);
3084 for (int i = 0; targets[i].run; i++) {
3085 OperatorDispatchTarget target = targets[i];
3086 if (target.state == state) {
3087 return target.run(C, op);
3088 }
3089 }
3091 return OPERATOR_CANCELLED;
3092}
3093
3096/* -------------------------------------------------------------------- */
3100enum {
3104};
3105
3107
3108static void wm_open_mainfile_after_dialog_callback(bContext *C, void *user_data)
3109{
3111 C, "WM_OT_open_mainfile", WM_OP_INVOKE_DEFAULT, (IDProperty *)user_data, nullptr);
3112}
3113
3115{
3116 if (RNA_boolean_get(op->ptr, "display_file_selector")) {
3118 }
3119 else {
3121 }
3122
3124 return OPERATOR_INTERFACE;
3125 }
3126 return wm_open_mainfile_dispatch(C, op);
3127}
3128
3130{
3132
3133 Main *bmain = CTX_data_main(C);
3134 const char *blendfile_path = BKE_main_blendfile_path(bmain);
3135
3136 if (CTX_wm_window(C) == nullptr) {
3137 /* In rare cases this could happen, when trying to invoke in background
3138 * mode on load for example. Don't use poll for this because exec()
3139 * can still run without a window. */
3140 BKE_report(op->reports, RPT_ERROR, "Context window not set");
3141 return OPERATOR_CANCELLED;
3142 }
3143
3144 /* If possible, get the name of the most recently used `.blend` file. */
3145 if (G.recent_files.first) {
3146 RecentFile *recent = static_cast<RecentFile *>(G.recent_files.first);
3147 blendfile_path = recent->filepath;
3148 }
3149
3150 RNA_string_set(op->ptr, "filepath", blendfile_path);
3151 wm_open_init_load_ui(op, true);
3152 wm_open_init_use_scripts(op, true);
3153 op->customdata = nullptr;
3154
3156
3158}
3159
3161{
3162 char filepath[FILE_MAX];
3163 bool success;
3164
3165 RNA_string_get(op->ptr, "filepath", filepath);
3166 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3167
3168 /* For file opening, also print in console for warnings, not only errors. */
3170
3171 /* Re-use last loaded setting so we can reload a file without changing. */
3172 wm_open_init_load_ui(op, false);
3173 wm_open_init_use_scripts(op, false);
3174
3175 SET_FLAG_FROM_TEST(G.fileflags, !RNA_boolean_get(op->ptr, "load_ui"), G_FILE_NO_UI);
3177 success = wm_file_read_opwrap(C, filepath, op->reports);
3178
3179 if (success) {
3180 if (G.fileflags & G_FILE_NO_UI) {
3182 }
3183 ED_view3d_local_collections_reset(C, (G.fileflags & G_FILE_NO_UI) != 0);
3184 return OPERATOR_FINISHED;
3185 }
3186 return OPERATOR_CANCELLED;
3187}
3188
3195
3200
3201static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
3202{
3203 return wm_open_mainfile_dispatch(C, op);
3204}
3205
3207{
3208 return wm_open_mainfile__open(C, op);
3209}
3210
3212 wmOperatorType * /*ot*/,
3213 PointerRNA *ptr)
3214{
3215 if (!RNA_struct_property_is_set(ptr, "filepath")) {
3216 return "";
3217 }
3218
3219 char filepath[FILE_MAX];
3220 RNA_string_get(ptr, "filepath", filepath);
3221
3222 BLI_stat_t stats;
3223 if (BLI_stat(filepath, &stats) == -1) {
3224 return fmt::format("{}\n\n{}", filepath, TIP_("File Not Found"));
3225 }
3226
3227 /* Date. */
3228 char date_str[FILELIST_DIRENTRY_DATE_LEN];
3229 char time_str[FILELIST_DIRENTRY_TIME_LEN];
3230 bool is_today, is_yesterday;
3232 nullptr, int64_t(stats.st_mtime), false, time_str, date_str, &is_today, &is_yesterday);
3233 if (is_today || is_yesterday) {
3234 STRNCPY(date_str, is_today ? TIP_("Today") : TIP_("Yesterday"));
3235 }
3236
3237 /* Size. */
3238 char size_str[FILELIST_DIRENTRY_SIZE_LEN];
3239 BLI_filelist_entry_size_to_string(nullptr, uint64_t(stats.st_size), false, size_str);
3240
3241 return fmt::format("{}\n\n{}: {} {}\n{}: {}",
3242 filepath,
3243 TIP_("Modified"),
3244 date_str,
3245 time_str,
3246 TIP_("Size"),
3247 size_str);
3248}
3249
3250/* Currently fits in a pointer. */
3253};
3254BLI_STATIC_ASSERT(sizeof(FileRuntime) <= sizeof(void *), "Struct must not exceed pointer size");
3255
3257{
3258 FileRuntime *file_info = (FileRuntime *)&op->customdata;
3259 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_scripts");
3260 bool is_untrusted = false;
3261 char filepath[FILE_MAX];
3262 char *lslash;
3263
3264 RNA_string_get(op->ptr, "filepath", filepath);
3265
3266 /* Get the directory. */
3267 lslash = (char *)BLI_path_slash_rfind(filepath);
3268 if (lslash) {
3269 *(lslash + 1) = '\0';
3270 }
3271
3272 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
3273 if (BKE_autoexec_match(filepath) == true) {
3274 RNA_property_boolean_set(op->ptr, prop, false);
3275 is_untrusted = true;
3276 }
3277 }
3278
3279 if (file_info) {
3280 file_info->is_untrusted = is_untrusted;
3281 }
3282
3283 return is_untrusted;
3284}
3285
3286static void wm_open_mainfile_ui(bContext * /*C*/, wmOperator *op)
3287{
3288 FileRuntime *file_info = (FileRuntime *)&op->customdata;
3289 uiLayout *layout = op->layout;
3290 const char *autoexec_text;
3291
3292 uiItemR(layout, op->ptr, "load_ui", UI_ITEM_NONE, nullptr, ICON_NONE);
3293
3294 uiLayout *col = uiLayoutColumn(layout, false);
3295 if (file_info->is_untrusted) {
3296 autoexec_text = IFACE_("Trusted Source [Untrusted Path]");
3297 uiLayoutSetActive(col, false);
3298 uiLayoutSetEnabled(col, false);
3299 }
3300 else {
3301 autoexec_text = IFACE_("Trusted Source");
3302 }
3303
3304 uiItemR(col, op->ptr, "use_scripts", UI_ITEM_NONE, autoexec_text, ICON_NONE);
3305}
3306
3308{
3310 "use_scripts",
3311 true,
3312 "Trusted Source",
3313 "Allow .blend file to execute scripts automatically, default available from "
3314 "system preferences");
3315}
3316
3318{
3319 ot->name = "Open";
3320 ot->idname = "WM_OT_open_mainfile";
3321 ot->description = "Open a Blender file";
3323
3328 /* Omit window poll so this can work in background mode. */
3329
3337
3339 ot->srna, "load_ui", true, "Load UI", "Load user interface setup in the .blend file");
3340
3342
3344 ot->srna, "display_file_selector", true, "Display File Selector", "");
3346
3348}
3349
3352/* -------------------------------------------------------------------- */
3356static int wm_revert_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
3357{
3358 std::string message = IFACE_("Any unsaved changes will be lost.");
3360 message += "\n";
3361 message += IFACE_("Warning: There are unsaved external image(s).");
3362 }
3363
3364 return WM_operator_confirm_ex(C,
3365 op,
3366 IFACE_("Revert to the Saved File"),
3367 message.c_str(),
3368 IFACE_("Revert"),
3370 false);
3371}
3372
3374{
3375 Main *bmain = CTX_data_main(C);
3376 bool success;
3377 char filepath[FILE_MAX];
3378
3379 wm_open_init_use_scripts(op, false);
3380
3382
3383 STRNCPY(filepath, BKE_main_blendfile_path(bmain));
3384 success = wm_file_read_opwrap(C, filepath, op->reports);
3385
3386 if (success) {
3387 return OPERATOR_FINISHED;
3388 }
3389 return OPERATOR_CANCELLED;
3390}
3391
3393{
3394 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3395 return (blendfile_path[0] != '\0');
3396}
3397
3399{
3400 ot->name = "Revert";
3401 ot->idname = "WM_OT_revert_mainfile";
3402 ot->description = "Reload the saved file";
3403
3407
3409}
3410
3413/* -------------------------------------------------------------------- */
3418{
3419 char filepath[FILE_MAX];
3420 BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE);
3421 G.fileflags |= G_FILE_RECOVER_READ;
3422 const bool success = wm_file_read_opwrap(C, filepath, reports);
3423 G.fileflags &= ~G_FILE_RECOVER_READ;
3424 return success;
3425}
3426
3428{
3429 wm_open_init_use_scripts(op, true);
3432 if (!G.background) {
3433 wmOperatorType *ot = op->type;
3434 PointerRNA *props_ptr = MEM_new<PointerRNA>(__func__);
3436 RNA_boolean_set(props_ptr, "use_scripts", true);
3438 }
3439 return OPERATOR_FINISHED;
3440 }
3441 return OPERATOR_CANCELLED;
3442}
3443
3445{
3447 C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data, nullptr);
3448}
3449
3450static int wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
3451{
3452 /* Keep the current setting instead of using the preferences since a file selector
3453 * doesn't give us the option to change the setting. */
3454 wm_open_init_use_scripts(op, false);
3455
3458 {
3459 return OPERATOR_INTERFACE;
3460 }
3461 return wm_recover_last_session_exec(C, op);
3462}
3463
3465{
3466 ot->name = "Recover Last Session";
3467 ot->idname = "WM_OT_recover_last_session";
3468 ot->description = "Open the last closed file (\"" BLENDER_QUIT_FILE "\")";
3469
3472
3474}
3475
3478/* -------------------------------------------------------------------- */
3483{
3484 char filepath[FILE_MAX];
3485 bool success;
3486
3487 RNA_string_get(op->ptr, "filepath", filepath);
3488 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3489
3490 wm_open_init_use_scripts(op, true);
3492
3493 G.fileflags |= G_FILE_RECOVER_READ;
3494
3495 success = wm_file_read_opwrap(C, filepath, op->reports);
3496
3497 G.fileflags &= ~G_FILE_RECOVER_READ;
3498
3499 if (success) {
3500 if (!G.background) {
3501 wmOperatorType *ot = op->type;
3502 PointerRNA *props_ptr = MEM_new<PointerRNA>(__func__);
3504 RNA_boolean_set(props_ptr, "use_scripts", true);
3506 }
3507 return OPERATOR_FINISHED;
3508 }
3509 return OPERATOR_CANCELLED;
3510}
3511
3512static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
3513{
3514 char filepath[FILE_MAX];
3515
3516 wm_autosave_location(filepath);
3517 RNA_string_set(op->ptr, "filepath", filepath);
3518 wm_open_init_use_scripts(op, true);
3520
3522}
3523
3525{
3526 ot->name = "Recover Auto Save";
3527 ot->idname = "WM_OT_recover_auto_save";
3528 ot->description = "Open an automatically saved file to recover it";
3529
3532
3540
3542}
3543
3546/* -------------------------------------------------------------------- */
3552static void wm_filepath_default(const Main *bmain, char *filepath)
3553{
3554 if (bmain->filepath[0] == '\0') {
3555 char filename_untitled[FILE_MAXFILE];
3556 SNPRINTF(filename_untitled, "%s.blend", DATA_("untitled"));
3557 BLI_path_filename_ensure(filepath, FILE_MAX, filename_untitled);
3558 }
3559}
3560
3562{
3563 PropertyRNA *prop;
3564
3565 prop = RNA_struct_find_property(op->ptr, "compress");
3566 if (!RNA_property_is_set(op->ptr, prop)) {
3567 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3568 if (blendfile_path[0] != '\0') { /* Keep flag for existing file. */
3569 RNA_property_boolean_set(op->ptr, prop, (G.fileflags & G_FILE_COMPRESS) != 0);
3570 }
3571 else { /* Use userdef for new file. */
3572 RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_FILECOMPRESS) != 0);
3573 }
3574 }
3575}
3576
3578{
3579 Main *bmain = CTX_data_main(C);
3580 PropertyRNA *prop;
3581 char filepath[FILE_MAX];
3582
3583 prop = RNA_struct_find_property(op->ptr, "filepath");
3584 if (!RNA_property_is_set(op->ptr, prop)) {
3585 const char *blendfile_path = BKE_main_blendfile_path(bmain);
3586 /* If not saved before, get the name of the most recently used `.blend` file. */
3587 if ((blendfile_path[0] == '\0') && G.recent_files.first) {
3588 RecentFile *recent = static_cast<RecentFile *>(G.recent_files.first);
3589 STRNCPY(filepath, recent->filepath);
3590 }
3591 else {
3592 STRNCPY(filepath, blendfile_path);
3593 }
3594
3595 /* For convenience when using "Save As" on asset system files:
3596 * Replace `.asset.blend` extension with just `.blend`.
3597 * Asset system files must not be overridden (except by the asset system),
3598 * there are further checks to prevent this entirely. */
3599 if (bmain->is_asset_edit_file &&
3601 {
3602 filepath[strlen(filepath) - strlen(BLENDER_ASSET_FILE_SUFFIX)] = '\0';
3603 BLI_path_extension_ensure(filepath, FILE_MAX, ".blend");
3604 }
3605
3606 wm_filepath_default(bmain, filepath);
3607 RNA_property_string_set(op->ptr, prop, filepath);
3608 }
3609}
3610
3611static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
3612{
3613
3615 save_set_filepath(C, op);
3616
3617 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
3618 if (!RNA_property_is_set(op->ptr, prop)) {
3619 RNA_property_boolean_set(op->ptr, prop, (U.flag & USER_RELPATHS));
3620 }
3621
3623
3625}
3626
3627/* Function used for #WM_OT_save_mainfile too. */
3629{
3630 Main *bmain = CTX_data_main(C);
3631 char filepath[FILE_MAX];
3632 const bool is_save_as = (op->type->invoke == wm_save_as_mainfile_invoke);
3633 const bool use_save_as_copy = is_save_as && RNA_boolean_get(op->ptr, "copy");
3634
3635 /* We could expose all options to the users however in most cases remapping
3636 * existing relative paths is a good default.
3637 * Users can manually make their paths relative & absolute if they wish. */
3638 const eBLO_WritePathRemap remap_mode = RNA_boolean_get(op->ptr, "relative_remap") ?
3642
3643 const bool is_filepath_set = RNA_struct_property_is_set(op->ptr, "filepath");
3644 if (is_filepath_set) {
3645 RNA_string_get(op->ptr, "filepath", filepath);
3646 BLI_path_canonicalize_native(filepath, sizeof(filepath));
3647 }
3648 else {
3649 STRNCPY(filepath, BKE_main_blendfile_path(bmain));
3650 }
3651
3652 if (filepath[0] == '\0') {
3653 BKE_report(op->reports,
3654 RPT_ERROR,
3655 "Unable to save an unsaved file with an empty or unset \"filepath\" property");
3656 return OPERATOR_CANCELLED;
3657 }
3658
3659 if ((is_save_as == false) && RNA_boolean_get(op->ptr, "incremental")) {
3660 char head[FILE_MAXFILE], tail[FILE_MAXFILE];
3661 ushort digits;
3662 int num = BLI_path_sequence_decode(filepath, head, sizeof(head), tail, sizeof(tail), &digits);
3663 /* Numbers greater than INT_MAX return 0, resulting in always appending "1" to the name. */
3664 if (num == 0 && digits == 0) {
3665 /* This does nothing if there are no numbers at the end of the head. */
3667 }
3668
3669 const int tries_limit = 1000;
3670 int tries = 0;
3671 bool in_use = true;
3672 do {
3673 num++;
3674 tries++;
3675 BLI_path_sequence_encode(filepath, sizeof(filepath), head, tail, digits, num);
3676 in_use = BLI_exists(filepath);
3677 } while (in_use && tries < tries_limit && num < INT_MAX);
3678 if (in_use) {
3679 BKE_report(op->reports, RPT_ERROR, "Unable to find an available incremented file name");
3680 return OPERATOR_CANCELLED;
3681 }
3682 }
3683
3684 const int fileflags_orig = G.fileflags;
3685 int fileflags = G.fileflags;
3686
3687 /* Set compression flag. */
3688 SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "compress"), G_FILE_COMPRESS);
3689
3690 const bool success = wm_file_write(
3691 C, filepath, fileflags, remap_mode, use_save_as_copy, op->reports);
3692
3693 if ((op->flag & OP_IS_INVOKE) == 0) {
3694 /* OP_IS_INVOKE is set when the operator is called from the GUI.
3695 * If it is not set, the operator is called from a script and
3696 * shouldn't influence G.fileflags. */
3697 G.fileflags = fileflags_orig;
3698 }
3699
3700 if (success == false) {
3701 return OPERATOR_CANCELLED;
3702 }
3703
3704 if (!use_save_as_copy) {
3705 /* If saved file is the active one, there are technically no more compatibility issues, the
3706 * file on disk now matches the currently opened data version-wise. */
3707 bmain->has_forward_compatibility_issues = false;
3708
3709 /* If saved file is the active one, notify WM so that saved status and window title can be
3710 * updated. */
3712 if (wmWindowManager *wm = CTX_wm_manager(C)) {
3713 /* Restart auto-save timer to avoid unnecessary unexpected freezing (because of auto-save)
3714 * when often saving manually. */
3717 wm->autosave_scheduled = false;
3718 }
3719 }
3720
3721 if (!is_save_as && RNA_boolean_get(op->ptr, "exit")) {
3723 }
3724
3725 return OPERATOR_FINISHED;
3726}
3727
3729{
3730 char filepath[FILE_MAX];
3731 RNA_string_get(op->ptr, "filepath", filepath);
3732 if (!BKE_blendfile_extension_check(filepath)) {
3733 /* NOTE(@ideasman42): some users would prefer #BLI_path_extension_replace(),
3734 * we have had some nitpicking bug reports about this.
3735 * Always adding the extension as users may use '.' as part of the file-name. */
3736 BLI_path_extension_ensure(filepath, FILE_MAX, ".blend");
3737 RNA_string_set(op->ptr, "filepath", filepath);
3738 return true;
3739 }
3740 return false;
3741}
3742
3744{
3745 if (RNA_boolean_get(ptr, "copy")) {
3746 return CTX_IFACE_(ot->translation_context, "Save Copy");
3747 }
3748 return "";
3749}
3750
3752 wmOperatorType * /*ot*/,
3753 PointerRNA *ptr)
3754{
3755 if (RNA_boolean_get(ptr, "copy")) {
3756 return BLI_strdup(TIP_(
3757 "Save the current file in the desired location but do not make the saved file active"));
3758 }
3759 return "";
3760}
3761
3763{
3764 PropertyRNA *prop;
3765
3766 ot->name = "Save As";
3767 ot->idname = "WM_OT_save_as_mainfile";
3768 ot->description = "Save the current file in the desired location";
3769
3775 /* Omit window poll so this can work in background mode. */
3776
3780 FILE_SAVE,
3784 RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
3786 "relative_remap",
3787 true,
3788 "Remap Relative",
3789 "Remap relative paths when saving to a different directory");
3790 prop = RNA_def_boolean(
3791 ot->srna,
3792 "copy",
3793 false,
3794 "Save Copy",
3795 "Save a copy of the actual working state but does not make saved file active");
3797}
3798
3799static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
3800{
3801 int ret;
3802
3803 /* Cancel if no active window. */
3804 if (CTX_wm_window(C) == nullptr) {
3805 return OPERATOR_CANCELLED;
3806 }
3807
3809 save_set_filepath(C, op);
3810
3811 /* If we're saving for the first time and prefer relative paths -
3812 * any existing paths will be absolute,
3813 * enable the option to remap paths to avoid confusion, see: #37240. */
3814 const char *blendfile_path = BKE_main_blendfile_path_from_global();
3815 if ((blendfile_path[0] == '\0') && (U.flag & USER_RELPATHS)) {
3816 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "relative_remap");
3817 if (!RNA_property_is_set(op->ptr, prop)) {
3818 RNA_property_boolean_set(op->ptr, prop, true);
3819 }
3820 }
3821
3822 if (blendfile_path[0] != '\0') {
3826 }
3827 else {
3829 }
3830 }
3831 else {
3834 }
3835
3836 return ret;
3837}
3838
3840 wmOperatorType * /*ot*/,
3841 PointerRNA *ptr)
3842{
3843 if (RNA_boolean_get(ptr, "incremental")) {
3844 return TIP_(
3845 "Save the current Blender file with a numerically incremented name that does not "
3846 "overwrite any existing files");
3847 }
3848 return "";
3849}
3850
3852{
3853 ot->name = "Save Blender File";
3854 ot->idname = "WM_OT_save_mainfile";
3855 ot->description = "Save the current Blender file";
3856
3861 /* Omit window poll so this can work in background mode. */
3862
3863 PropertyRNA *prop;
3867 FILE_SAVE,
3871 RNA_def_boolean(ot->srna, "compress", false, "Compress", "Write compressed .blend file");
3873 "relative_remap",
3874 false,
3875 "Remap Relative",
3876 "Remap relative paths when saving to a different directory");
3877
3878 prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "Exit Blender after saving");
3880
3881 prop = RNA_def_boolean(ot->srna,
3882 "incremental",
3883 false,
3884 "Incremental",
3885 "Save the current Blender file with a numerically incremented name that "
3886 "does not overwrite any existing files");
3888}
3889
3892/* -------------------------------------------------------------------- */
3897
3899 {CLEAR_RECENT_ALL, "ALL", 0, "All Items", ""},
3900 {CLEAR_RECENT_MISSING, "MISSING", 0, "Items Not Found", ""},
3901 {0, nullptr, 0, nullptr, nullptr},
3902};
3903
3905{
3907 C, op, event, IFACE_("Clear Recent Files List"), IFACE_("Remove"));
3908}
3909
3911{
3912 ClearRecentInclude include = static_cast<ClearRecentInclude>(RNA_enum_get(op->ptr, "remove"));
3913
3914 if (include == CLEAR_RECENT_ALL) {
3916 }
3917 else if (include == CLEAR_RECENT_MISSING) {
3918 LISTBASE_FOREACH_MUTABLE (RecentFile *, recent, &G.recent_files) {
3919 if (!BLI_exists(recent->filepath)) {
3920 BLI_freelinkN(&G.recent_files, recent);
3921 }
3922 }
3923 }
3924
3926
3927 return OPERATOR_FINISHED;
3928}
3929
3931{
3932 uiLayout *layout = op->layout;
3933 uiLayoutSetPropSep(layout, true);
3934 uiLayoutSetPropDecorate(layout, false);
3935
3936 uiItemS(layout);
3937 uiItemR(layout, op->ptr, "remove", UI_ITEM_R_TOGGLE, nullptr, ICON_NONE);
3938 uiItemS(layout);
3939}
3940
3942{
3943 ot->name = "Clear Recent Files List";
3944 ot->idname = "WM_OT_clear_recent_files";
3945 ot->description = "Clear the recent files list";
3946
3950
3951 /* flags */
3953
3954 /* props */
3955 ot->prop = RNA_def_enum(
3956 ot->srna, "remove", prop_clear_recent_types, CLEAR_RECENT_ALL, "Remove", "");
3957}
3958
3961/* -------------------------------------------------------------------- */
3965static void wm_block_autorun_warning_ignore(bContext *C, void *arg_block, void * /*arg*/)
3966{
3967 wmWindow *win = CTX_wm_window(C);
3968 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
3969
3970 /* Free the data as it's no longer needed. */
3971 wm_test_autorun_revert_action_set(nullptr, nullptr);
3972}
3973
3975{
3976 wmWindow *win = CTX_wm_window(C);
3977
3978 UI_popup_block_close(C, win, block);
3979
3980 /* Save user preferences for permanent execution. */
3981 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
3982 WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, nullptr, nullptr);
3983 }
3984
3985 /* Load file again with scripts enabled.
3986 * The reload is necessary to allow scripts to run when the files loads. */
3988}
3989
3991{
3992 wmWindow *win = CTX_wm_window(C);
3993 Main *bmain = CTX_data_main(C);
3994
3995 UI_popup_block_close(C, win, block);
3996
3997 /* Save user preferences for permanent execution. */
3998 if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) {
3999 WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, nullptr, nullptr);
4000 }
4001
4002 /* Force a full refresh, but without reloading the file. */
4003 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
4005 }
4006}
4007
4008/* Build the auto-run warning dialog UI. */
4009static uiBlock *block_create_autorun_warning(bContext *C, ARegion *region, void * /*arg1*/)
4010{
4011 const char *blendfile_path = BKE_main_blendfile_path_from_global();
4013
4014 uiBlock *block = UI_block_begin(C, region, "autorun_warning_popup", UI_EMBOSS);
4019
4020 uiLayout *layout = uiItemsAlertBox(block, 44, ALERT_ICON_ERROR);
4021
4022 /* Title and explanation text. */
4023 uiLayout *col = uiLayoutColumn(layout, true);
4025 RPT_("For security reasons, automatic execution of Python scripts "
4026 "in this file was disabled:"),
4027 ICON_NONE,
4028 true,
4029 false);
4030 uiItemL_ex(col, G.autoexec_fail, ICON_NONE, false, true);
4031 uiItemL(col, RPT_("This may lead to unexpected behavior"), ICON_NONE);
4032
4033 uiItemS(layout);
4034
4035 PointerRNA pref_ptr = RNA_pointer_create(nullptr, &RNA_PreferencesFilePaths, &U);
4036 uiItemR(layout,
4037 &pref_ptr,
4038 "use_scripts_auto_execute",
4040 RPT_("Permanently allow execution of scripts"),
4041 ICON_NONE);
4042
4043 uiItemS_ex(layout, 3.0f);
4044
4045 /* Buttons. */
4046 uiBut *but;
4047 uiLayout *split = uiLayoutSplit(layout, 0.0f, true);
4048 uiLayoutSetScaleY(split, 1.2f);
4049
4050 /* Empty space. */
4051 col = uiLayoutColumn(split, false);
4052 uiItemS(col);
4053
4054 col = uiLayoutColumn(split, false);
4055
4056 /* Allow reload if we have a saved file.
4057 * Otherwise just enable scripts and reset the depsgraphs. */
4058 if ((blendfile_path[0] != '\0') && wm->file_saved) {
4059 but = uiDefIconTextBut(block,
4061 0,
4062 ICON_NONE,
4063 IFACE_("Allow Execution"),
4064 0,
4065 0,
4066 50,
4067 UI_UNIT_Y,
4068 nullptr,
4069 0,
4070 0,
4071 TIP_("Reload file with execution of Python scripts enabled"));
4073 but, [block](bContext &C) { wm_block_autorun_warning_reload_with_scripts(&C, block); });
4074 }
4075 else {
4076 but = uiDefIconTextBut(block,
4078 0,
4079 ICON_NONE,
4080 IFACE_("Allow Execution"),
4081 0,
4082 0,
4083 50,
4084 UI_UNIT_Y,
4085 nullptr,
4086 0,
4087 0,
4088 TIP_("Enable scripts"));
4089 UI_but_func_set(but,
4090 [block](bContext &C) { wm_block_autorun_warning_enable_scripts(&C, block); });
4091 }
4093
4094 col = uiLayoutColumn(split, false);
4095 but = uiDefIconTextBut(block,
4097 0,
4098 ICON_NONE,
4099 IFACE_("Ignore"),
4100 0,
4101 0,
4102 50,
4103 UI_UNIT_Y,
4104 nullptr,
4105 0,
4106 0,
4107 TIP_("Continue using file without Python scripts"));
4111
4113
4114 return block;
4115}
4116
4124static struct {
4128
4141
4143{
4146
4147 /* Use regular revert. */
4148 if (ot == nullptr) {
4149 ot = WM_operatortype_find("WM_OT_revert_mainfile", false);
4150 ptr = MEM_new<PointerRNA>(__func__);
4152 RNA_boolean_set(ptr, "use_scripts", true);
4153
4154 /* Set state, so it's freed correctly. */
4156 }
4157
4159 wm_test_autorun_revert_action_set(nullptr, nullptr);
4160}
4161
4163{
4164 /* Test if any auto-execution of scripts failed. */
4165 if ((G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL) == 0) {
4166 return;
4167 }
4168
4169 /* Only show the warning once. */
4171 return;
4172 }
4173
4175
4177 wmWindow *win = (wm->winactive) ? wm->winactive : static_cast<wmWindow *>(wm->windows.first);
4178
4179 if (win) {
4180 /* We want this warning on the Main window, not a child window even if active. See #118765. */
4181 if (win->parent) {
4182 win = win->parent;
4183 }
4184
4185 wmWindow *prevwin = CTX_wm_window(C);
4186 CTX_wm_window_set(C, win);
4188 CTX_wm_window_set(C, prevwin);
4189 }
4190}
4191
4194/* -------------------------------------------------------------------- */
4198static void free_post_file_close_action(void *arg)
4199{
4200 wmGenericCallback *action = (wmGenericCallback *)arg;
4202}
4203
4204static void wm_free_operator_properties_callback(void *user_data)
4205{
4206 IDProperty *properties = (IDProperty *)user_data;
4207 IDP_FreeProperty(properties);
4208}
4209
4210static const char *save_file_overwrite_dialog_name = "save_file_overwrite_popup";
4211
4212static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bmain)
4213{
4214 uiLayout *layout = uiLayoutColumn(parent_layout, true);
4215 /* Trick to make both lines of text below close enough to look like they are part of a same
4216 * block. */
4217 uiLayoutSetScaleY(layout, 0.70f);
4218
4220 char writer_ver_str[16];
4221 char current_ver_str[16];
4222 if (bmain->versionfile == BLENDER_VERSION) {
4224 writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile);
4226 current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION);
4227 }
4228 else {
4230 writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
4232 current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1);
4233 }
4234
4235 char message_line1[256];
4236 char message_line2[256];
4237 SNPRINTF(message_line1,
4238 RPT_("This file was saved by a newer version of Blender (%s)"),
4239 writer_ver_str);
4240 SNPRINTF(message_line2,
4241 RPT_("Saving it with this Blender (%s) may cause loss of data"),
4242 current_ver_str);
4243 uiItemL(layout, message_line1, ICON_NONE);
4244 uiItemL(layout, message_line2, ICON_NONE);
4245 }
4246
4247 if (bmain->is_asset_edit_file) {
4249 uiItemS_ex(layout, 1.4f);
4250 }
4251
4252 uiItemL(layout,
4253 RPT_("This file is managed by the Blender asset system. It can only be"),
4254 ICON_NONE);
4255 uiItemL(layout, RPT_("saved as a new, regular file."), ICON_NONE);
4256 }
4257}
4258
4259static void save_file_overwrite_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
4260{
4261 wmWindow *win = CTX_wm_window(C);
4262 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4263}
4264
4266{
4267 uiBut *but = uiDefIconTextBut(
4268 block, UI_BTYPE_BUT, 0, ICON_NONE, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, nullptr, 0, 0, "");
4269 UI_but_func_set(but, save_file_overwrite_cancel, block, post_action);
4271}
4272
4273static void save_file_overwrite_confirm(bContext *C, void *arg_block, void *arg_data)
4274{
4275 wmWindow *win = CTX_wm_window(C);
4276
4277 /* Re-use operator properties as defined for the initial 'save' operator, which triggered this
4278 * 'forward compat' popup. */
4280 static_cast<wmGenericCallback *>(arg_data));
4281
4282 /* Needs to be done after stealing the callback data above, otherwise it would cause a
4283 * use-after-free. */
4284 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4285
4286 PointerRNA operator_propptr = {};
4287 PointerRNA *operator_propptr_p = &operator_propptr;
4288 IDProperty *operator_idproperties = static_cast<IDProperty *>(callback->user_data);
4289 WM_operator_properties_alloc(&operator_propptr_p, &operator_idproperties, "WM_OT_save_mainfile");
4290
4291 WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, operator_propptr_p, nullptr);
4292
4294}
4295
4297{
4298 uiBut *but = uiDefIconTextBut(block,
4300 0,
4301 ICON_NONE,
4302 IFACE_("Overwrite"),
4303 0,
4304 0,
4305 0,
4306 UI_UNIT_Y,
4307 nullptr,
4308 0,
4309 0,
4310 "");
4311 UI_but_func_set(but, save_file_overwrite_confirm, block, post_action);
4314}
4315
4316static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*arg_data*/)
4317{
4318 wmWindow *win = CTX_wm_window(C);
4319 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4320
4321 WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
4322}
4323
4325{
4326 uiBut *but = uiDefIconTextBut(block,
4328 0,
4329 ICON_NONE,
4330 IFACE_("Save As..."),
4331 0,
4332 0,
4333 0,
4334 UI_UNIT_Y,
4335 nullptr,
4336 0,
4337 0,
4338 "");
4339 UI_but_func_set(but, save_file_overwrite_saveas, block, post_action);
4342}
4343
4345{
4346 wmGenericCallback *post_action = static_cast<wmGenericCallback *>(arg1);
4347 Main *bmain = CTX_data_main(C);
4348
4353
4354 uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_WARNING);
4355
4356 /* Title. */
4358 if (bmain->is_asset_edit_file) {
4359 uiItemL_ex(layout,
4360 RPT_("Cannot overwrite asset system files. Save as new file"),
4361 ICON_NONE,
4362 true,
4363 false);
4364 uiItemL_ex(layout, RPT_("with an older Blender version?"), ICON_NONE, true, false);
4365 }
4366 else {
4367 uiItemL_ex(
4368 layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
4369 }
4370 }
4371 else if (bmain->is_asset_edit_file) {
4372 uiItemL_ex(layout,
4373 RPT_("Cannot overwrite asset system files. Save as new file?"),
4374 ICON_NONE,
4375 true,
4376 false);
4377 }
4378 else {
4380 }
4381
4382 /* Filename. */
4383 const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
4384 char filename[FILE_MAX];
4385 if (blendfile_path[0] != '\0') {
4386 BLI_path_split_file_part(blendfile_path, filename, sizeof(filename));
4387 }
4388 else {
4389 SNPRINTF(filename, "%s.blend", DATA_("untitled"));
4390 /* Since this dialog should only be shown when re-saving an existing file, current filepath
4391 * should never be empty. */
4393 }
4394 uiItemL(layout, filename, ICON_NONE);
4395
4396 /* Detailed message info. */
4397 file_overwrite_detailed_info_show(layout, bmain);
4398
4399 uiItemS_ex(layout, 4.0f);
4400
4401 /* Buttons. */
4402
4403 uiLayout *split = uiLayoutSplit(layout, 0.3f, true);
4404 uiLayoutSetScaleY(split, 1.2f);
4405
4406 uiLayoutColumn(split, false);
4407 /* Asset files don't actually allow overriding. */
4408 const bool allow_overwrite = !bmain->is_asset_edit_file;
4409 if (allow_overwrite) {
4410 save_file_overwrite_confirm_button(block, post_action);
4411 }
4412
4413 uiLayout *split_right = uiLayoutSplit(split, 0.1f, true);
4414
4415 uiLayoutColumn(split_right, false);
4416 /* Empty space. */
4417
4418 uiLayoutColumn(split_right, false);
4419 save_file_overwrite_cancel_button(block, post_action);
4420
4421 uiLayoutColumn(split_right, false);
4422 save_file_overwrite_saveas_button(block, post_action);
4423
4425 return block;
4426}
4427
4440
4443/* -------------------------------------------------------------------- */
4448
4449static void wm_block_file_close_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
4450{
4451 wmWindow *win = CTX_wm_window(C);
4452 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4453}
4454
4455static void wm_block_file_close_discard(bContext *C, void *arg_block, void *arg_data)
4456{
4458
4459 /* Close the popup before executing the callback. Otherwise
4460 * the popup might be closed by the callback, which will lead
4461 * to a crash. */
4462 wmWindow *win = CTX_wm_window(C);
4463 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4464
4465 callback->exec(C, callback->user_data);
4467}
4468
4469static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_data)
4470{
4471 const Main *bmain = CTX_data_main(C);
4472 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
4474 bool execute_callback = true;
4475
4476 wmWindow *win = CTX_wm_window(C);
4477 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
4478
4479 int modified_images_count = ED_image_save_all_modified_info(CTX_data_main(C), nullptr);
4480 if (modified_images_count > 0 && save_images_when_file_is_closed) {
4481 if (ED_image_should_save_modified(bmain)) {
4482 ReportList *reports = CTX_wm_reports(C);
4483 ED_image_save_all_modified(C, reports);
4484 WM_report_banner_show(wm, win);
4485 }
4486 else {
4487 execute_callback = false;
4488 }
4489 }
4490
4491 bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0';
4492
4493 if (file_has_been_saved_before) {
4495 /* Need to invoke to get the file-browser and choose where to save the new file.
4496 * This also makes it impossible to keep on going with current operation, which is why
4497 * callback cannot be executed anymore.
4498 *
4499 * This is the same situation as what happens when the file has never been saved before
4500 * (outer `else` statement, below). */
4501 WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
4502 execute_callback = false;
4503 }
4504 else {
4505 if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, nullptr, nullptr) &
4507 {
4508 execute_callback = false;
4509 }
4510 }
4511 }
4512 else {
4513 WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
4514 execute_callback = false;
4515 }
4516
4517 if (execute_callback) {
4518 callback->exec(C, callback->user_data);
4519 }
4521}
4522
4524{
4525 uiBut *but = uiDefIconTextBut(
4526 block, UI_BTYPE_BUT, 0, ICON_NONE, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, nullptr, 0, 0, "");
4527 UI_but_func_set(but, wm_block_file_close_cancel, block, post_action);
4529}
4530
4532{
4533 uiBut *but = uiDefIconTextBut(block,
4535 0,
4536 ICON_NONE,
4537 IFACE_("Don't Save"),
4538 0,
4539 0,
4540 0,
4541 UI_UNIT_Y,
4542 nullptr,
4543 0,
4544 0,
4545 "");
4546 UI_but_func_set(but, wm_block_file_close_discard, block, post_action);
4548}
4549
4551 wmGenericCallback *post_action,
4552 const bool needs_overwrite_confirm)
4553{
4554 uiBut *but = uiDefIconTextBut(
4555 block,
4557 0,
4558 ICON_NONE,
4559 /* Forward compatibility issues force using 'save as' operator instead of 'save' one. */
4560 needs_overwrite_confirm ? IFACE_("Save As...") : IFACE_("Save"),
4561 0,
4562 0,
4563 0,
4564 UI_UNIT_Y,
4565 nullptr,
4566 0,
4567 0,
4568 "");
4569 UI_but_func_set(but, wm_block_file_close_save, block, post_action);
4572}
4573
4574static const char *close_file_dialog_name = "file_close_popup";
4575
4576static void save_catalogs_when_file_is_closed_set_fn(bContext * /*C*/, void *arg1, void * /*arg2*/)
4577{
4578 char *save_catalogs_when_file_is_closed = static_cast<char *>(arg1);
4580 *save_catalogs_when_file_is_closed != 0);
4581}
4582
4584{
4585 using namespace blender;
4586 wmGenericCallback *post_action = (wmGenericCallback *)arg1;
4587 Main *bmain = CTX_data_main(C);
4588
4593
4594 uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION);
4595
4596 const bool needs_overwrite_confirm = BKE_main_needs_overwrite_confirm(bmain);
4597
4598 /* Title. */
4599 uiItemL_ex(layout, RPT_("Save changes before closing?"), ICON_NONE, true, false);
4600
4601 /* Filename. */
4602 const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
4603 char filename[FILE_MAX];
4604 if (blendfile_path[0] != '\0') {
4605 BLI_path_split_file_part(blendfile_path, filename, sizeof(filename));
4606 }
4607 else {
4608 SNPRINTF(filename, "%s.blend", DATA_("untitled"));
4609 }
4610 uiItemL(layout, filename, ICON_NONE);
4611
4612 /* Potential forward compatibility issues message. */
4613 if (needs_overwrite_confirm) {
4614 file_overwrite_detailed_info_show(layout, bmain);
4615 }
4616
4617 /* Image Saving Warnings. */
4618 ReportList reports;
4619 BKE_reports_init(&reports, RPT_STORE);
4620 uint modified_images_count = ED_image_save_all_modified_info(bmain, &reports);
4621
4622 LISTBASE_FOREACH (Report *, report, &reports.list) {
4623 uiLayout *row = uiLayoutColumn(layout, false);
4624 uiLayoutSetScaleY(row, 0.6f);
4625 uiItemS(row);
4626
4627 /* Error messages created in ED_image_save_all_modified_info() can be long,
4628 * but are made to separate into two parts at first colon between text and paths.
4629 */
4630 char *message = BLI_strdupn(report->message, report->len);
4631 char *path_info = strstr(message, ": ");
4632 if (path_info) {
4633 /* Terminate message string at colon. */
4634 path_info[1] = '\0';
4635 /* Skip over the ": ". */
4636 path_info += 2;
4637 }
4638 uiItemL_ex(row, message, ICON_NONE, false, true);
4639 if (path_info) {
4640 uiItemL_ex(row, path_info, ICON_NONE, false, true);
4641 }
4642 MEM_freeN(message);
4643 }
4644
4645 /* Used to determine if extra separators are needed. */
4646 bool has_extra_checkboxes = false;
4647
4648 /* Modified Images Checkbox. */
4649 if (modified_images_count > 0) {
4650 char message[64];
4651 SNPRINTF(message, "Save %u modified image(s)", modified_images_count);
4652 /* Only the first checkbox should get extra separation. */
4653 if (!has_extra_checkboxes) {
4654 uiItemS(layout);
4655 }
4656 uiDefButBitC(block,
4658 1,
4659 0,
4660 message,
4661 0,
4662 0,
4663 0,
4664 UI_UNIT_Y,
4666 0,
4667 0,
4668 "");
4669 has_extra_checkboxes = true;
4670 }
4671
4673 static char save_catalogs_when_file_is_closed;
4674
4675 save_catalogs_when_file_is_closed = ed::asset::catalogs_get_save_catalogs_when_file_is_saved();
4676
4677 /* Only the first checkbox should get extra separation. */
4678 if (!has_extra_checkboxes) {
4679 uiItemS(layout);
4680 }
4681 uiBut *but = uiDefButBitC(block,
4683 1,
4684 0,
4685 "Save modified asset catalogs",
4686 0,
4687 0,
4688 0,
4689 UI_UNIT_Y,
4690 &save_catalogs_when_file_is_closed,
4691 0,
4692 0,
4693 "");
4694 UI_but_func_set(but,
4696 &save_catalogs_when_file_is_closed,
4697 nullptr);
4698 has_extra_checkboxes = true;
4699 }
4700
4701 BKE_reports_free(&reports);
4702
4703 uiItemS_ex(layout, has_extra_checkboxes ? 2.0f : 4.0f);
4704
4705 /* Buttons. */
4706#ifdef _WIN32
4707 const bool windows_layout = true;
4708#else
4709 const bool windows_layout = false;
4710#endif
4711
4712 if (windows_layout) {
4713 /* Windows standard layout. */
4714
4715 uiLayout *split = uiLayoutSplit(layout, 0.0f, true);
4716 uiLayoutSetScaleY(split, 1.2f);
4717
4718 uiLayoutColumn(split, false);
4719 wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm);
4720
4721 uiLayoutColumn(split, false);
4722 wm_block_file_close_discard_button(block, post_action);
4723
4724 uiLayoutColumn(split, false);
4725 wm_block_file_close_cancel_button(block, post_action);
4726 }
4727 else {
4728 /* Non-Windows layout (macOS and Linux). */
4729
4730 uiLayout *split = uiLayoutSplit(layout, 0.3f, true);
4731 uiLayoutSetScaleY(split, 1.2f);
4732
4733 uiLayoutColumn(split, false);
4734 wm_block_file_close_discard_button(block, post_action);
4735
4736 uiLayout *split_right = uiLayoutSplit(split, 0.1f, true);
4737
4738 uiLayoutColumn(split_right, false);
4739 /* Empty space. */
4740
4741 uiLayoutColumn(split_right, false);
4742 wm_block_file_close_cancel_button(block, post_action);
4743
4744 uiLayoutColumn(split_right, false);
4745 wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm);
4746 }
4747
4749 return block;
4750}
4751
4762
4764 wmOperator *op,
4765 wmGenericCallbackFn post_action_fn)
4766{
4767 if (U.uiflag & USER_SAVE_PROMPT &&
4769 {
4770 wmGenericCallback *callback = MEM_cnew<wmGenericCallback>(__func__);
4771 callback->exec = post_action_fn;
4772 callback->user_data = IDP_CopyProperty(op->properties);
4775 return true;
4776 }
4777
4778 return false;
4779}
4780
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:57
bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_maxncpy) ATTR_NONNULL(1)
Definition appdir.cc:1071
void BKE_tempdir_init(const char *userdir)
Definition appdir.cc:1190
#define BLENDER_USERPREF_FILE
bool BKE_appdir_app_template_has_userpref(const char *app_template) ATTR_NONNULL(1)
Definition appdir.cc:1085
#define BLENDER_HISTORY_FILE
@ BLENDER_USER_CONFIG
std::optional< std::string > BKE_appdir_folder_id(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:704
#define BLENDER_STARTUP_FILE
#define BLENDER_QUIT_FILE
const char * BKE_tempdir_base() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
Definition appdir.cc:1211
bool BKE_appdir_app_template_any()
Definition appdir.cc:1066
std::optional< std::string > BKE_appdir_folder_id_create(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:764
bool BKE_autoexec_match(const char *path)
Definition autoexec.cc:27
Blender util stuff.
void BKE_blender_userdef_app_template_data_set_and_free(UserDef *userdef)
Definition blender.cc:445
void BKE_blender_userdef_data_set_and_free(UserDef *userdef)
Definition blender.cc:264
#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:149
#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:83
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:64
void BKE_callback_exec_null(Main *bmain, eCbEvent evt)
Definition callbacks.cc:39
@ BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST
@ BKE_CB_EVT_LOAD_FACTORY_USERDEF_POST
@ BKE_CB_EVT_SAVE_PRE
@ BKE_CB_EVT_LOAD_POST_FAIL
@ BKE_CB_EVT_EXTENSION_REPOS_UPDATE_PRE
@ BKE_CB_EVT_VERSION_UPDATE
@ BKE_CB_EVT_SAVE_POST_FAIL
@ BKE_CB_EVT_SAVE_POST
@ BKE_CB_EVT_LOAD_POST
@ BKE_CB_EVT_LOAD_PRE
@ BKE_CB_EVT_EXTENSION_REPOS_UPDATE_POST
ReportList * CTX_wm_reports(const bContext *C)
bScreen * CTX_wm_screen(const bContext *C)
bool CTX_py_init_get(bContext *C)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
void CTX_wm_window_set(bContext *C, wmWindow *win)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
wmMsgBus * CTX_wm_message_bus(const bContext *C)
void CTX_wm_region_popup_set(bContext *C, ARegion *region_popup)
@ G_FLAG_SCRIPT_OVERRIDE_PREF
@ G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET
@ G_FLAG_USERPREF_NO_SAVE_ON_EXIT
@ G_FLAG_SCRIPT_AUTOEXEC_FAIL
@ G_FLAG_SCRIPT_AUTOEXEC
@ G_FLAG_INTERNET_ALLOW
#define G_FLAG_ALL_READFILE
@ G_FILE_RECOVER_READ
@ G_FILE_AUTOPACK
@ G_FILE_RECOVER_WRITE
@ G_FILE_NO_UI
@ G_FILE_COMPRESS
#define G_MAIN
#define G_FLAG_INTERNET_OVERRIDE_PREF_ANY
#define G_FLAG_ALL_RUNTIME
void IDP_FreeProperty(IDProperty *prop)
Definition idprop.cc:1227
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:861
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:2316
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:782
bool BKE_main_needs_overwrite_confirm(const Main *bmain)
Definition main.cc:474
#define BLEN_THUMB_SIZE
Definition BKE_main.hh:595
ImBuf * BKE_main_thumbnail_to_imbuf(Main *bmain, BlendThumbnail *data)
Definition main.cc:806
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:832
BlendThumbnail * BKE_main_thumbnail_from_buffer(Main *bmain, const uint8_t *rect, const int size[2])
Definition main.cc:760
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:837
bool BKE_main_namemap_validate(Main *bmain) ATTR_NONNULL()
#define FOREACH_NODETREE_END
Definition BKE_node.hh:870
#define FOREACH_NODETREE_BEGIN(bmain, _nodetree, _id)
Definition BKE_node.hh:860
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:69
void BKE_report_print_level_set(ReportList *reports, eReportType level)
Definition report.cc:237
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
void BKE_reports_init(ReportList *reports, int flag)
Definition report.cc:54
void BKE_scene_free_depsgraph_hash(Scene *scene)
Definition scene.cc:3272
ScrArea * BKE_screen_find_big_area(const bScreen *screen, int spacetype, short min)
Definition screen.cc:903
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:815
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:566
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:84
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:87
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
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:554
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:600
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()
Some types for dealing with directories.
#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()
void * BLI_findstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count_at_most(const struct ListBase *listbase, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_findstring_ptr(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
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.c:12
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.c:40
#define STRNCPY(dst, src)
Definition BLI_string.h:593
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.c:29
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
int BLI_str_rstrip_digits(char *str) ATTR_NONNULL()
Definition string.c:1008
unsigned short ushort
unsigned int uint
int BLI_thread_is_main(void)
Definition threads.cc:179
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
void BLI_timer_on_file_load(void)
Definition BLI_timer.c:142
#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.
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_ERROR(clg_ref,...)
Definition CLG_log.h:182
#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
Object is a sort of wrapper for general info.
@ 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
@ 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
#define UI_SCALE_FAC
@ V3D_OFSDRAW_NONE
@ V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS
@ V3D_RUNTIME_LOCAL_MAYBE_EMPTY
@ OPERATOR_RUNNING_MODAL
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:218
bool ED_editors_flush_edits(Main *bmain)
Definition ed_util.cc:331
void ED_editors_init(bContext *C)
Definition ed_util.cc:94
void ED_view3d_local_collections_reset(const bContext *C, bool reset_all)
ImBuf * ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, Scene *scene, eDrawType drawtype, View3D *v3d, ARegion *region, int sizex, int sizey, eImBufFlags imbuf_flag, int alpha_mode, const char *viewname, bool restore_rv3d_mats, GPUOffScreen *ofs, GPUViewport *viewport, char err_out[256])
ImBuf * ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph, Scene *scene, View3DShading *shading_override, eDrawType drawtype, Object *camera, int width, int height, eImBufFlags imbuf_flags, eV3DOffscreenDrawFlag draw_flags, int alpha_mode, const char *viewname, GPUOffScreen *ofs, GPUViewport *viewport, char err_out[256])
GHOST C-API function and type declarations.
void GHOST_SetWindowUserData(GHOST_WindowHandle windowhandle, GHOST_TUserDataPtr user_data)
void GHOST_addToSystemRecentFiles(const char *filepath)
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)
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:779
Contains defines and structs used throughout the imbuf module.
@ IB_rect
void IMB_metadata_set_field(IDProperty *metadata, const char *key, const char *value)
Definition metadata.cc:69
void IMB_metadata_ensure(IDProperty **metadata)
Definition metadata.cc:24
ImBuf * IMB_thumb_create(const char *filepath, ThumbSize size, ThumbSource source, ImBuf *img)
Definition thumbs.cc:485
void IMB_thumb_delete(const char *file_or_lib_path, ThumbSize size)
Definition thumbs.cc:515
@ THB_FAIL
Definition IMB_thumbs.hh:22
@ THB_LARGE
Definition IMB_thumbs.hh:21
@ THB_SOURCE_BLEND
Definition IMB_thumbs.hh:28
#define PREVIEW_RENDER_LARGE_HEIGHT
Definition IMB_thumbs.hh:39
void MEM_CacheLimiter_set_maximum(size_t m)
Read Guarded memory(de)allocation.
@ RNA_EQ_STRICT
@ PROP_POINTER
Definition RNA_types.hh:70
@ PROP_COLLECTION
Definition RNA_types.hh:71
PropertyFlag
Definition RNA_types.hh:201
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
#define C
Definition RandGen.cpp:29
constexpr int SEQ_MAX_CHANNELS
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
#define UI_UNIT_Y
void uiLayoutSetActive(uiLayout *layout, bool active)
void UI_init_userdef()
@ UI_EMBOSS
void UI_block_theme_style_set(uiBlock *block, char theme_style)
void uiLayoutSetEnabled(uiLayout *layout, bool enabled)
@ UI_BLOCK_NUMSELECT
@ UI_BLOCK_LOOP
@ UI_BLOCK_KEEP_OPEN
@ UI_BLOCK_NO_WIN_CLIP
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 uiItemL(uiLayout *layout, const char *name, int icon)
void UI_popup_block_invoke(bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, eUIEmbossType emboss)
void uiItemS(uiLayout *layout)
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, const char *tip)
void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block)
bool UI_popup_block_name_exists(const bScreen *screen, blender::StringRef name)
void uiItemS_ex(uiLayout *layout, float factor, LayoutSeparatorType type=LayoutSeparatorType::Auto)
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, const char *tip)
#define UI_ITEM_NONE
void UI_block_emboss_set(uiBlock *block, eUIEmbossType emboss)
void UI_but_drawflag_disable(uiBut *but, int flag)
@ UI_BLOCK_THEME_STYLE_POPUP
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void UI_block_bounds_set_centered(uiBlock *block, int addval)
Definition interface.cc:616
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
uiLayout * uiLayoutSplit(uiLayout *layout, float percentage, bool align)
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BTYPE_BUT
@ UI_BTYPE_CHECKBOX
uiBut * uiItemL_ex(uiLayout *layout, const char *name, int icon, bool highlight, bool redalert)
void UI_but_flag_enable(uiBut *but, int flag)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_BUT_TEXT_LEFT
@ UI_ITEM_R_TOGGLE
@ UI_BUT_REDALERT
@ UI_BUT_ACTIVE_DEFAULT
@ ALERT_ICON_WARNING
@ ALERT_ICON_QUESTION
@ ALERT_ICON_ERROR
void UI_view2d_zoom_cache_reset()
Definition view2d.cc:1037
@ WM_FILESEL_FILEPATH
Definition WM_api.hh:936
@ FILE_OPENFILE
Definition WM_api.hh:945
@ FILE_SAVE
Definition WM_api.hh:946
#define ND_ASSET_LIST_READING
Definition WM_types.hh:516
@ OPTYPE_INTERNAL
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_WINDOW
Definition WM_types.hh:342
#define ND_FILEREAD
Definition WM_types.hh:379
#define NC_WM
Definition WM_types.hh:341
#define ND_DATACHANGED
Definition WM_types.hh:381
#define NC_ASSET
Definition WM_types.hh:371
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:225
#define ND_FILESAVE
Definition WM_types.hh:380
void(*)(bContext *C, void *user_data) wmGenericCallbackFn
Definition WM_types.hh:147
ATTR_WARN_UNUSED_RESULT const BMLoop * l
unsigned int U
Definition btGjkEpa3.h:78
constexpr bool endswith(StringRef suffix) const
#define printf
const Depsgraph * depsgraph
DEGForeachIDComponentCallback callback
#define offsetof(t, d)
int len
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uint col
void IMB_freeImBuf(ImBuf *)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ListBase R_engines
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
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)
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)
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(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)
#define INT32_MAX
Definition stdint.h:137
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
#define INT32_MIN
Definition stdint.h:136
unsigned char uint8_t
Definition stdint.h:78
unsigned __int64 uint64_t
Definition stdint.h:90
struct BMLoop * next
LinkNode * resynced_lib_overrides_libraries
double lib_overrides_recursive_resync
int resynced_lib_overrides_libraries_count
struct BlendFileReadReport::@127 duration
struct BlendFileReadReport::@128 count
wmWindowManager * old_wm
eBLO_WritePathRemap remap_mode
FileReaderSeekFn seek
FileReaderCloseFn close
FileReaderReadFn read
bool is_untrusted
Definition wm_files.cc:3252
IDProperty * metadata
char filepath[1024]
Definition DNA_ID.h:531
void * first
ListBase scenes
Definition BKE_main.hh:210
ListBase wm
Definition BKE_main.hh:239
short subversionfile
Definition BKE_main.hh:137
bool is_asset_edit_file
Definition BKE_main.hh:151
bool has_forward_compatibility_issues
Definition BKE_main.hh:144
char filepath[1024]
Definition BKE_main.hh:136
bool recovered
Definition BKE_main.hh:158
BlendThumbnail * blen_thumb
Definition BKE_main.hh:207
ListBase libraries
Definition BKE_main.hh:211
ListBase screens
Definition BKE_main.hh:225
short versionfile
Definition BKE_main.hh:137
ListBase workspaces
Definition BKE_main.hh:246
int(* run)(bContext *C, wmOperator *op)
Definition wm_files.cc:3078
StructRNA * type
Definition RNA_types.hh:41
void * data
Definition RNA_types.hh:42
RecentFile * next
Definition WM_types.hh:1398
char * filepath
Definition WM_types.hh:1399
UserDef_Runtime runtime
View3D_Runtime runtime
struct View3D * localvd
View3DShading shading
Wrapper for bScreen.
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 * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
std::string(* get_description)(bContext *C, wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1074
const char * idname
Definition WM_types.hh:992
bool(* check)(bContext *C, wmOperator *op)
Definition WM_types.hh:1014
std::string(* get_name)(wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1068
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * translation_context
Definition WM_types.hh:994
const char * description
Definition WM_types.hh:996
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1053
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
IDProperty * properties
struct uiLayout * layout
struct wmOperatorType * type
struct PointerRNA * ptr
struct wmMsgBus * message_bus
struct wmKeyConfig * defaultconf
WindowManagerRuntimeHandle * runtime
struct UndoStack * undo_stack
struct wmWindow * winactive
struct wmKeyConfig * userconf
struct wmTimer * autosavetimer
struct wmWindow * windrawable
struct wmKeyConfig * addonconf
struct wmWindow * parent
struct wmEvent * eventstate
struct wmEvent * event_last_handled
ccl_device_inline int abs(int x)
Definition util/math.h:120
void wm_close_and_free(bContext *C, wmWindowManager *wm)
Definition wm.cc:565
void wm_clear_default_size(bContext *C)
Definition wm.cc:518
void WM_check(bContext *C)
Definition wm.cc:479
void WM_keyconfig_reload(bContext *C)
Definition wm.cc:430
void wm_add_default(Main *bmain, bContext *C)
Definition wm.cc:540
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:1271
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)
int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
int WM_operator_name_call_with_properties(bContext *C, const char *opstring, wmOperatorCallContext context, IDProperty *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)
int WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_remove_handlers(bContext *C, ListBase *handlers)
@ WM_HANDLER_TYPE_OP
@ TIMERAUTOSAVE
void WM_autosave_write(wmWindowManager *wm, Main *bmain)
Definition wm_files.cc:2311
static struct @1380 wm_test_autorun_revert_action_data
static void free_post_file_close_action(void *arg)
Definition wm_files.cc:4198
void WM_file_autosave_init(wmWindowManager *wm)
Definition wm_files.cc:2353
bool WM_autosave_is_scheduled(wmWindowManager *wm)
Definition wm_files.cc:2306
void wm_homefile_read(bContext *C, const wmHomeFileRead_Params *params_homefile, ReportList *reports)
Definition wm_files.cc:1567
void wm_autosave_timer_end(wmWindowManager *wm)
Definition wm_files.cc:2345
static void create_operator_state(wmOperatorType *ot, int first_state)
Definition wm_files.cc:3058
#define BKE_READ_EXOTIC_OK_BLEND
Definition wm_files.cc:551
static int wm_clear_recent_files_exec(bContext *, wmOperator *op)
Definition wm_files.cc:3910
bool wm_operator_close_file_dialog_if_needed(bContext *C, wmOperator *op, wmGenericCallbackFn post_action_fn)
Definition wm_files.cc:4763
static void wm_init_userdef(Main *bmain)
Definition wm_files.cc:506
static int wm_history_file_read_exec(bContext *, wmOperator *)
Definition wm_files.cc:2772
static std::string wm_save_mainfile_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition wm_files.cc:3839
static bool wm_save_mainfile_check(bContext *, wmOperator *op)
Definition wm_files.cc:3728
@ OPEN_MAINFILE_STATE_OPEN
Definition wm_files.cc:3103
@ OPEN_MAINFILE_STATE_DISCARD_CHANGES
Definition wm_files.cc:3101
@ OPEN_MAINFILE_STATE_SELECT_FILE_PATH
Definition wm_files.cc:3102
static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3427
static uiBlock * block_create__close_file_dialog(bContext *C, ARegion *region, void *arg1)
Definition wm_files.cc:4583
static BlendFileReadWMSetupData * wm_file_read_setup_wm_init(bContext *C, Main *bmain, const bool is_read_homefile)
Definition wm_files.cc:205
static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3201
static int operator_state_dispatch(bContext *C, wmOperator *op, OperatorDispatchTarget *targets)
Definition wm_files.cc:3081
bool WM_file_recover_last_session(bContext *C, ReportList *reports)
Definition wm_files.cc:3417
void WM_OT_read_history(wmOperatorType *ot)
Definition wm_files.cc:2779
static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2914
static void wm_open_mainfile_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:3108
void wm_open_init_use_scripts(wmOperator *op, bool use_prefs)
Definition wm_files.cc:2444
static bool wm_file_write_check_with_report_on_failure(Main *bmain, const char *filepath, ReportList *reports)
Definition wm_files.cc:2072
static int wm_homefile_read_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2800
static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op)
Definition wm_files.cc:3114
#define BKE_READ_EXOTIC_FAIL_FORMAT
Definition wm_files.cc:549
void wm_test_autorun_revert_action_set(wmOperatorType *ot, PointerRNA *ptr)
Definition wm_files.cc:4129
#define BKE_READ_EXOTIC_FAIL_OPEN
Definition wm_files.cc:550
ClearRecentInclude
Definition wm_files.cc:3896
@ CLEAR_RECENT_MISSING
Definition wm_files.cc:3896
@ CLEAR_RECENT_ALL
Definition wm_files.cc:3896
static uiBlock * block_create_autorun_warning(bContext *C, ARegion *region, void *)
Definition wm_files.cc:4009
bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWindowManager *wm)
Definition wm_files.cc:181
static ImBuf * blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **r_thumb)
Definition wm_files.cc:1857
void WM_OT_save_as_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3762
void WM_OT_open_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3317
static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3799
void WM_file_tag_modified()
Definition wm_files.cc:171
static void wm_block_file_close_cancel_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4523
static RecentFile * wm_file_history_find(const char *filepath)
Definition wm_files.cc:1647
const char * WM_init_state_app_template_get()
Definition wm_files.cc:1182
PointerRNA * ptr
Definition wm_files.cc:4126
void wm_file_read_report(Main *bmain, wmWindow *win)
Definition wm_files.cc:650
void wm_autosave_timer_begin(wmWindowManager *wm)
Definition wm_files.cc:2340
static void rna_struct_update_when_changed(bContext *C, Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b)
Definition wm_files.cc:2622
static int wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3450
void WM_OT_recover_last_session(wmOperatorType *ot)
Definition wm_files.cc:3464
static void wm_file_read_pre(bool use_data, bool)
Definition wm_files.cc:682
static blender::int2 blend_file_thumb_clamp_size(const int size[2], const int limit)
Definition wm_files.cc:1840
static int wm_revert_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3373
static void wm_block_autorun_warning_enable_scripts(bContext *C, uiBlock *block)
Definition wm_files.cc:3990
static void save_file_overwrite_saveas(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4316
static void wm_history_files_free()
Definition wm_files.cc:1640
static int wm_read_factory_settings_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2984
static bool wm_file_read_opwrap(bContext *C, const char *filepath, ReportList *reports)
Definition wm_files.cc:3040
static RecentFile * wm_history_file_new(const char *filepath)
Definition wm_files.cc:1626
static bool wm_revert_mainfile_poll(bContext *)
Definition wm_files.cc:3392
static void wm_read_callback_pre_wrapper(bContext *C, const char *filepath)
Definition wm_files.cc:880
void wm_history_file_read()
Definition wm_files.cc:1594
void wm_autosave_delete()
Definition wm_files.cc:2384
static bool wm_open_mainfile_check(bContext *, wmOperator *op)
Definition wm_files.cc:3256
static ImBuf * blend_file_thumb_from_camera(const bContext *C, Scene *scene, bScreen *screen, BlendThumbnail **r_thumb)
Definition wm_files.cc:1929
static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:2908
static OperatorDispatchTarget wm_open_mainfile_dispatch_targets[]
Definition wm_files.cc:3189
#define USERDEF_RESTORE(member)
static void wm_userpref_read_exceptions(UserDef *userdef_curr, const UserDef *userdef_prev)
Definition wm_files.cc:2608
void WM_OT_read_userpref(wmOperatorType *ot)
Definition wm_files.cc:2716
static void wm_test_autorun_revert_action_exec(bContext *C)
Definition wm_files.cc:4142
static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data)
Definition wm_files.cc:3444
static char save_images_when_file_is_closed
Definition wm_files.cc:4447
static void save_file_overwrite_cancel_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4265
static void wm_block_file_close_save_button(uiBlock *block, wmGenericCallback *post_action, const bool needs_overwrite_confirm)
Definition wm_files.cc:4550
void WM_OT_save_homefile(wmOperatorType *ot)
Definition wm_files.cc:2560
void WM_OT_read_factory_userpref(wmOperatorType *ot)
Definition wm_files.cc:2752
static void wm_file_read_setup_wm_finalize(bContext *C, Main *bmain, BlendFileReadWMSetupData *wm_setup_data)
Definition wm_files.cc:426
static int wm_userpref_write_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2577
static int get_operator_state(wmOperator *op)
Definition wm_files.cc:3066
void WM_init_state_app_template_set(const char *app_template)
Definition wm_files.cc:1170
static void save_set_compress(wmOperator *op)
Definition wm_files.cc:3561
bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
Definition wm_files.cc:1044
static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3628
void WM_OT_save_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3851
void WM_OT_recover_auto_save(wmOperatorType *ot)
Definition wm_files.cc:3524
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:1193
static std::string wm_save_as_mainfile_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition wm_files.cc:3751
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:314
static struct @1378 wm_init_state_app_template
char app_template[64]
Definition wm_files.cc:1166
static void wm_block_file_close_discard(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4455
static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3611
void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *)
Definition wm_files.cc:2358
static void wm_history_file_write()
Definition wm_files.cc:1657
static void set_next_operator_state(wmOperator *op, int state)
Definition wm_files.cc:3071
void WM_file_autoexec_init(const char *filepath)
Definition wm_files.cc:635
static int wm_open_mainfile_dispatch(bContext *C, wmOperator *op)
Definition wm_files.cc:3196
static int wm_revert_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3356
void wm_homefile_read_post(bContext *C, const wmFileReadPost_Params *params_file_read_post)
Definition wm_files.cc:1574
static int wm_userpref_read_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2726
static void wm_file_read_post(bContext *C, const char *filepath, const wmFileReadPost_Params *params)
Definition wm_files.cc:716
static void wm_userpref_update_when_changed(bContext *C, Main *bmain, UserDef *userdef_prev, UserDef *userdef_curr)
Definition wm_files.cc:2656
static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:3512
static const char * close_file_dialog_name
Definition wm_files.cc:4574
static void wm_autosave_location(char filepath[FILE_MAX])
Definition wm_files.cc:2261
static std::string wm_open_mainfile_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition wm_files.cc:3211
static void save_catalogs_when_file_is_closed_set_fn(bContext *, void *arg1, void *)
Definition wm_files.cc:4576
static void wm_filepath_default(const Main *bmain, char *filepath)
Definition wm_files.cc:3552
void WM_OT_read_factory_settings(wmOperatorType *ot)
Definition wm_files.cc:3014
static int wm_userpref_read_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2673
static int wm_open_mainfile__open(bContext *C, wmOperator *op)
Definition wm_files.cc:3160
static bool wm_autosave_write_try(Main *bmain, wmWindowManager *wm)
Definition wm_files.cc:2284
static int wm_read_exotic(const char *filepath)
Definition wm_files.cc:566
static void wm_block_autorun_warning_reload_with_scripts(bContext *C, uiBlock *block)
Definition wm_files.cc:3974
static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bmain)
Definition wm_files.cc:4212
void wm_test_autorun_warning(bContext *C)
Definition wm_files.cc:4162
static void wm_block_file_close_cancel(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4449
wmOperatorType * ot
Definition wm_files.cc:4125
static void wm_file_read_setup_wm_substitute_old_window(wmWindowManager *oldwm, wmWindowManager *wm, wmWindow *oldwin, wmWindow *win)
Definition wm_files.cc:268
void wm_open_init_load_ui(wmOperator *op, bool use_prefs)
Definition wm_files.cc:2434
static void read_factory_reset_props(wmOperatorType *ot)
Definition wm_files.cc:2411
static CLG_LogRef LOG
Definition wm_files.cc:154
static void wm_read_callback_post_wrapper(bContext *C, const char *filepath, const bool success)
Definition wm_files.cc:887
static void wm_clear_recent_files_ui(bContext *, wmOperator *op)
Definition wm_files.cc:3930
static void wm_block_autorun_warning_ignore(bContext *C, void *arg_block, void *)
Definition wm_files.cc:3965
static void wm_history_file_free(RecentFile *recent)
Definition wm_files.cc:1633
static void wm_autosave_timer_begin_ex(wmWindowManager *wm, double timestep)
Definition wm_files.cc:2331
static const EnumPropertyItem prop_clear_recent_types[]
Definition wm_files.cc:3898
static void save_file_overwrite_confirm_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4296
static void wm_open_mainfile_def_property_use_scripts(wmOperatorType *ot)
Definition wm_files.cc:3307
#define BKE_READ_EXOTIC_FAIL_PATH
Definition wm_files.cc:548
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:2107
static void file_read_reports_finalize(BlendFileReadReport *bf_reports)
Definition wm_files.cc:916
static void wm_free_operator_properties_callback(void *user_data)
Definition wm_files.cc:4204
static int wm_clear_recent_files_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition wm_files.cc:3904
static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4469
static void save_set_filepath(bContext *C, wmOperator *op)
Definition wm_files.cc:3577
static int wm_recover_auto_save_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3482
static uiBlock * block_create_save_file_overwrite_dialog(bContext *C, ARegion *region, void *arg1)
Definition wm_files.cc:4344
static void wm_file_read_setup_wm_use_new(bContext *C, Main *, BlendFileReadWMSetupData *wm_setup_data, wmWindowManager *wm)
Definition wm_files.cc:365
void WM_OT_read_homefile(wmOperatorType *ot)
Definition wm_files.cc:2940
static std::string wm_save_as_mainfile_get_name(wmOperatorType *ot, PointerRNA *ptr)
Definition wm_files.cc:3743
static int wm_open_mainfile_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:3206
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:1748
static void wm_block_file_close_discard_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4531
static void wm_open_mainfile_ui(bContext *, wmOperator *op)
Definition wm_files.cc:3286
static void wm_history_file_update()
Definition wm_files.cc:1683
static int wm_homefile_write_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_files.cc:2534
static int wm_homefile_write_exec(bContext *C, wmOperator *op)
Definition wm_files.cc:2468
bool write_crash_blend()
Definition wm_files.cc:2056
static void wm_gpu_backend_override_from_userdef()
Definition wm_files.cc:492
void WM_OT_save_userpref(wmOperatorType *ot)
Definition wm_files.cc:2589
static void save_file_overwrite_confirm(bContext *C, void *arg_block, void *arg_data)
Definition wm_files.cc:4273
void WM_OT_clear_recent_files(wmOperatorType *ot)
Definition wm_files.cc:3941
static void read_homefile_props(wmOperatorType *ot)
Definition wm_files.cc:2922
void wm_save_file_overwrite_dialog(bContext *C, wmOperator *op)
Definition wm_files.cc:4428
static const char * save_file_overwrite_dialog_name
Definition wm_files.cc:4210
static void save_file_overwrite_cancel(bContext *C, void *arg_block, void *)
Definition wm_files.cc:4259
static int wm_open_mainfile__select_file_path(bContext *C, wmOperator *op)
Definition wm_files.cc:3129
static void save_file_overwrite_saveas_button(uiBlock *block, wmGenericCallback *post_action)
Definition wm_files.cc:4324
void WM_OT_revert_mainfile(wmOperatorType *ot)
Definition wm_files.cc:3398
void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action)
Definition wm_files.cc:4752
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:576
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()
int WM_operator_confirm(bContext *C, wmOperator *op, const wmEvent *)
int WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)
int 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)
void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
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:429
void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen)
void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm)
Definition wm_window.cc:905
void wm_window_clear_drawable(wmWindowManager *wm)
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)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const int event_type, const double time_step)
bScreen * WM_window_get_active_screen(const wmWindow *win)