UI: File Save Incremental Operator #104678

Merged
Harley Acheson merged 1 commits from Harley/blender:SaveIncremental into main 2023-06-01 17:35:06 +02:00
6 changed files with 73 additions and 2 deletions

View File

@ -590,6 +590,7 @@ def km_window(params):
("wm.open_mainfile", {"type": 'O', "value": 'PRESS', "ctrl": True}, None),
("wm.save_mainfile", {"type": 'S', "value": 'PRESS', "ctrl": True}, None),
("wm.save_as_mainfile", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("wm.save_mainfile", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, {"properties": [("incremental", True)]}),
("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
# Quick menu and toolbar
@ -4555,7 +4556,7 @@ def km_pose(params):
("armature.layers_show_all", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True}, None),
("armature.armature_layers", {"type": 'M', "value": 'PRESS', "shift": True}, None),
("pose.bone_layers", {"type": 'M', "value": 'PRESS'}, None),
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS'}, None),
("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None),
("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
@ -5484,7 +5485,7 @@ def km_armature(params):
("armature.bone_layers", {"type": 'M', "value": 'PRESS'}, None),
# Special transforms.
op_tool_optional(
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
(op_tool_cycle, "builtin.bone_size"), params),
op_tool_optional(
("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True},

View File

@ -188,6 +188,7 @@ def km_window(params):
("wm.open_mainfile", {"type": 'O', "value": 'PRESS', "ctrl": True}, None),
("wm.save_mainfile", {"type": 'S', "value": 'PRESS', "ctrl": True}, None),
("wm.save_as_mainfile", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("wm.save_mainfile", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, {"properties": [("incremental", True)]}),
("wm.quit_blender", {"type": 'Q', "value": 'PRESS', "ctrl": True}, None),
# Quick menu and toolbar

View File

@ -283,6 +283,10 @@ class TOPBAR_MT_file(Menu):
layout.operator_context = 'EXEC_AREA' if context.blend_data.is_saved else 'INVOKE_AREA'
layout.operator("wm.save_mainfile", text="Save", icon='FILE_TICK')
sub = layout.row()
sub.enabled = context.blend_data.is_saved
Harley marked this conversation as resolved Outdated

Use enabled otherwise this is still clickable (which isn't expected for greyed out menu items).

Use `enabled` otherwise this is still clickable (which isn't expected for greyed out menu items).
sub.operator("wm.save_mainfile", text="Save Incremental").incremental = True
layout.operator_context = 'INVOKE_AREA'
layout.operator("wm.save_as_mainfile", text="Save As...")
layout.operator_context = 'INVOKE_AREA'

View File

@ -435,6 +435,15 @@ void BLI_str_rstrip(char *str) ATTR_NONNULL(1);
*/
int BLI_str_rstrip_float_zero(char *str, char pad) ATTR_NONNULL(1);
/**
* Strip trailing digits.
* ABC123 -> ABC
*
* \param str:
* \return The number of digits stripped.
*/
int BLI_str_rstrip_digits(char *str) ATTR_NONNULL();
/**
* Return index of a string in a string array.
*

View File

@ -1057,6 +1057,17 @@ int BLI_str_rstrip_float_zero(char *str, const char pad)
return totstrip;
}
int BLI_str_rstrip_digits(char *str)
{
int totstrip = 0;
int str_len = strlen(str);
while (str_len > 0 && isdigit(str[--str_len])) {
str[str_len] = '\0';
totstrip++;
}
return totstrip;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -3257,6 +3257,31 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
if (RNA_boolean_get(op->ptr, "incremental")) {
char head[FILE_MAXFILE], tail[FILE_MAXFILE];
ushort digits;
int num = BLI_path_sequence_decode(filepath, head, sizeof(head), tail, sizeof(tail), &digits);
/* Numbers greater than INT_MAX return 0, resulting in always appending "1" to the name. */
if (num == 0 && digits == 0) {
/* This does nothing if there are no numbers at the end of the head. */
BLI_str_rstrip_digits(head);
}
const int tries_limit = 1000;
int tries = 0;
bool in_use = true;
do {
num++;
tries++;
BLI_path_sequence_encode(filepath, sizeof(filepath), head, tail, digits, num);
in_use = BLI_exists(filepath);
} while (in_use && tries < tries_limit && num < INT_MAX);
if (in_use) {
BKE_report(op->reports, RPT_ERROR, "Unable to find an available incremented file name");
return OPERATOR_CANCELLED;
}
}
const int fileflags_orig = G.fileflags;
int fileflags = G.fileflags;
@ -3391,6 +3416,18 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *
return ret;
}
static char *wm_save_mainfile_get_description(bContext * /*C*/,
wmOperatorType * /*ot*/,
PointerRNA *ptr)
{
if (RNA_boolean_get(ptr, "incremental")) {
return BLI_strdup(
TIP_("Save the current Blender file with a numerically incremented name that does not "
"overwrite any existing files"));
}
return nullptr;
}
void WM_OT_save_mainfile(wmOperatorType *ot)
{
ot->name = "Save Blender File";
Harley marked this conversation as resolved Outdated

Again, doesn't mention incrementing doesn't overwrite.

Again, doesn't mention incrementing doesn't overwrite.
@ -3419,6 +3456,14 @@ void WM_OT_save_mainfile(wmOperatorType *ot)
prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "Exit Blender after saving");
RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE));
prop = RNA_def_boolean(ot->srna,
"incremental",
false,
"Incremental",
"Save the current Blender file with a numerically incremented name that "
"does not overwrite any existing files");
RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE));
}
/** \} */