diff --git a/source/blender/blenkernel/BKE_blendfile.h b/source/blender/blenkernel/BKE_blendfile.h index 2bff684948d..e835137bfa1 100644 --- a/source/blender/blenkernel/BKE_blendfile.h +++ b/source/blender/blenkernel/BKE_blendfile.h @@ -74,6 +74,7 @@ void BKE_blendfile_write_partial_begin(struct Main *bmain_src); bool BKE_blendfile_write_partial(struct Main *bmain_src, const char *filepath, const int write_flags, + const int remap_mode, struct ReportList *reports); void BKE_blendfile_write_partial_end(struct Main *bmain_src); diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index a1871d22da7..a134f29228f 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -177,13 +177,11 @@ enum { /** On read, use #FileGlobal.filename instead of the real location on-disk, * needed for recovering temp files so relative paths resolve */ G_FILE_RECOVER = (1 << 23), - /** On write, remap relative file paths to the new file location. */ - G_FILE_RELATIVE_REMAP = (1 << 24), /** On write, make backup `.blend1`, `.blend2` ... files, when the users preference is enabled */ G_FILE_HISTORY = (1 << 25), /** BMesh option to save as older mesh format */ /* #define G_FILE_MESH_COMPAT (1 << 26) */ - /** On write, restore paths after editing them (G_FILE_RELATIVE_REMAP) */ + /** On write, restore paths after editing them (see #BLO_WRITE_PATH_REMAP_RELATIVE). */ G_FILE_SAVE_COPY = (1 << 27), /* #define G_FILE_GLSL_NO_ENV_LIGHTING (1 << 28) */ /* deprecated */ }; @@ -192,8 +190,7 @@ enum { * Run-time only #G.fileflags which are never read or written to/from Blend files. * This means we can change the values without worrying about do-versions. */ -#define G_FILE_FLAG_ALL_RUNTIME \ - (G_FILE_NO_UI | G_FILE_RELATIVE_REMAP | G_FILE_HISTORY | G_FILE_SAVE_COPY) +#define G_FILE_FLAG_ALL_RUNTIME (G_FILE_NO_UI | G_FILE_HISTORY | G_FILE_SAVE_COPY) /** ENDIAN_ORDER: indicates what endianness the platform where the file was written had. */ #if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c index 4d27621a861..bd133ce9ea6 100644 --- a/source/blender/blenkernel/intern/blender_copybuffer.c +++ b/source/blender/blenkernel/intern/blender_copybuffer.c @@ -72,9 +72,10 @@ void BKE_copybuffer_tag_ID(ID *id) */ bool BKE_copybuffer_save(Main *bmain_src, const char *filename, ReportList *reports) { - const int write_flags = G_FILE_RELATIVE_REMAP; + const int write_flags = 0; + const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; - bool retval = BKE_blendfile_write_partial(bmain_src, filename, write_flags, reports); + bool retval = BKE_blendfile_write_partial(bmain_src, filename, write_flags, remap_mode, reports); BKE_blendfile_write_partial_end(bmain_src); diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index c8aff345487..e19a4935698 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -119,7 +119,7 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev) BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter); BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), numstr); - /* success = */ /* UNUSED */ BLO_write_file(bmain, filename, fileflags, NULL, NULL); + /* success = */ /* UNUSED */ BLO_write_file(bmain, filename, fileflags, NULL); BLI_strncpy(mfu->filename, filename, sizeof(mfu->filename)); } diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index ef474022f19..a3031e9047f 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -646,7 +646,7 @@ bool BKE_blendfile_userdef_write(const char *filepath, ReportList *reports) Main *mainb = MEM_callocN(sizeof(Main), "empty main"); bool ok = false; - if (BLO_write_file(mainb, filepath, G_FILE_USERPREFS, reports, NULL)) { + if (BLO_write_file(mainb, filepath, G_FILE_USERPREFS, reports)) { ok = true; } @@ -777,7 +777,8 @@ bool BKE_blendfile_workspace_config_write(Main *bmain, const char *filepath, Rep BKE_blendfile_write_partial_tag_ID(&workspace->id, true); } - if (BKE_blendfile_write_partial(bmain, filepath, fileflags, reports)) { + if (BKE_blendfile_write_partial( + bmain, filepath, fileflags, BLO_WRITE_PATH_REMAP_NONE, reports)) { retval = true; } @@ -829,11 +830,13 @@ static void blendfile_write_partial_cb(void *UNUSED(handle), Main *UNUSED(bmain) } /** + * \param remap_mode: Choose the kind of path remapping or none #eBLO_FilePathRemap. * \return Success. */ bool BKE_blendfile_write_partial(Main *bmain_src, const char *filepath, const int write_flags, + const int remap_mode, ReportList *reports) { Main *bmain_dst = MEM_callocN(sizeof(Main), "copybuffer"); @@ -875,12 +878,12 @@ bool BKE_blendfile_write_partial(Main *bmain_src, * This happens because id_sort_by_name does not take into account * string case or the library name, so the order is not strictly * defined for two linked data-blocks with the same name! */ - if (write_flags & G_FILE_RELATIVE_REMAP) { + if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) { path_list_backup = BKE_bpath_list_backup(bmain_dst, path_list_flag); } /* save the buffer */ - retval = BLO_write_file(bmain_dst, filepath, write_flags, reports, NULL); + retval = BLO_write_file_ex(bmain_dst, filepath, write_flags, reports, remap_mode, NULL); if (path_list_backup) { BKE_bpath_list_restore(bmain_dst, path_list_flag, path_list_backup); diff --git a/source/blender/blenloader/BLO_writefile.h b/source/blender/blenloader/BLO_writefile.h index d83abf7f9ed..18783474392 100644 --- a/source/blender/blenloader/BLO_writefile.h +++ b/source/blender/blenloader/BLO_writefile.h @@ -30,11 +30,32 @@ struct Main; struct MemFile; struct ReportList; +/** + * Adjust paths when saving (kept unless #G_FILE_SAVE_COPY is set). + */ +typedef enum eBLO_WritePathRemap { + /** No path manipulation. */ + BLO_WRITE_PATH_REMAP_NONE = 1, + /** Remap existing relative paths (default). */ + BLO_WRITE_PATH_REMAP_RELATIVE = 2, + /** Remap paths making all paths relative to the new location. */ + BLO_WRITE_PATH_REMAP_RELATIVE_ALL = 3, + /** Make all paths absolute. */ + BLO_WRITE_PATH_REMAP_ABSOLUTE = 4, +} eBLO_WritePathRemap; + +extern bool BLO_write_file_ex(struct Main *mainvar, + const char *filepath, + const int write_flags, + struct ReportList *reports, + /* Extra arguments. */ + eBLO_WritePathRemap remap_mode, + const struct BlendThumbnail *thumb); extern bool BLO_write_file(struct Main *mainvar, const char *filepath, - int write_flags, - struct ReportList *reports, - const struct BlendThumbnail *thumb); + const int write_flags, + struct ReportList *reports); + extern bool BLO_write_file_mem(struct Main *mainvar, struct MemFile *compare, struct MemFile *current, diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index a40cc4c3ad2..1cda22de941 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -4400,11 +4400,13 @@ static bool do_history(const char *name, ReportList *reports) /** * \return Success. */ -bool BLO_write_file(Main *mainvar, - const char *filepath, - int write_flags, - ReportList *reports, - const BlendThumbnail *thumb) +bool BLO_write_file_ex(Main *mainvar, + const char *filepath, + const int write_flags, + ReportList *reports, + /* Extra arguments. */ + eBLO_WritePathRemap remap_mode, + const BlendThumbnail *thumb) { char tempname[FILE_MAX + 1]; eWriteWrapType ww_type; @@ -4439,7 +4441,15 @@ bool BLO_write_file(Main *mainvar, } /* Remapping of relative paths to new file location. */ - if (write_flags & G_FILE_RELATIVE_REMAP) { + if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) { + + if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) { + /* Make all relative as none of the existing paths can be relative in an unsaved document. */ + if (G.relbase_valid == false) { + remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE_ALL; + } + } + char dir_src[FILE_MAX]; char dir_dst[FILE_MAX]; BLI_split_dir_part(mainvar->name, dir_src, sizeof(dir_src)); @@ -4449,23 +4459,43 @@ bool BLO_write_file(Main *mainvar, BLI_path_normalize(mainvar->name, dir_dst); BLI_path_normalize(mainvar->name, dir_src); - if (G.relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) { - /* Saved to same path. Nothing to do. */ - write_flags &= ~G_FILE_RELATIVE_REMAP; + /* Only for relative, not relative-all, as this means making existing paths relative. */ + if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) { + if (G.relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) { + /* Saved to same path. Nothing to do. */ + remap_mode = BLO_WRITE_PATH_REMAP_NONE; + } } - else { + else if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) { + if (G.relbase_valid == false) { + /* Unsaved, all paths are absolute.Even if the user manages to set a relative path, + * there is no base-path that can be used to make it absolute. */ + remap_mode = BLO_WRITE_PATH_REMAP_NONE; + } + } + + if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) { /* Check if we need to backup and restore paths. */ if (UNLIKELY(G_FILE_SAVE_COPY & write_flags)) { path_list_backup = BKE_bpath_list_backup(mainvar, path_list_flag); } - if (G.relbase_valid) { - /* Saved, make relative paths relative to new location (if possible). */ - BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, NULL); - } - else { - /* Unsaved, make all relative. */ - BKE_bpath_relative_convert(mainvar, dir_dst, NULL); + switch (remap_mode) { + case BLO_WRITE_PATH_REMAP_RELATIVE: + /* Saved, make relative paths relative to new location (if possible). */ + BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, NULL); + break; + case BLO_WRITE_PATH_REMAP_RELATIVE_ALL: + /* Make all relative (when requested or unsaved). */ + BKE_bpath_relative_convert(mainvar, dir_dst, NULL); + break; + case BLO_WRITE_PATH_REMAP_ABSOLUTE: + /* Make all absolute (when requested or unsaved). */ + BKE_bpath_absolute_convert(mainvar, dir_src, NULL); + break; + case BLO_WRITE_PATH_REMAP_NONE: + BLI_assert(0); /* Unreachable. */ + break; } } } @@ -4510,6 +4540,15 @@ bool BLO_write_file(Main *mainvar, return 1; } +bool BLO_write_file(Main *mainvar, + const char *filepath, + const int write_flags, + ReportList *reports) +{ + return BLO_write_file_ex( + mainvar, filepath, write_flags, reports, BLO_WRITE_PATH_REMAP_NONE, NULL); +} + /** * \return Success. */ diff --git a/source/blender/python/intern/bpy_library_write.c b/source/blender/python/intern/bpy_library_write.c index fec0cef7b05..66d20dd357f 100644 --- a/source/blender/python/intern/bpy_library_write.c +++ b/source/blender/python/intern/bpy_library_write.c @@ -35,6 +35,8 @@ #include "BKE_main.h" #include "BKE_report.h" +#include "BLO_writefile.h" + #include "RNA_types.h" #include "bpy_capi_utils.h" @@ -45,8 +47,7 @@ PyDoc_STRVAR( bpy_lib_write_doc, - ".. method:: write(filepath, datablocks, relative_remap=False, fake_user=False, " - "compress=False)\n" + ".. method:: write(filepath, datablocks, path_remap=False, fake_user=False, compress=False)\n" "\n" " Write data-blocks into a blend file.\n" "\n" @@ -58,8 +59,14 @@ PyDoc_STRVAR( " :type filepath: string\n" " :arg datablocks: set of data-blocks (:class:`bpy.types.ID` instances).\n" " :type datablocks: set\n" - " :arg relative_remap: When True, make paths relative to the current blend-file.\n" - " :type relative_remap: bool\n" + " :arg path_remap: Optionally remap paths when writing the file:\n" + "\n" + " - ``NONE`` No path manipulation (default).\n" + " - ``RELATIVE`` Remap paths that are already relative to the new location.\n" + " - ``RELATIVE_ALL`` Remap all paths to be relative to the new location.\n" + " - ``ABSOLUTE`` Make all paths absolute on writing.\n" + "\n" + " :type path_remap: string\n" " :arg fake_user: When True, data-blocks will be written with fake-user flag enabled.\n" " :type fake_user: bool\n" " :arg compress: When True, write a compressed blend file.\n" @@ -70,13 +77,23 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject const char *filepath; char filepath_abs[FILE_MAX]; PyObject *datablocks = NULL; - bool use_relative_remap = false, use_fake_user = false, use_compress = false; + + const struct PyC_StringEnumItems path_remap_items[] = { + {BLO_WRITE_PATH_REMAP_NONE, "NONE"}, + {BLO_WRITE_PATH_REMAP_RELATIVE, "RELATIVE"}, + {BLO_WRITE_PATH_REMAP_RELATIVE_ALL, "RELATIVE_ALL"}, + {BLO_WRITE_PATH_REMAP_ABSOLUTE, "ABSOLUTE"}, + {0, NULL}, + }; + struct PyC_StringEnum path_remap = {path_remap_items, BLO_WRITE_PATH_REMAP_NONE}; + + bool use_fake_user = false, use_compress = false; static const char *_keywords[] = { "filepath", "datablocks", /* optional */ - "relative_remap", + "path_remap", "fake_user", "compress", NULL, @@ -88,8 +105,8 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject &filepath, &PySet_Type, &datablocks, - PyC_ParseBool, - &use_relative_remap, + PyC_ParseStringEnum, + &path_remap, PyC_ParseBool, &use_fake_user, PyC_ParseBool, @@ -100,10 +117,6 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject Main *bmain_src = G_MAIN; int write_flags = 0; - if (use_relative_remap) { - write_flags |= G_FILE_RELATIVE_REMAP; - } - if (use_compress) { write_flags |= G_FILE_COMPRESS; } @@ -162,8 +175,8 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject ReportList reports; BKE_reports_init(&reports, RPT_STORE); - - retval = BKE_blendfile_write_partial(bmain_src, filepath_abs, write_flags, &reports); + retval = BKE_blendfile_write_partial( + bmain_src, filepath_abs, write_flags, path_remap.value_found, &reports); /* cleanup state */ BKE_blendfile_write_partial_end(bmain_src); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 1ecfbfb4b8d..b2753886f69 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1409,7 +1409,7 @@ bool write_crash_blend(void) BLI_strncpy(path, BKE_main_blendfile_path_from_global(), sizeof(path)); BLI_path_extension_replace(path, sizeof(path), "_crash.blend"); - if (BLO_write_file(G_MAIN, path, fileflags, NULL, NULL)) { + if (BLO_write_file(G_MAIN, path, fileflags, NULL)) { printf("written: %s\n", path); return 1; } @@ -1422,7 +1422,11 @@ bool write_crash_blend(void) /** * \see #wm_homefile_write_exec wraps #BLO_write_file in a similar way. */ -static bool wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports) +static bool wm_file_write(bContext *C, + const char *filepath, + int fileflags, + eBLO_WritePathRemap remap_mode, + ReportList *reports) { Main *bmain = CTX_data_main(C); Library *li; @@ -1499,7 +1503,7 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo /* XXX temp solution to solve bug, real fix coming (ton) */ bmain->recovered = 0; - if (BLO_write_file(CTX_data_main(C), filepath, fileflags, reports, thumb)) { + if (BLO_write_file_ex(CTX_data_main(C), filepath, fileflags, reports, remap_mode, thumb)) { const bool do_history = (G.background == false) && (CTX_wm_manager(C)->op_undo_depth == 0); if (!(fileflags & G_FILE_SAVE_COPY)) { @@ -1632,7 +1636,7 @@ void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *UNUSED(wt)) ED_editors_flush_edits(bmain); /* Error reporting into console */ - BLO_write_file(bmain, filepath, fileflags, NULL, NULL); + BLO_write_file(bmain, filepath, fileflags, NULL); } /* do timer after file write, just in case file write takes a long time */ wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, U.savetime * 60.0); @@ -1752,7 +1756,8 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) /* Force save as regular blend file. */ fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY); - if (BLO_write_file(bmain, filepath, fileflags, op->reports, NULL) == 0) { + if (BLO_write_file_ex( + bmain, filepath, fileflags, op->reports, BLO_WRITE_PATH_REMAP_RELATIVE, NULL) == 0) { printf("fail\n"); return OPERATOR_CANCELLED; } @@ -2668,6 +2673,12 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) char path[FILE_MAX]; const bool is_save_as = (op->type->invoke == wm_save_as_mainfile_invoke); + /* We could expose all options to the users however in most cases remapping + * existing relative paths is a good default. + * Users can manually make their paths relative & absolute if they wish. */ + const eBLO_WritePathRemap remap_mode = RNA_boolean_get(op->ptr, "relative_remap") ? + BLO_WRITE_PATH_REMAP_RELATIVE : + BLO_WRITE_PATH_REMAP_NONE; save_set_compress(op); if (RNA_struct_property_is_set(op->ptr, "filepath")) { @@ -2683,13 +2694,12 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) /* set compression flag */ SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "compress"), G_FILE_COMPRESS); - SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "relative_remap"), G_FILE_RELATIVE_REMAP); SET_FLAG_FROM_TEST( fileflags, (RNA_struct_property_is_set(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")), G_FILE_SAVE_COPY); - const bool ok = wm_file_write(C, path, fileflags, op->reports); + const bool ok = wm_file_write(C, path, fileflags, remap_mode, op->reports); if ((op->flag & OP_IS_INVOKE) == 0) { /* OP_IS_INVOKE is set when the operator is called from the GUI. diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 001acc459c2..27aa9c532e5 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -500,7 +500,7 @@ void WM_exit_ex(bContext *C, const bool do_python) has_edited = ED_editors_flush_edits(bmain); - if ((has_edited && BLO_write_file(bmain, filename, fileflags, NULL, NULL)) || + if ((has_edited && BLO_write_file(bmain, filename, fileflags, NULL)) || (undo_memfile && BLO_memfile_write_file(undo_memfile, filename))) { printf("Saved session recovery to '%s'\n", filename); }