From 188d66527c6cd8056df6ffa2d9d9dce3b970c0ea Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Sun, 23 Jul 2023 20:29:29 +0200 Subject: [PATCH 1/5] BKE version: Add utils to get string out of numbers for blendfile version. Since in rare cases there may be needs to expose file subversion here, the following format was defined: . (sub ) Part of #109151 (PR !110109). --- .../blender/blenkernel/BKE_blender_version.h | 15 ++++++++++++++ source/blender/blenkernel/intern/blender.c | 20 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 04173f417b8..62741300e20 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once +#include "BLI_utildefines.h" + #ifdef __cplusplus extern "C" { #endif @@ -39,6 +41,19 @@ const char *BKE_blender_version_string(void); /* Returns true when version cycle is alpha, otherwise (beta, rc) returns false. */ bool BKE_blender_version_is_alpha(void); +/** Fill in given string buffer with user-readable formated file version and subversion (if + * provided). + * + * \param str_buff a char buffer where the formated string is written, minimal recommended size is + * 8, or 16 if subversion is provided. + * + * \param file_subversion the file subversion, if given value < 0, it is ignored, and only the + * `file_version` is used. */ +void BKE_blender_version_blendfile_string_from_values(char *str_buff, + const size_t str_buff_len, + const short file_version, + const short file_subversion); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index d1b5bf980ad..b84bb9f597d 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -124,6 +124,26 @@ const char *BKE_blender_version_string(void) return blender_version_string; } +void BKE_blender_version_blendfile_string_from_values(char *str_buff, + const size_t str_buff_len, + const short file_version, + const short file_subversion) +{ + const short file_version_major = file_version / 100; + const short file_version_minor = file_version % 100; + if (file_subversion >= 0) { + BLI_snprintf(str_buff, + str_buff_len, + "%d.%d (sub %d)", + file_version_major, + file_version_minor, + file_subversion); + } + else { + BLI_snprintf(str_buff, str_buff_len, "%d.%d", file_version_major, file_version_minor); + } +} + bool BKE_blender_version_is_alpha(void) { bool is_alpha = STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha"); -- 2.30.2 From d1ba2caf38e1a77c6cc46e62ec6f2137263ff57e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 24 Jul 2023 11:21:20 +0200 Subject: [PATCH 2/5] Reduce file minversion to 3.3 (sub 6) for Blender 3.6. Required to make 3.6 files readable by 3.3 once the compatibility changes are backported to the LTS releases. Part of #109151 (PR !110109). --- source/blender/blenkernel/BKE_blender_version.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 62741300e20..3ae0c29f66e 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -30,10 +30,12 @@ extern "C" { #define BLENDER_FILE_SUBVERSION 11 /* Minimum Blender version that supports reading file written with the current - * version. Older Blender versions will test this and show a warning if the file - * was written with too new a version. */ -#define BLENDER_FILE_MIN_VERSION 305 -#define BLENDER_FILE_MIN_SUBVERSION 9 + * version. Older Blender versions will test this and cancel loading the file, showing a warning to + * the user. + * + * See https://wiki.blender.org/wiki/Process/Compatibility_Handling for details. */ +#define BLENDER_FILE_MIN_VERSION 303 +#define BLENDER_FILE_MIN_SUBVERSION 06 /** User readable version string. */ const char *BKE_blender_version_string(void); -- 2.30.2 From 44dc3c25713011e551856297cbdb3ebb5b5fcf94 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 24 Jul 2023 14:18:22 +0200 Subject: [PATCH 3/5] Core: Implement new blendfile compatibility handling. This implements the main aspects of changes to blendfile compatibility as designed in #109151: * Blender files which file minversion is newer than current Blender executable won't be loaded at all. * Blender files which file version is newer than current Blender will triger systematic warning to user: * In the status info bar (lower right corner in default UI). * When attempting to save (overwrite) them. This means that the file minversion becomes a hard limit, and not a soft, warning-only as it used to be. Further more, forward compatibility warning is now systematic (instead of depending on file minversion), and more visible for users. See also https://wiki.blender.org/wiki/Process/Compatibility_Handling for details over the new policy. Technically: * Opening any file with a minversion newer than current Blender file one now triggers an early abort, with an error message reported to the user. This is handled by a new utils called from `blo_decode_and_check`. * Any file newer than current Blender version sets a new `has_forward_compatibility_issues` flag in Main struct at read time. * Status bar info area is turned into a template, which uses this flag to display special warning UI and tooltip when set. * A new confirmation popup appears when user tries to save (overwrite) such a 'newer' blendfile, stating potential loos of data, and proposing by default to 'save as' instead. * The 'quit unsaved' popup has also been updated to 'save as' instead of 'save' when the edited file is has potential forward compitibility issues. NOTE: For 3.6LTS, an additional hot-fix is applied on read file minversion, to ensure that all files from early 4.0 develpment stages remain readable in Blender 3.6LTS after this commit. Part of #109151 (PR !110109). --- scripts/startup/bl_ui/space_statusbar.py | 2 +- source/blender/blenkernel/BKE_main.h | 11 + source/blender/blenloader/intern/readfile.cc | 81 ++++- source/blender/editors/include/ED_info.h | 4 + source/blender/editors/include/UI_interface.h | 1 + .../editors/interface/interface_templates.cc | 122 ++++++++ .../blender/editors/space_info/info_stats.cc | 12 +- source/blender/makesrna/intern/rna_ui_api.c | 3 + .../blender/windowmanager/intern/wm_files.cc | 277 ++++++++++++++++-- source/blender/windowmanager/wm_files.h | 7 + 10 files changed, 492 insertions(+), 28 deletions(-) diff --git a/scripts/startup/bl_ui/space_statusbar.py b/scripts/startup/bl_ui/space_statusbar.py index a281492aba8..bad53f8ae52 100644 --- a/scripts/startup/bl_ui/space_statusbar.py +++ b/scripts/startup/bl_ui/space_statusbar.py @@ -25,7 +25,7 @@ class STATUSBAR_HT_header(Header): row.alignment = 'RIGHT' # Stats & Info - row.label(text=context.screen.statusbar_info(), translate=False) + layout.template_status_info() classes = ( diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 8df2d6d534f..4b1e02db7b6 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -135,6 +135,13 @@ typedef struct Main { char filepath[1024]; /* 1024 = FILE_MAX */ short versionfile, subversionfile; /* see BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION */ short minversionfile, minsubversionfile; + /** The currently opened .blend file was written from a newer version of Blender, and has forward + * compatibility issues (data loss). + * + * \note: In practice currently this is only based on the version numbers, in the future it + * could try to use more refined detection on load. */ + bool has_forward_compatibility_issues; + /** Commit timestamp from `buildinfo`. */ uint64_t build_commit_timestamp; /** Commit Hash from `buildinfo`. */ @@ -451,6 +458,10 @@ int set_listbasepointers(struct Main *main, struct ListBase *lb[]); ((main)->versionfile < (ver) || \ ((main)->versionfile == (ver) && (main)->subversionfile < (subver))) +#define MAIN_VERSION_FILE_OLDER_OR_EQUAL(main, ver, subver) \ + ((main)->versionfile < (ver) || \ + ((main)->versionfile == (ver) && (main)->subversionfile <= (subver))) + /** * The size of thumbnails (optionally) stored in the `.blend` files header. * diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index 9682b09f62d..b1121fbfce4 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -64,6 +64,7 @@ #include "BKE_anim_data.h" #include "BKE_animsys.h" #include "BKE_asset.h" +#include "BKE_blender_version.h" #include "BKE_collection.h" #include "BKE_global.h" /* for G */ #include "BKE_idprop.h" @@ -409,6 +410,8 @@ void blo_split_main(ListBase *mainlist, Main *main) libmain->curlib = lib; libmain->versionfile = lib->versionfile; libmain->subversionfile = lib->subversionfile; + libmain->has_forward_compatibility_issues = !MAIN_VERSION_FILE_OLDER_OR_EQUAL( + libmain, BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION); BLI_addtail(mainlist, libmain); lib->temp_index = i; lib_main_array[i] = libmain; @@ -428,6 +431,22 @@ void blo_split_main(ListBase *mainlist, Main *main) MEM_freeN(lib_main_array); } +/* Hot-fix file minversion for a specific range of versions. + * + * This is needed for newer LTS releases of 3.6 to be able to properly open files from early + * development versions of 4.0. */ +static void file_global_minversion_hotfix(FileGlobal *fg) +{ + if ((fg->minversion > BLENDER_FILE_VERSION) || + (fg->minversion == BLENDER_FILE_VERSION && fg->minsubversion > BLENDER_FILE_SUBVERSION)) + { + if (fg->minversion < 400 || (fg->minversion == 400 && fg->minsubversion <= 11)) { + fg->minversion = BLENDER_FILE_VERSION; + fg->minsubversion = BLENDER_FILE_SUBVERSION; + } + } +} + static void read_file_version(FileData *fd, Main *main) { BHead *bhead; @@ -436,6 +455,8 @@ static void read_file_version(FileData *fd, Main *main) if (bhead->code == BLO_CODE_GLOB) { FileGlobal *fg = static_cast(read_struct(fd, bhead, "Global")); if (fg) { + file_global_minversion_hotfix(fg); + main->subversionfile = fg->subversion; main->minversionfile = fg->minversion; main->minsubversionfile = fg->minsubversion; @@ -1077,6 +1098,55 @@ static FileData *filedata_new(BlendFileReadReport *reports) return fd; } +/** Check if minversion of the file is older than current Blender, return false if it is not. + * Should only be called after #read_file_dna was successfuly executed. */ +static bool is_minversion_older_than_blender(FileData *fd, ReportList *reports) +{ + BLI_assert(fd->filesdna != nullptr); + for (BHead *bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code != BLO_CODE_GLOB) { + continue; + } + + FileGlobal *fg = static_cast(read_struct(fd, bhead, "Global")); + file_global_minversion_hotfix(fg); + + if ((fg->minversion > BLENDER_FILE_VERSION) || + (fg->minversion == BLENDER_FILE_VERSION && fg->minsubversion > BLENDER_FILE_SUBVERSION)) + { + char writer_ver_str[16]; + char min_reader_ver_str[16]; + if (fd->fileversion == fg->minversion) { + BKE_blender_version_blendfile_string_from_values( + writer_ver_str, sizeof(writer_ver_str), short(fd->fileversion), fg->subversion); + BKE_blender_version_blendfile_string_from_values( + min_reader_ver_str, sizeof(min_reader_ver_str), fg->minversion, fg->minsubversion); + } + else { + BKE_blender_version_blendfile_string_from_values( + writer_ver_str, sizeof(writer_ver_str), short(fd->fileversion), -1); + BKE_blender_version_blendfile_string_from_values( + min_reader_ver_str, sizeof(min_reader_ver_str), fg->minversion, -1); + } + BKE_reportf(reports, + RPT_ERROR, + TIP_("The file was saved by a newer version, open it with Blender %s or later"), + min_reader_ver_str); + CLOG_WARN(&LOG, + "%s: File saved by a newer version of Blender (%s), Blender %s or later is " + "needed to open it.", + fd->relabase, + writer_ver_str, + min_reader_ver_str); + MEM_freeN(fg); + return true; + } + MEM_freeN(fg); + return false; + } + return false; +} + static FileData *blo_decode_and_check(FileData *fd, ReportList *reports) { decode_blender_header(fd); @@ -1089,6 +1159,10 @@ static FileData *blo_decode_and_check(FileData *fd, ReportList *reports) blo_filedata_free(fd); fd = nullptr; } + if (is_minversion_older_than_blender(fd, reports)) { + blo_filedata_free(fd); + fd = nullptr; + } } else { BKE_reportf( @@ -3402,10 +3476,15 @@ static BHead *read_global(BlendFileData *bfd, FileData *fd, BHead *bhead) { FileGlobal *fg = static_cast(read_struct(fd, bhead, "Global")); - /* copy to bfd handle */ + /* NOTE: `bfd->main->versionfile` is supposed to have already been set from `fd->fileversion` + * beforehand by calling code. */ bfd->main->subversionfile = fg->subversion; + bfd->main->has_forward_compatibility_issues = !MAIN_VERSION_FILE_OLDER_OR_EQUAL( + bfd->main, BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION); + bfd->main->minversionfile = fg->minversion; bfd->main->minsubversionfile = fg->minsubversion; + bfd->main->build_commit_timestamp = fg->build_commit_timestamp; STRNCPY(bfd->main->build_hash, fg->build_hash); diff --git a/source/blender/editors/include/ED_info.h b/source/blender/editors/include/ED_info.h index f6ddb1b3ab6..8074f6750f9 100644 --- a/source/blender/editors/include/ED_info.h +++ b/source/blender/editors/include/ED_info.h @@ -17,6 +17,10 @@ struct wmWindowManager; /* info_stats.c */ void ED_info_stats_clear(struct wmWindowManager *wm, struct ViewLayer *view_layer); +const char *ED_info_statusbar_string_ex(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer, + const char statusbar_flag); const char *ED_info_statusbar_string(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 6d2013688f5..b99274b7d17 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2455,6 +2455,7 @@ void uiTemplateHeader3D_mode(uiLayout *layout, struct bContext *C); void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C); void uiTemplateReportsBanner(uiLayout *layout, struct bContext *C); void uiTemplateInputStatus(uiLayout *layout, struct bContext *C); +void uiTemplateStatusInfo(uiLayout *layout, struct bContext *C); void uiTemplateKeymapItemProperties(uiLayout *layout, struct PointerRNA *ptr); bool uiTemplateEventFromKeymapItem(struct uiLayout *layout, diff --git a/source/blender/editors/interface/interface_templates.cc b/source/blender/editors/interface/interface_templates.cc index b061938a771..ad3175d0acc 100644 --- a/source/blender/editors/interface/interface_templates.cc +++ b/source/blender/editors/interface/interface_templates.cc @@ -39,6 +39,7 @@ #include "BLT_translation.h" #include "BKE_action.h" +#include "BKE_blender_version.h" #include "BKE_blendfile.h" #include "BKE_cachefile.h" #include "BKE_colorband.h" @@ -69,6 +70,7 @@ #include "DEG_depsgraph_query.h" #include "ED_fileselect.h" +#include "ED_info.h" #include "ED_object.h" #include "ED_render.h" #include "ED_screen.h" @@ -6509,6 +6511,126 @@ void uiTemplateInputStatus(uiLayout *layout, bContext *C) } } +void uiTemplateStatusInfo(uiLayout *layout, bContext *C) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + if (!bmain->has_forward_compatibility_issues) { + const char *status_info_txt = ED_info_statusbar_string(bmain, scene, view_layer); + uiItemL(layout, status_info_txt, ICON_NONE); + return; + } + + /* Blender version part is shown as warning area when there are forward compatibility issues with + * currently loaded .blend file. */ + + const char *status_info_txt = ED_info_statusbar_string_ex( + bmain, scene, view_layer, (U.statusbar_flag & ~STATUSBAR_SHOW_VERSION)); + uiItemL(layout, status_info_txt, ICON_NONE); + + status_info_txt = ED_info_statusbar_string_ex(bmain, scene, view_layer, STATUSBAR_SHOW_VERSION); + + uiBut *but; + + const uiStyle *style = UI_style_get(); + uiLayout *ui_abs = uiLayoutAbsolute(layout, false); + uiBlock *block = uiLayoutGetBlock(ui_abs); + 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)); + + 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, + 0, + 0, + ""); + /* 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, + 0, + 0, + ""); + + /* 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, + TIP_("File saved by newer Blender\n(%s), expect loss of data"), + writer_ver_str); + but = uiDefIconBut(block, + UI_BTYPE_BUT, + 0, + ICON_ERROR, + int(3 * UI_SCALE_FAC), + 0, + UI_UNIT_X, + UI_UNIT_Y, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + compat_error_msg); + 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, + 0.0f, + 0.0f, + compat_error_msg); + + UI_block_emboss_set(block, previous_emboss); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index 19dbd60820f..94cb89c4926 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -599,10 +599,10 @@ static void get_stats_string(char *info, info + *ofs, len - *ofs, TIP_(" | Objects:%s/%s"), stats_fmt->totobjsel, stats_fmt->totobj); } -static const char *info_statusbar_string(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - char statusbar_flag) +const char *ED_info_statusbar_string_ex(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + const char statusbar_flag) { char formatted_mem[BLI_STR_FORMAT_INT64_BYTE_UNIT_SIZE]; size_t ofs = 0; @@ -682,7 +682,7 @@ static const char *info_statusbar_string(Main *bmain, const char *ED_info_statusbar_string(Main *bmain, Scene *scene, ViewLayer *view_layer) { - return info_statusbar_string(bmain, scene, view_layer, U.statusbar_flag); + return ED_info_statusbar_string_ex(bmain, scene, view_layer, U.statusbar_flag); } const char *ED_info_statistics_string(Main *bmain, Scene *scene, ViewLayer *view_layer) @@ -692,7 +692,7 @@ const char *ED_info_statistics_string(Main *bmain, Scene *scene, ViewLayer *view STATUSBAR_SHOW_VERSION | STATUSBAR_SHOW_SCENE_DURATION; - return info_statusbar_string(bmain, scene, view_layer, statistics_status_bar_flag); + return ED_info_statusbar_string_ex(bmain, scene, view_layer, statistics_status_bar_flag); } static void stats_row(int col1, diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index a9ea378194b..44d25ff6f20 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -1769,6 +1769,9 @@ void RNA_api_ui_layout(StructRNA *srna) func = RNA_def_function(srna, "template_input_status", "uiTemplateInputStatus"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); + func = RNA_def_function(srna, "template_status_info", "uiTemplateStatusInfo"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + func = RNA_def_function(srna, "template_node_link", "uiTemplateNodeLink"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); parm = RNA_def_pointer(func, "ntree", "NodeTree", "", ""); diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index a6c644d155a..a859a124f04 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -62,6 +62,7 @@ #include "BKE_appdir.h" #include "BKE_autoexec.h" #include "BKE_blender.h" +#include "BKE_blender_version.h" #include "BKE_blendfile.h" #include "BKE_callbacks.h" #include "BKE_context.h" @@ -3277,6 +3278,12 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (!is_save_as) { + /* If saved as current file, there are technically no more compatibility issues, the file on + * disk now matches the currently opened data version-wise. */ + bmain->has_forward_compatibility_issues = false; + } + WM_event_add_notifier(C, NC_WM | ND_FILESAVE, nullptr); if (!is_save_as && RNA_boolean_get(op->ptr, "exit")) { @@ -3381,7 +3388,13 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent * } if (blendfile_path[0] != '\0') { - ret = wm_save_as_mainfile_exec(C, op); + if (CTX_data_main(C)->has_forward_compatibility_issues) { + wm_save_file_forwardcompat_dialog(C, op); + ret = OPERATOR_INTERFACE; + } + else { + ret = wm_save_as_mainfile_exec(C, op); + } } else { WM_event_add_fileselect(C, op); @@ -3660,6 +3673,206 @@ void wm_test_autorun_warning(bContext *C) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Save File Forward Compatibility Dialog + * \{ */ + +static void free_post_file_close_action(void *arg) +{ + wmGenericCallback *action = (wmGenericCallback *)arg; + WM_generic_callback_free(action); +} + +static void wm_free_operator_properties_callback(void *user_data) +{ + IDProperty *properties = (IDProperty *)user_data; + IDP_FreeProperty(properties); +} + +static const char *save_file_forwardcompat_dialog_name = "save_file_forwardcompat_popup"; + +static void file_forwardcompat_detailed_info_show(uiLayout *parent_layout, Main *bmain) +{ + uiLayout *layout = uiLayoutColumn(parent_layout, true); + /* Trick to make both lines of text below close enough to look like they are part of a same + * 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); + } + + char message_line1[256]; + char message_line2[256]; + SNPRINTF(message_line1, + TIP_("This file was saved by a newer version of Blender (%s)"), + writer_ver_str); + SNPRINTF(message_line2, + TIP_("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); +} + +static void save_file_forwardcompat_cancel(bContext *C, void *arg_block, void * /*arg_data*/) +{ + wmWindow *win = CTX_wm_window(C); + UI_popup_block_close(C, win, static_cast(arg_block)); +} + +static void save_file_forwardcompat_cancel_button(uiBlock *block, wmGenericCallback *post_action) +{ + uiBut *but = uiDefIconTextBut( + block, UI_BTYPE_BUT, 0, 0, IFACE_("Cancel"), 0, 0, 0, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + UI_but_func_set(but, save_file_forwardcompat_cancel, block, post_action); + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); +} + +static void save_file_forwardcompat_overwrite(bContext *C, void *arg_block, void *arg_data) +{ + wmWindow *win = CTX_wm_window(C); + UI_popup_block_close(C, win, static_cast(arg_block)); + + /* Re-use operator properties as defined for the initial 'save' operator, which triggered this + * 'forward compat' popup. */ + wmGenericCallback *callback = WM_generic_callback_steal( + static_cast(arg_data)); + PointerRNA operator_propptr = {}; + PointerRNA *operator_propptr_p = &operator_propptr; + IDProperty *operator_idproperties = static_cast(callback->user_data); + WM_operator_properties_alloc(&operator_propptr_p, &operator_idproperties, "WM_OT_save_mainfile"); + + WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, operator_propptr_p, nullptr); + + WM_generic_callback_free(callback); +} + +static void save_file_forwardcompat_overwrite_button(uiBlock *block, + wmGenericCallback *post_action) +{ + uiBut *but = uiDefIconTextBut( + block, UI_BTYPE_BUT, 0, 0, IFACE_("Overwrite"), 0, 0, 0, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + UI_but_func_set(but, save_file_forwardcompat_overwrite, block, post_action); + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); + UI_but_flag_enable(but, UI_BUT_REDALERT); +} + +static void save_file_forwardcompat_saveas(bContext *C, void *arg_block, void * /*arg_data*/) +{ + wmWindow *win = CTX_wm_window(C); + UI_popup_block_close(C, win, static_cast(arg_block)); + + WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr); +} + +static void save_file_forwardcompat_saveas_button(uiBlock *block, wmGenericCallback *post_action) +{ + uiBut *but = uiDefIconTextBut(block, + UI_BTYPE_BUT, + 0, + 0, + IFACE_("Save As..."), + 0, + 0, + 0, + UI_UNIT_Y, + nullptr, + 0, + 0, + 0, + 0, + ""); + UI_but_func_set(but, save_file_forwardcompat_saveas, block, post_action); + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); + UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT); +} + +static uiBlock *block_create_save_file_forwardcompat_dialog(bContext *C, + ARegion *region, + void *arg1) +{ + wmGenericCallback *post_action = static_cast(arg1); + Main *bmain = CTX_data_main(C); + + uiBlock *block = UI_block_begin(C, region, save_file_forwardcompat_dialog_name, UI_EMBOSS); + UI_block_flag_enable( + block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT); + UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + + uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_WARNING); + + /* Title. */ + uiItemL_ex( + layout, TIP_("Overwrite file with an older Blender version?"), ICON_NONE, true, false); + + /* Filename. */ + const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C)); + char filename[FILE_MAX]; + if (blendfile_path[0] != '\0') { + BLI_path_split_file_part(blendfile_path, filename, sizeof(filename)); + } + else { + SNPRINTF(filename, "%s.blend", DATA_("untitled")); + /* Since this dialog should only be shown when re-saving an existing file, current filepath + * should never be empty. */ + BLI_assert_unreachable(); + } + uiItemL(layout, filename, ICON_NONE); + + /* Detailed message info. */ + file_forwardcompat_detailed_info_show(layout, bmain); + + uiItemS_ex(layout, 4.0f); + + /* Buttons. */ + + uiLayout *split = uiLayoutSplit(layout, 0.3f, true); + uiLayoutSetScaleY(split, 1.2f); + + uiLayoutColumn(split, false); + save_file_forwardcompat_overwrite_button(block, post_action); + + uiLayout *split_right = uiLayoutSplit(split, 0.1f, true); + + uiLayoutColumn(split_right, false); + /* Empty space. */ + + uiLayoutColumn(split_right, false); + save_file_forwardcompat_cancel_button(block, post_action); + + uiLayoutColumn(split_right, false); + save_file_forwardcompat_saveas_button(block, post_action); + + UI_block_bounds_set_centered(block, 14 * UI_SCALE_FAC); + return block; +} + +void wm_save_file_forwardcompat_dialog(bContext *C, wmOperator *op) +{ + if (!UI_popup_block_name_exists(CTX_wm_screen(C), save_file_forwardcompat_dialog_name)) { + wmGenericCallback *callback = MEM_cnew(__func__); + callback->exec = nullptr; + callback->user_data = IDP_CopyProperty(op->properties); + callback->free_user_data = wm_free_operator_properties_callback; + + UI_popup_block_invoke( + C, block_create_save_file_forwardcompat_dialog, callback, free_post_file_close_action); + } +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Close File Dialog * \{ */ @@ -3710,11 +3923,23 @@ static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_dat bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0'; if (file_has_been_saved_before) { - if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, nullptr, nullptr) & - OPERATOR_CANCELLED) - { + if (bmain->has_forward_compatibility_issues) { + /* Need to invoke to get the filebrowser and choose where to save the new file. + * This also makes it impossible to keep on going with current operation, which is why + * callback cannot be executed anymore. + * + * This is the same situation as what happens when the file has never been saved before + * (outer `else` statement, below). */ + WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr); execute_callback = false; } + else { + if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, nullptr, nullptr) & + OPERATOR_CANCELLED) + { + execute_callback = false; + } + } } else { WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr); @@ -3743,10 +3968,27 @@ static void wm_block_file_close_discard_button(uiBlock *block, wmGenericCallback UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); } -static void wm_block_file_close_save_button(uiBlock *block, wmGenericCallback *post_action) +static void wm_block_file_close_save_button(uiBlock *block, + wmGenericCallback *post_action, + const bool has_forwardcompat_issues) { uiBut *but = uiDefIconTextBut( - block, UI_BTYPE_BUT, 0, 0, IFACE_("Save"), 0, 0, 0, UI_UNIT_Y, 0, 0, 0, 0, 0, ""); + block, + UI_BTYPE_BUT, + 0, + 0, + /* Forward compatibility issues force using 'save as' operator instead of 'save' one. */ + has_forwardcompat_issues ? IFACE_("Save As...") : IFACE_("Save"), + 0, + 0, + 0, + UI_UNIT_Y, + nullptr, + 0, + 0, + 0, + 0, + ""); UI_but_func_set(but, wm_block_file_close_save, block, post_action); UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT); @@ -3774,6 +4016,8 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C, uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION); + const bool has_forwardcompat_issues = bmain->has_forward_compatibility_issues; + /* Title. */ uiItemL_ex(layout, TIP_("Save changes before closing?"), ICON_NONE, true, false); @@ -3788,6 +4032,11 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C, } uiItemL(layout, filename, ICON_NONE); + /* Potential forward compatibility issues message. */ + if (has_forwardcompat_issues) { + file_forwardcompat_detailed_info_show(layout, bmain); + } + /* Image Saving Warnings. */ ReportList reports; BKE_reports_init(&reports, RPT_STORE); @@ -3894,7 +4143,7 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C, uiLayoutSetScaleY(split, 1.2f); uiLayoutColumn(split, false); - wm_block_file_close_save_button(block, post_action); + wm_block_file_close_save_button(block, post_action, has_forwardcompat_issues); uiLayoutColumn(split, false); wm_block_file_close_discard_button(block, post_action); @@ -3920,19 +4169,13 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C, wm_block_file_close_cancel_button(block, post_action); uiLayoutColumn(split_right, false); - wm_block_file_close_save_button(block, post_action); + wm_block_file_close_save_button(block, post_action, has_forwardcompat_issues); } UI_block_bounds_set_centered(block, 14 * UI_SCALE_FAC); return block; } -static void free_post_file_close_action(void *arg) -{ - wmGenericCallback *action = (wmGenericCallback *)arg; - WM_generic_callback_free(action); -} - void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action) { if (!UI_popup_block_name_exists(CTX_wm_screen(C), close_file_dialog_name)) { @@ -3944,12 +4187,6 @@ void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action) } } -static void wm_free_operator_properties_callback(void *user_data) -{ - IDProperty *properties = (IDProperty *)user_data; - IDP_FreeProperty(properties); -} - bool wm_operator_close_file_dialog_if_needed(bContext *C, wmOperator *op, wmGenericCallbackFn post_action_fn) diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 225589d996a..7b7c21ade6b 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -88,6 +88,13 @@ bool wm_operator_close_file_dialog_if_needed(bContext *C, * Check if there is data that would be lost when closing the current file without saving. */ bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWindowManager *wm); +/** + * Confirmation dialog when user is about to save the current blend file, and it was prviously + * created by a newer version of Blender. + * + * Important to ask confirmation, as this is a very common scenario of data loss. + */ +void wm_save_file_forwardcompat_dialog(bContext *C, wmOperator *op); void WM_OT_save_homefile(struct wmOperatorType *ot); void WM_OT_save_userpref(struct wmOperatorType *ot); -- 2.30.2 From f1ea39ed019545d28767b58953956ed82f626f64 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 24 Jul 2023 14:48:27 +0200 Subject: [PATCH 4/5] BKE_file: Add util to check if a given blend file is readable. `BKE_blendfile_is_readable` attempts to open the given file path, and return `true` on success, `false` on failure. Part of #109151 (PR !110109). --- source/blender/blenkernel/BKE_blendfile.h | 7 +++++++ source/blender/blenkernel/intern/blendfile.cc | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/source/blender/blenkernel/BKE_blendfile.h b/source/blender/blenkernel/BKE_blendfile.h index f216ff58ab3..85e44a4bb6e 100644 --- a/source/blender/blenkernel/BKE_blendfile.h +++ b/source/blender/blenkernel/BKE_blendfile.h @@ -45,6 +45,13 @@ bool BKE_blendfile_library_path_explode(const char *path, char **r_group, char **r_name); +/** + * Check whether a given path is actually a Blender-readable, valid .blend file. + * + * \note Currently does attempt to open and read (part of) the given file. + */ +bool BKE_blendfile_is_readable(const char *path, struct ReportList *reports); + /** * Shared setup function that makes the data from `bfd` into the current blend file, * replacing the contents of #G.main. diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index fe096836e84..c670f857647 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -136,6 +136,18 @@ bool BKE_blendfile_library_path_explode(const char *path, return true; } +bool BKE_blendfile_is_readable(const char *path, ReportList *reports) +{ + BlendFileReadReport readfile_reports; + readfile_reports.reports = reports; + BlendHandle *bh = BLO_blendhandle_from_file(path, &readfile_reports); + if (bh != nullptr) { + BLO_blendhandle_close(bh); + return true; + } + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ -- 2.30.2 From e75343010241ef41b7218cb97cbeb713dc9fcb3c Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 24 Jul 2023 14:53:33 +0200 Subject: [PATCH 5/5] File Browser: Add proper warning when trying to list content of invalid library. In case a blend file opening fails (in the context of library content listing, e.g. for linking or appending), the File Browser would just show an emtpy window. With the new comnpatibility policy, this will now become a fairly common situation, which requires giving proper explanations to the user. This commit re-uses and extends the system used to display errors messages from the Asset browser. Part of #109151 (PR !110109). --- .../blender/editors/space_file/file_draw.cc | 125 +++++++++++++++--- .../blender/editors/space_file/file_intern.h | 7 + source/blender/editors/space_file/filelist.cc | 5 + source/blender/editors/space_file/filelist.h | 4 + .../blender/editors/space_file/space_file.c | 9 ++ 5 files changed, 133 insertions(+), 17 deletions(-) diff --git a/source/blender/editors/space_file/file_draw.cc b/source/blender/editors/space_file/file_draw.cc index c147b2564be..e439f67a8e3 100644 --- a/source/blender/editors/space_file/file_draw.cc +++ b/source/blender/editors/space_file/file_draw.cc @@ -25,6 +25,7 @@ #include "BKE_blendfile.h" #include "BKE_context.h" +#include "BKE_report.h" #include "BLT_translation.h" @@ -1168,12 +1169,11 @@ void file_draw_list(const bContext *C, ARegion *region) layout->curr_size = params->thumbnail_size; } -static void file_draw_invalid_library_hint(const bContext *C, - const SpaceFile *sfile, - ARegion *region) +static void file_draw_invalid_asset_library_hint(const bContext *C, + const SpaceFile *sfile, + ARegion *region, + FileAssetSelectParams *asset_params) { - const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); - char library_ui_path[PATH_MAX]; file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path)); @@ -1228,21 +1228,112 @@ static void file_draw_invalid_library_hint(const bContext *C, } } +static void file_draw_invalid_library_hint(const bContext * /*C*/, + const SpaceFile *sfile, + ARegion *region, + const char *blendfile_path, + ReportList *reports) +{ + uchar text_col[4]; + UI_GetThemeColor4ubv(TH_TEXT, text_col); + + const View2D *v2d = ®ion->v2d; + const int pad = sfile->layout->tile_border_x; + const int width = BLI_rctf_size_x(&v2d->tot) - (2 * pad); + const int line_height = sfile->layout->textheight; + int sx = v2d->tot.xmin + pad; + /* For some reason no padding needed. */ + int sy = v2d->tot.ymax; + + { + const char *message = TIP_("Unreadable Blender library file:"); + file_draw_string_multiline(sx, sy, message, width, line_height, text_col, nullptr, &sy); + + sy -= line_height; + file_draw_string(sx, sy, blendfile_path, width, line_height, UI_STYLE_TEXT_LEFT, text_col); + } + + /* Separate a bit further. */ + sy -= line_height * 2.2f; + + LISTBASE_FOREACH (Report *, report, &reports->list) { + const short report_type = report->type; + if (report_type <= RPT_INFO) { + continue; + } + + int icon = ICON_INFO; + if (report_type > RPT_WARNING) { + icon = ICON_ERROR; + } + UI_icon_draw(sx, sy - UI_UNIT_Y, icon); + + file_draw_string_multiline(sx + UI_UNIT_X, + sy, + TIP_(report->message), + width - UI_UNIT_X, + line_height, + text_col, + nullptr, + &sy); + sy -= line_height; + } +} + bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region) { - FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); - /* Only for asset browser. */ - if (!ED_fileselect_is_asset_browser(sfile)) { - return false; - } - /* Check if the library exists. */ - if ((asset_params->asset_library_ref.type == ASSET_LIBRARY_LOCAL) || - filelist_is_dir(sfile->files, asset_params->base_params.dir)) - { - return false; + char blendfile_path[FILE_MAX_LIBEXTRA]; + const bool is_asset_browser = ED_fileselect_is_asset_browser(sfile); + const bool is_library_browser = !is_asset_browser && + filelist_islibrary(sfile->files, blendfile_path, nullptr); + + if (is_asset_browser) { + FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); + + /* Check if the asset library exists. */ + if (!((asset_params->asset_library_ref.type == ASSET_LIBRARY_LOCAL) || + filelist_is_dir(sfile->files, asset_params->base_params.dir))) + { + file_draw_invalid_asset_library_hint(C, sfile, region, asset_params); + return true; + } } - file_draw_invalid_library_hint(C, sfile, region); + /* Check if the blendfile library is valid (has entries). */ + if (is_library_browser) { + if (!filelist_is_ready(sfile->files)) { + return false; + } - return true; + const int numfiles = filelist_files_num_entries(sfile->files); + if (numfiles > 0) { + return false; + } + + /* This could all be part of the file-list loading: + * - When loading fails this could be saved in the file-list, e.g. when + * `BLO_blendhandle_from_file()` returns null in `filelist_readjob_list_lib()`, a + * `FL_HAS_INVALID_LIBRARY` file-list flag could be set. + * - Reports from it could also be stored in `FileList` rather than being ignored + * (`RPT_STORE` must be set!). + * - Then we could just check for `is_library_browser` and the `FL_HAS_INVALID_LIBRARY` flag + * here, and draw the hint with the reports in the file-list. (We would not draw a hint for + * recursive loading, even if the file-list has the "has invalid library" flag set, which + * seems like the wanted behavior.) + * - The call to BKE_blendfile_is_readable() would not be needed then. + */ + if (!sfile->runtime->is_blendfile_status_set) { + BKE_reports_clear(&sfile->runtime->is_blendfile_readable_reports); + sfile->runtime->is_blendfile_readable = BKE_blendfile_is_readable( + blendfile_path, &sfile->runtime->is_blendfile_readable_reports); + sfile->runtime->is_blendfile_status_set = true; + } + if (!sfile->runtime->is_blendfile_readable) { + file_draw_invalid_library_hint( + C, sfile, region, blendfile_path, &sfile->runtime->is_blendfile_readable_reports); + return true; + } + } + + return false; } diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index e11244d0214..12e2b8ab0e8 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -8,6 +8,7 @@ #pragma once #include "DNA_space_types.h" +#include "DNA_windowmanager_types.h" #ifdef __cplusplus extern "C" { @@ -179,6 +180,12 @@ typedef struct SpaceFile_Runtime { * Use file_on_reload_callback_register() to register a callback. */ onReloadFn on_reload; onReloadFnData on_reload_custom_data; + + /* Indicates, if the current filepath is a blendfile library one, if its status has been checked, + * and if it is readable. */ + bool is_blendfile_status_set; + bool is_blendfile_readable; + ReportList is_blendfile_readable_reports; } SpaceFile_Runtime; /** diff --git a/source/blender/editors/space_file/filelist.cc b/source/blender/editors/space_file/filelist.cc index 1013af8ac29..41444cb5e26 100644 --- a/source/blender/editors/space_file/filelist.cc +++ b/source/blender/editors/space_file/filelist.cc @@ -1933,6 +1933,11 @@ BlendHandle *filelist_lib(FileList *filelist) return filelist->libfiledata; } +int filelist_files_num_entries(FileList *filelist) +{ + return filelist->filelist.entries_num; +} + static const char *fileentry_uiname(const char *root, FileListInternEntry *entry, char *buff) { if (entry->asset) { diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 6cc89967d79..b7a951d8c07 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -205,6 +205,10 @@ struct BlendHandle *filelist_lib(struct FileList *filelist); bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group); void filelist_freelib(struct FileList *filelist); +/** Return the total raw number of entries listed in the given `filelist`, whether they are + * filtered out or not. */ +int filelist_files_num_entries(struct FileList *filelist); + void filelist_readjob_start(struct FileList *filelist, int space_notifier, const struct bContext *C); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 29f6adbfbb5..7bb28088292 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -19,6 +19,7 @@ #include "BKE_global.h" #include "BKE_lib_remap.h" #include "BKE_main.h" +#include "BKE_report.h" #include "BKE_screen.h" #include "RNA_access.h" @@ -125,6 +126,9 @@ static void file_free(SpaceLink *sl) MEM_SAFE_FREE(sfile->params); MEM_SAFE_FREE(sfile->asset_params); + if (sfile->runtime != NULL) { + BKE_reports_clear(&sfile->runtime->is_blendfile_readable_reports); + } MEM_SAFE_FREE(sfile->runtime); MEM_SAFE_FREE(sfile->layout); @@ -141,6 +145,7 @@ static void file_init(wmWindowManager *UNUSED(wm), ScrArea *area) if (sfile->runtime == NULL) { sfile->runtime = MEM_callocN(sizeof(*sfile->runtime), __func__); + BKE_reports_init(&sfile->runtime->is_blendfile_readable_reports, RPT_STORE); } /* Validate the params right after file read. */ fileselect_refresh_params(sfile); @@ -203,6 +208,10 @@ static void file_refresh(const bContext *C, ScrArea *area) fileselect_refresh_params(sfile); folder_history_list_ensure_for_active_browse_mode(sfile); + if (sfile->runtime != NULL) { + sfile->runtime->is_blendfile_status_set = false; + } + if (sfile->files && (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES) && filelist_needs_reset_on_main_changes(sfile->files)) { -- 2.30.2