3.6 - Implementation of new blendfile compatibility policy #110568
|
@ -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 = (
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -28,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);
|
||||
|
@ -39,6 +43,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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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<FileGlobal *>(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<FileGlobal *>(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<FileGlobal *>(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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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", "", "");
|
||||
|
|
|
@ -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<uiBlock *>(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<uiBlock *>(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<wmGenericCallback *>(arg_data));
|
||||
PointerRNA operator_propptr = {};
|
||||
PointerRNA *operator_propptr_p = &operator_propptr;
|
||||
IDProperty *operator_idproperties = static_cast<IDProperty *>(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<uiBlock *>(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<wmGenericCallback *>(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<wmGenericCallback>(__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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue