From c5180e0988f73f6fd92bf1ac5967bb44296cf944 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Sat, 6 Jul 2024 21:42:49 +0200 Subject: [PATCH] Assets: Special .asset.blend files, for storing externally editable assets Introduces a new kind of blend file that store assets that can savely be edited from the Blender UI, without having to open the blend-file storing the asset itself (asset "pushing" workflow). Only brush assets will use this for now. Technical Documentation: https://developer.blender.org/docs/features/asset_system/asset_editing/ User Documentation: https://docs.blender.org/manual/en/latest/files/asset_libraries/introduction.html#asset-system-files-asset-blend-extention The API to manage assets by generating these files is added in the following commit. Main authors: Bastien Montagne, Brecht Van Lommel, Julian Eisel Pull Request for the latest design iteration: https://projects.blender.org/blender/blender/pulls/124246 Part of the brush assets project, see: - https://projects.blender.org/blender/blender/issues/116337 - https://projects.blender.org/blender/blender/pulls/106303 --- source/blender/blenkernel/BKE_blendfile.hh | 5 + source/blender/blenkernel/BKE_global.hh | 6 + source/blender/blenkernel/BKE_main.hh | 18 +++ source/blender/blenkernel/intern/blendfile.cc | 12 ++ source/blender/blenkernel/intern/main.cc | 10 ++ source/blender/blenloader/intern/readfile.cc | 7 + source/blender/blenloader/intern/writefile.cc | 8 + .../templates/interface_templates.cc | 148 +++++++++++------- source/blender/makesdna/DNA_ID.h | 5 + .../blender/windowmanager/intern/wm_files.cc | 112 +++++++++---- 10 files changed, 245 insertions(+), 86 deletions(-) diff --git a/source/blender/blenkernel/BKE_blendfile.hh b/source/blender/blenkernel/BKE_blendfile.hh index 7e4d407e90f..7bb06f0cad4 100644 --- a/source/blender/blenkernel/BKE_blendfile.hh +++ b/source/blender/blenkernel/BKE_blendfile.hh @@ -29,6 +29,11 @@ struct ReportList; struct UserDef; struct WorkspaceConfigFileData; +/** + * The suffix used for blendfiles managed by the asset system. + */ +#define BLENDER_ASSET_FILE_SUFFIX ".asset.blend" + /** * Check whether given path ends with a blend file compatible extension * (`.blend`, `.ble` or `.blend.gz`). diff --git a/source/blender/blenkernel/BKE_global.hh b/source/blender/blenkernel/BKE_global.hh index b4e74a4095d..5afc5fd0ccf 100644 --- a/source/blender/blenkernel/BKE_global.hh +++ b/source/blender/blenkernel/BKE_global.hh @@ -294,6 +294,12 @@ enum { /** BMesh option to save as older mesh format */ /* #define G_FILE_MESH_COMPAT (1 << 26) */ /* #define G_FILE_GLSL_NO_ENV_LIGHTING (1 << 28) */ /* deprecated */ + /** + * This file contains a single asset and its dependencies. Users may edit the asset through the + * UI, at which point the file will be regenerated by the asset system (API in + * #BKE_asset_edit.hh). Stored with a .asset.blend prefix. + */ + G_FILE_ASSET_EDIT_FILE = (1 << 29), }; /** diff --git a/source/blender/blenkernel/BKE_main.hh b/source/blender/blenkernel/BKE_main.hh index 0399a728c49..ec138308d73 100644 --- a/source/blender/blenkernel/BKE_main.hh +++ b/source/blender/blenkernel/BKE_main.hh @@ -143,6 +143,13 @@ struct Main { * could try to use more refined detection on load. */ bool has_forward_compatibility_issues; + /** + * This file was written by the asset system with the #G_FILE_ASSET_EDIT_FILE flag (now cleared). + * It must not be overwritten, except by the asset system itself. Otherwise the file could end up + * with user created data that would be lost when the asset system regenerates the file. + */ + bool is_asset_edit_file; + /** Commit timestamp from `buildinfo`. */ uint64_t build_commit_timestamp; /** Commit Hash from `buildinfo`. */ @@ -344,6 +351,17 @@ void BKE_main_merge(Main *bmain_dst, Main **r_bmain_src, MainMergeReport &report */ bool BKE_main_is_empty(Main *bmain); +/** + * Check whether the bmain has issues, e.g. for reporting in the status bar. + */ +bool BKE_main_has_issues(const Main *bmain); + +/** + * Check whether user confirmation should be required when overwriting this `bmain` into its source + * blendfile. + */ +bool BKE_main_needs_overwrite_confirm(const Main *bmain); + void BKE_main_lock(Main *bmain); void BKE_main_unlock(Main *bmain); diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index 16008627107..0e48c515053 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -874,6 +874,18 @@ static void setup_app_data(bContext *C, BLI_assert(bfd->curscene != nullptr); mode = LOAD_UNDO; } + else if (bfd->fileflags & G_FILE_ASSET_EDIT_FILE) { + BKE_report(reports->reports, + RPT_WARNING, + "This file is managed by the asset system, you cannot overwrite it (using \"Save " + "As\" is possible)"); + /* From now on the file in memory is a normal file, further saving it will contain a + * window-manager, scene, ... and potentially user created data. Use #Main.is_asset_edit_file + * to detect if saving this file needs extra protections. */ + bfd->fileflags &= ~G_FILE_ASSET_EDIT_FILE; + BLI_assert(bfd->main->is_asset_edit_file); + mode = LOAD_UI_OFF; + } /* May happen with library files, loading undo-data should never have a null `curscene` * (but may have a null `curscreen`). */ else if (ELEM(nullptr, bfd->curscreen, bfd->curscene)) { diff --git a/source/blender/blenkernel/intern/main.cc b/source/blender/blenkernel/intern/main.cc index a8052c150a1..2ec9084a5c5 100644 --- a/source/blender/blenkernel/intern/main.cc +++ b/source/blender/blenkernel/intern/main.cc @@ -466,6 +466,16 @@ bool BKE_main_is_empty(Main *bmain) return result; } +bool BKE_main_has_issues(const Main *bmain) +{ + return bmain->has_forward_compatibility_issues || bmain->is_asset_edit_file; +} + +bool BKE_main_needs_overwrite_confirm(const Main *bmain) +{ + return bmain->has_forward_compatibility_issues || bmain->is_asset_edit_file; +} + void BKE_main_lock(Main *bmain) { BLI_spin_lock((SpinLock *)bmain->lock); diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index 2664d5320f4..465777b7e7a 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -356,6 +356,8 @@ void blo_join_main(ListBase *mainlist) BKE_main_namemap_clear(mainl); while ((tojoin = mainl->next)) { + BLI_assert(((tojoin->curlib->runtime.tag & LIBRARY_IS_ASSET_EDIT_FILE) != 0) == + tojoin->is_asset_edit_file); add_main_to_main(mainl, tojoin); BLI_remlink(mainlist, tojoin); tojoin->next = tojoin->prev = nullptr; @@ -418,6 +420,7 @@ void blo_split_main(ListBase *mainlist, Main *main) libmain->subversionfile = lib->runtime.subversionfile; libmain->has_forward_compatibility_issues = !MAIN_VERSION_FILE_OLDER_OR_EQUAL( libmain, BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION); + libmain->is_asset_edit_file = (lib->runtime.tag & LIBRARY_IS_ASSET_EDIT_FILE) != 0; BLI_addtail(mainlist, libmain); lib->runtime.temp_index = i; lib_main_array[i] = libmain; @@ -448,6 +451,7 @@ static void read_file_version(FileData *fd, Main *main) main->subversionfile = fg->subversion; main->minversionfile = fg->minversion; main->minsubversionfile = fg->minsubversion; + main->is_asset_edit_file = (fg->fileflags & G_FILE_ASSET_EDIT_FILE) != 0; MEM_freeN(fg); } else if (bhead->code == BLO_CODE_ENDB) { @@ -458,6 +462,8 @@ static void read_file_version(FileData *fd, Main *main) if (main->curlib) { main->curlib->runtime.versionfile = main->versionfile; main->curlib->runtime.subversionfile = main->subversionfile; + SET_FLAG_FROM_TEST( + main->curlib->runtime.tag, main->is_asset_edit_file, LIBRARY_IS_ASSET_EDIT_FILE); } } @@ -2957,6 +2963,7 @@ static BHead *read_global(BlendFileData *bfd, FileData *fd, BHead *bhead) bfd->main->build_commit_timestamp = fg->build_commit_timestamp; STRNCPY(bfd->main->build_hash, fg->build_hash); + bfd->main->is_asset_edit_file = (fg->fileflags & G_FILE_ASSET_EDIT_FILE) != 0; bfd->fileflags = fg->fileflags; bfd->globalf = fg->globalf; diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc index f6574eab330..5cc7504ac25 100644 --- a/source/blender/blenloader/intern/writefile.cc +++ b/source/blender/blenloader/intern/writefile.cc @@ -1528,6 +1528,14 @@ static bool BLO_write_file_impl(Main *mainvar, const BlendThumbnail *thumb = params->thumb; const bool relbase_valid = (mainvar->filepath[0] != '\0'); + /* Extra protection: Never save a non asset file as asset file. Otherwise a normal file is turned + * into an asset file, which can result in data loss because the asset system will allow editing + * this file from the UI, regenerating its content with just the asset and it dependencies. */ + if ((write_flags & G_FILE_ASSET_EDIT_FILE) && !mainvar->is_asset_edit_file) { + BKE_reportf(reports, RPT_ERROR, "Cannot save normal file (%s) as asset system file", tempname); + return false; + } + /* Path backup/restore. */ void *path_list_backup = nullptr; const eBPathForeachFlag path_list_flag = (BKE_BPATH_FOREACH_PATH_SKIP_LINKED | diff --git a/source/blender/editors/interface/templates/interface_templates.cc b/source/blender/editors/interface/templates/interface_templates.cc index f4b533574c6..29800f65fb4 100644 --- a/source/blender/editors/interface/templates/interface_templates.cc +++ b/source/blender/editors/interface/templates/interface_templates.cc @@ -6416,6 +6416,29 @@ void uiTemplateInputStatus(uiLayout *layout, bContext *C) } } +static std::string ui_template_status_tooltip(bContext *C, void * /*argN*/, const char * /*tip*/) +{ + Main *bmain = CTX_data_main(C); + std::string tooltip_message = ""; + + if (bmain->has_forward_compatibility_issues) { + char writer_ver_str[12]; + BKE_blender_version_blendfile_string_from_values( + writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1); + tooltip_message += fmt::format(RPT_("File saved by newer Blender\n({}), expect loss of data"), + writer_ver_str); + } + if (bmain->is_asset_edit_file) { + if (!tooltip_message.empty()) { + tooltip_message += "\n\n"; + } + tooltip_message += RPT_( + "This file is managed by the Blender asset system and cannot be overridden"); + } + + return tooltip_message; +} + void uiTemplateStatusInfo(uiLayout *layout, bContext *C) { Main *bmain = CTX_data_main(C); @@ -6488,7 +6511,7 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) } } - if (!bmain->has_forward_compatibility_issues) { + if (!BKE_main_has_issues(bmain)) { if (U.statusbar_flag & STATUSBAR_SHOW_VERSION) { if (has_status_info) { uiItemS_ex(row, -0.5f); @@ -6502,12 +6525,21 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) return; } + blender::StringRefNull version_string = ED_info_statusbar_string_ex( + bmain, scene, view_layer, STATUSBAR_SHOW_VERSION); + blender::StringRefNull warning_message; + /* Blender version part is shown as warning area when there are forward compatibility issues with * currently loaded .blend file. */ - - status_info_txt = ED_info_statusbar_string_ex(bmain, scene, view_layer, STATUSBAR_SHOW_VERSION); - - uiBut *but; + if (bmain->has_forward_compatibility_issues) { + warning_message = version_string; + } + else { + /* For other issues, still show the version if enabled. */ + if (U.statusbar_flag & STATUSBAR_SHOW_VERSION) { + uiItemL(layout, version_string.c_str(), ICON_NONE); + } + } const uiStyle *style = UI_style_get(); uiLayout *ui_abs = uiLayoutAbsolute(layout, false); @@ -6515,57 +6547,53 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) eUIEmbossType previous_emboss = UI_block_emboss_get(block); UI_fontstyle_set(&style->widgetlabel); - int width = int( - BLF_width(style->widgetlabel.uifont_id, status_info_txt, strlen(status_info_txt))); - width = max_ii(width, int(10 * UI_SCALE_FAC)); + const int width = max_ii(int(BLF_width(style->widgetlabel.uifont_id, + warning_message.c_str(), + warning_message.size())), + int(10 * UI_SCALE_FAC)); UI_block_align_begin(block); /* Background for icon. */ - but = uiDefBut(block, - UI_BTYPE_ROUNDBOX, - 0, - "", - 0, - 0, - UI_UNIT_X + (6 * UI_SCALE_FAC), - UI_UNIT_Y, - nullptr, - 0.0f, - 0.0f, - ""); + uiBut *but = uiDefBut(block, + UI_BTYPE_ROUNDBOX, + 0, + "", + 0, + 0, + UI_UNIT_X + (6 * UI_SCALE_FAC), + UI_UNIT_Y, + nullptr, + 0.0f, + 0.0f, + ""); /* UI_BTYPE_ROUNDBOX's bg color is set in but->col. */ UI_GetThemeColorType4ubv(TH_INFO_WARNING, SPACE_INFO, but->col); - /* Background for the rest of the message. */ - but = uiDefBut(block, - UI_BTYPE_ROUNDBOX, - 0, - "", - UI_UNIT_X + (6 * UI_SCALE_FAC), - 0, - UI_UNIT_X + width, - UI_UNIT_Y, - nullptr, - 0.0f, - 0.0f, - ""); + if (!warning_message.is_empty()) { + /* Background for the rest of the message. */ + but = uiDefBut(block, + UI_BTYPE_ROUNDBOX, + 0, + "", + UI_UNIT_X + (6 * UI_SCALE_FAC), + 0, + UI_UNIT_X + width, + UI_UNIT_Y, + nullptr, + 0.0f, + 0.0f, + ""); - /* Use icon background at low opacity to highlight, but still contrasting with area TH_TEXT. */ - UI_GetThemeColorType4ubv(TH_INFO_WARNING, SPACE_INFO, but->col); - but->col[3] = 64; + /* Use icon background at low opacity to highlight, but still contrasting with area TH_TEXT. */ + UI_GetThemeColorType4ubv(TH_INFO_WARNING, SPACE_INFO, but->col); + but->col[3] = 64; + } UI_block_align_end(block); UI_block_emboss_set(block, UI_EMBOSS_NONE); - /* The report icon itself. */ - static char compat_error_msg[256]; - char writer_ver_str[12]; - BKE_blender_version_blendfile_string_from_values( - writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1); - SNPRINTF(compat_error_msg, - RPT_("File saved by newer Blender\n(%s), expect loss of data"), - writer_ver_str); + /* The warning icon itself. */ but = uiDefIconBut(block, UI_BTYPE_BUT, 0, @@ -6577,23 +6605,27 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C) nullptr, 0.0f, 0.0f, - compat_error_msg); + nullptr); + UI_but_func_tooltip_set(but, ui_template_status_tooltip, nullptr, nullptr); UI_GetThemeColorType4ubv(TH_INFO_WARNING_TEXT, SPACE_INFO, but->col); but->col[3] = 255; /* This theme color is RBG only, so have to set alpha here. */ - /* The report message. */ - but = uiDefBut(block, - UI_BTYPE_BUT, - 0, - status_info_txt, - UI_UNIT_X, - 0, - short(width + UI_UNIT_X), - UI_UNIT_Y, - nullptr, - 0.0f, - 0.0f, - compat_error_msg); + /* The warning message, if any. */ + if (!warning_message.is_empty()) { + but = uiDefBut(block, + UI_BTYPE_BUT, + 0, + warning_message.c_str(), + UI_UNIT_X, + 0, + short(width + UI_UNIT_X), + UI_UNIT_Y, + nullptr, + 0.0f, + 0.0f, + nullptr); + UI_but_func_tooltip_set(but, ui_template_status_tooltip, nullptr, nullptr); + } UI_block_emboss_set(block, previous_emboss); } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 6262fe0ef77..801ace876fc 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -550,6 +550,11 @@ enum eLibrary_Tag { LIBRARY_ASSET_EDITABLE = 1 << 1, /** The blend file of this library is writable for asset editing. */ LIBRARY_ASSET_FILE_WRITABLE = 1 << 2, + /** + * The blend file of this library has the #G_FILE_ASSET_EDIT_FILE flag set (refer to it for more + * info). + */ + LIBRARY_IS_ASSET_EDIT_FILE = 1 << 3, }; /** diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 1011f14863a..b53ba7b43bb 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -1923,6 +1923,13 @@ static bool wm_file_write_check_with_report_on_failure(Main *bmain, return false; } + if (bmain->is_asset_edit_file && + blender::StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) + { + BKE_report(reports, RPT_ERROR, "Cannot overwrite files that are managed by the asset system"); + return false; + } + LISTBASE_FOREACH (Library *, li, &bmain->libraries) { if (BLI_path_cmp(li->runtime.filepath_abs, filepath) == 0) { BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath); @@ -3424,6 +3431,16 @@ static void save_set_filepath(bContext *C, wmOperator *op) STRNCPY(filepath, blendfile_path); } + /* For convencience when using "Save As" on asset system files: Replace .asset.blend extension + * with just .blend. Asset system files must not be overridden (except by the asset system), + * there are further checks to prevent this entirely. */ + if (bmain->is_asset_edit_file && + blender::StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) + { + filepath[strlen(filepath) - strlen(BLENDER_ASSET_FILE_SUFFIX)] = '\0'; + BLI_path_extension_ensure(filepath, FILE_MAX, ".blend"); + } + wm_filepath_default(bmain, filepath); RNA_property_string_set(op->ptr, prop, filepath); } @@ -3639,7 +3656,7 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent * } if (blendfile_path[0] != '\0') { - if (CTX_data_main(C)->has_forward_compatibility_issues) { + if (BKE_main_needs_overwrite_confirm(CTX_data_main(C))) { wm_save_file_overwrite_dialog(C, op); ret = OPERATOR_INTERFACE; } @@ -4001,31 +4018,44 @@ static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bma * block. */ uiLayoutSetScaleY(layout, 0.70f); - char writer_ver_str[16]; - char current_ver_str[16]; - if (bmain->versionfile == BLENDER_VERSION) { - BKE_blender_version_blendfile_string_from_values( - writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile); - BKE_blender_version_blendfile_string_from_values( - current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION); - } - else { - BKE_blender_version_blendfile_string_from_values( - writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1); - BKE_blender_version_blendfile_string_from_values( - current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1); + if (bmain->has_forward_compatibility_issues) { + char writer_ver_str[16]; + char current_ver_str[16]; + if (bmain->versionfile == BLENDER_VERSION) { + BKE_blender_version_blendfile_string_from_values( + writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile); + BKE_blender_version_blendfile_string_from_values( + current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION); + } + else { + BKE_blender_version_blendfile_string_from_values( + writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1); + BKE_blender_version_blendfile_string_from_values( + current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1); + } + + char message_line1[256]; + char message_line2[256]; + SNPRINTF(message_line1, + RPT_("This file was saved by a newer version of Blender (%s)"), + writer_ver_str); + SNPRINTF(message_line2, + RPT_("Saving it with this Blender (%s) may cause loss of data"), + current_ver_str); + uiItemL(layout, message_line1, ICON_NONE); + uiItemL(layout, message_line2, ICON_NONE); } - char message_line1[256]; - char message_line2[256]; - SNPRINTF(message_line1, - RPT_("This file was saved by a newer version of Blender (%s)"), - writer_ver_str); - SNPRINTF(message_line2, - RPT_("Saving it with this Blender (%s) may cause loss of data"), - current_ver_str); - uiItemL(layout, message_line1, ICON_NONE); - uiItemL(layout, message_line2, ICON_NONE); + if (bmain->is_asset_edit_file) { + if (bmain->has_forward_compatibility_issues) { + uiItemS_ex(layout, 1.4f); + } + + uiItemL(layout, + RPT_("This file is managed by the Blender asset system. It can only be"), + ICON_NONE); + uiItemL(layout, RPT_("saved as a new, regular file."), ICON_NONE); + } } static void save_file_overwrite_cancel(bContext *C, void *arg_block, void * /*arg_data*/) @@ -4126,8 +4156,30 @@ static uiBlock *block_create_save_file_overwrite_dialog(bContext *C, ARegion *re uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_WARNING); /* Title. */ - uiItemL_ex( - layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false); + if (bmain->has_forward_compatibility_issues) { + if (bmain->is_asset_edit_file) { + uiItemL_ex(layout, + RPT_("Cannot overwrite asset system files. Save as new file"), + ICON_NONE, + true, + false); + uiItemL_ex(layout, RPT_("with an older Blender version?"), ICON_NONE, true, false); + } + else { + uiItemL_ex( + layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false); + } + } + else if (bmain->is_asset_edit_file) { + uiItemL_ex(layout, + RPT_("Cannot overwrite asset system files. Save as new file?"), + ICON_NONE, + true, + false); + } + else { + BLI_assert_unreachable(); + } /* Filename. */ const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C)); @@ -4154,7 +4206,11 @@ static uiBlock *block_create_save_file_overwrite_dialog(bContext *C, ARegion *re uiLayoutSetScaleY(split, 1.2f); uiLayoutColumn(split, false); - save_file_overwrite_confirm_button(block, post_action); + /* Asset files don't actually allow overriding. */ + const bool allow_overwrite = !bmain->is_asset_edit_file; + if (allow_overwrite) { + save_file_overwrite_confirm_button(block, post_action); + } uiLayout *split_right = uiLayoutSplit(split, 0.1f, true); @@ -4339,7 +4395,7 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION); - const bool needs_overwrite_confirm = bmain->has_forward_compatibility_issues; + const bool needs_overwrite_confirm = BKE_main_needs_overwrite_confirm(bmain); /* Title. */ uiItemL_ex(layout, RPT_("Save changes before closing?"), ICON_NONE, true, false);