Report/confirm overwrite of 'asset library' blendfiles. #117346

Merged
Bastien Montagne merged 5 commits from mont29/blender:bap-open-warning into brush-assets-project 2024-01-25 17:10:39 +01:00
7 changed files with 226 additions and 107 deletions

View File

@ -19,6 +19,11 @@ struct ReportList;
struct UserDef;
struct WorkspaceConfigFileData;
/**
* The suffix used for blendfiles managed by the asset system.
*/
#define BLENDER_ASSET_FILE_SUFFIX ".asset.blend"
/**
* Check whether given path ends with a blend file compatible extension
* (`.blend`, `.ble` or `.blend.gz`).

View File

@ -142,6 +142,14 @@ struct Main {
* could try to use more refined detection on load. */
bool has_forward_compatibility_issues;
/**
* The currently opened .blend file was created as an asset library storage.
*
* This is used to warn the user when they try to save it from Blender UI, since this will likely
* break the automatic management from the asset library system.
*/
bool is_asset_repository;
/** Commit timestamp from `buildinfo`. */
uint64_t build_commit_timestamp;
/** Commit Hash from `buildinfo`. */
@ -299,6 +307,17 @@ void BKE_main_merge(Main *bmain_dst, Main **r_bmain_src, MainMergeReport &report
*/
bool BKE_main_is_empty(Main *bmain);
/**
* Check whether the bmain has issues, e.g. for reporting in the status bar.
*/
bool BKE_main_has_issues(const Main *bmain);
/**
* Check whether user confirmation should be required when overwriting this `bmain` into its source
* blendfile.
*/
bool BKE_main_needs_overwrite_confirm(const Main *bmain);
void BKE_main_lock(Main *bmain);
void BKE_main_unlock(Main *bmain);

View File

@ -853,6 +853,9 @@ static void setup_app_data(bContext *C,
* nullptr curscreen)... */
else if (ELEM(nullptr, bfd->curscreen, bfd->curscene)) {
BKE_report(reports->reports, RPT_WARNING, "Library file, loading empty scene");
if (blender::StringRefNull(bfd->main->filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) {
bfd->main->is_asset_repository = true;
}
mode = LOAD_UI_OFF;
}
else if (G.fileflags & G_FILE_NO_UI) {

View File

@ -444,6 +444,16 @@ bool BKE_main_is_empty(Main *bmain)
return result;
}
bool BKE_main_has_issues(const Main *bmain)
{
return bmain->has_forward_compatibility_issues || bmain->is_asset_repository;
}
bool BKE_main_needs_overwrite_confirm(const Main *bmain)
{
return bmain->has_forward_compatibility_issues || bmain->is_asset_repository;
}
void BKE_main_lock(Main *bmain)
{
BLI_spin_lock((SpinLock *)bmain->lock);

View File

@ -6558,13 +6558,47 @@ void uiTemplateInputStatus(uiLayout *layout, bContext *C)
}
}
static void ui_template_status_info_warnings_messages(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
std::string &warning_message,
std::string &regular_message,
std::string &tooltip_message)
{
tooltip_message = "";
char statusbar_info_flag = U.statusbar_flag;
if (bmain->has_forward_compatibility_issues) {
warning_message = ED_info_statusbar_string_ex(
bmain, scene, view_layer, STATUSBAR_SHOW_VERSION);
statusbar_info_flag &= ~STATUSBAR_SHOW_VERSION;
char writer_ver_str[12];
BKE_blender_version_blendfile_string_from_values(
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
tooltip_message += fmt::format(RPT_("File saved by newer Blender\n({}), expect loss of data"),
writer_ver_str);
}
if (bmain->is_asset_repository) {
if (!tooltip_message.empty()) {
tooltip_message += "\n\n";
}
tooltip_message += RPT_(
"This file is managed by the Blender asset system\n"
mont29 marked this conversation as resolved Outdated

I would use the same text as the confirmation popup, just replace "overwriting" by "editing". The message to convey is the same.

This file is managed by the Blender asset system
By editing it as a regular blend file, it will no longer
be possible to update its assets through the asset browser
I would use the same text as the confirmation popup, just replace "overwriting" by "editing". The message to convey is the same. ``` This file is managed by the Blender asset system By editing it as a regular blend file, it will no longer be possible to update its assets through the asset browser ```
"By editing it as a regular blend file, it will no longer\n"
"be possible to update its assets through the asset browser");
}
regular_message = ED_info_statusbar_string_ex(bmain, scene, view_layer, statusbar_info_flag);
}
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) {
if (!BKE_main_has_issues(bmain)) {
const char *status_info_txt = ED_info_statusbar_string(bmain, scene, view_layer);
uiItemL(layout, status_info_txt, ICON_NONE);
return;
@ -6573,13 +6607,13 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
/* 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);
std::string warning_message;
std::string regular_message;
std::string tooltip_message;
ui_template_status_info_warnings_messages(
bmain, scene, view_layer, warning_message, regular_message, tooltip_message);
status_info_txt = ED_info_statusbar_string_ex(bmain, scene, view_layer, STATUSBAR_SHOW_VERSION);
uiBut *but;
uiItemL(layout, regular_message.c_str(), ICON_NONE);
const uiStyle *style = UI_style_get();
uiLayout *ui_abs = uiLayoutAbsolute(layout, false);
@ -6587,27 +6621,28 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
eUIEmbossType previous_emboss = UI_block_emboss_get(block);
UI_fontstyle_set(&style->widgetlabel);
int width = int(
BLF_width(style->widgetlabel.uifont_id, status_info_txt, strlen(status_info_txt)));
width = max_ii(width, int(10 * UI_SCALE_FAC));
const int width = max_ii(int(BLF_width(style->widgetlabel.uifont_id,
warning_message.c_str(),
warning_message.length())),
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,
"");
uiBut *but = uiDefBut(block,
UI_BTYPE_ROUNDBOX,
0,
"",
0,
0,
UI_UNIT_X + (6 * UI_SCALE_FAC),
UI_UNIT_Y,
nullptr,
0.0f,
0.0f,
0,
0,
"");
/* UI_BTYPE_ROUNDBOX's bg color is set in but->col. */
UI_GetThemeColorType4ubv(TH_INFO_WARNING, SPACE_INFO, but->col);
@ -6634,14 +6669,13 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
UI_block_align_end(block);
UI_block_emboss_set(block, UI_EMBOSS_NONE);
/* The report icon itself. */
static char compat_error_msg[256];
char writer_ver_str[12];
BKE_blender_version_blendfile_string_from_values(
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
SNPRINTF(compat_error_msg,
RPT_("File saved by newer Blender\n(%s), expect loss of data"),
writer_ver_str);
/* Tool tips have to be static currently.
* FIXME This is a horrible requirement from uiBut, should probably just store an std::string for
* the tooltip as well? */
static char tooltip_static_storage[256];
BLI_strncpy(tooltip_static_storage, tooltip_message.c_str(), sizeof(tooltip_static_storage));
/* The warning icon itself. */
but = uiDefIconBut(block,
UI_BTYPE_BUT,
0,
@ -6655,25 +6689,27 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
0.0f,
0.0f,
0.0f,
compat_error_msg);
tooltip_static_storage);
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);
/* The warning message, if any. */
if (!warning_message.empty()) {
but = uiDefBut(block,
UI_BTYPE_BUT,
0,
warning_message.c_str(),
UI_UNIT_X,
0,
short(width + UI_UNIT_X),
UI_UNIT_Y,
nullptr,
0.0f,
0.0f,
0.0f,
0.0f,
tooltip_static_storage);
}
UI_block_emboss_set(block, previous_emboss);
}

View File

@ -3552,8 +3552,8 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *
}
if (blendfile_path[0] != '\0') {
if (CTX_data_main(C)->has_forward_compatibility_issues) {
wm_save_file_forwardcompat_dialog(C, op);
if (BKE_main_needs_overwrite_confirm(CTX_data_main(C))) {
wm_save_file_overwrite_dialog(C, op);
ret = OPERATOR_INTERFACE;
}
else {
@ -3907,49 +3907,62 @@ static void wm_free_operator_properties_callback(void *user_data)
IDP_FreeProperty(properties);
}
static const char *save_file_forwardcompat_dialog_name = "save_file_forwardcompat_popup";
static const char *save_file_overwrite_dialog_name = "save_file_overwrite_popup";
static void file_forwardcompat_detailed_info_show(uiLayout *parent_layout, Main *bmain)
static void file_overwrite_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);
if (bmain->has_forward_compatibility_issues) {
char writer_ver_str[16];
char current_ver_str[16];
if (bmain->versionfile == BLENDER_VERSION) {
BKE_blender_version_blendfile_string_from_values(
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile);
BKE_blender_version_blendfile_string_from_values(
current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION);
}
else {
BKE_blender_version_blendfile_string_from_values(
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
BKE_blender_version_blendfile_string_from_values(
current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1);
}
char message_line1[256];
char message_line2[256];
SNPRINTF(message_line1,
RPT_("This file was saved by a newer version of Blender (%s)"),
writer_ver_str);
SNPRINTF(message_line2,
RPT_("Saving it with this Blender (%s) may cause loss of data"),
current_ver_str);
uiItemL(layout, message_line1, ICON_NONE);
uiItemL(layout, message_line2, ICON_NONE);
}
char message_line1[256];
char message_line2[256];
SNPRINTF(message_line1,
RPT_("This file was saved by a newer version of Blender (%s)"),
writer_ver_str);
SNPRINTF(message_line2,
RPT_("Saving it with this Blender (%s) may cause loss of data"),
current_ver_str);
uiItemL(layout, message_line1, ICON_NONE);
uiItemL(layout, message_line2, ICON_NONE);
if (bmain->is_asset_repository) {
if (bmain->has_forward_compatibility_issues) {
uiItemS_ex(layout, 1.4f);
}
uiItemL(layout, RPT_("This file is managed by the Blender asset system"), ICON_NONE);
uiItemL(
layout, RPT_("By overwriting it as a regular blend file, it will no longer "), ICON_NONE);
uiItemL(layout, RPT_("be possible to update its assets through the asset browser"), ICON_NONE);
}
}
static void save_file_forwardcompat_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
static void save_file_overwrite_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)
static void save_file_overwrite_cancel_button(uiBlock *block, wmGenericCallback *post_action)
{
uiBut *but = uiDefIconTextBut(block,
UI_BTYPE_BUT,
@ -3966,11 +3979,11 @@ static void save_file_forwardcompat_cancel_button(uiBlock *block, wmGenericCallb
0,
0,
"");
UI_but_func_set(but, save_file_forwardcompat_cancel, block, post_action);
UI_but_func_set(but, save_file_overwrite_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)
static void save_file_overwrite_confirm(bContext *C, void *arg_block, void *arg_data)
{
wmWindow *win = CTX_wm_window(C);
@ -3993,8 +4006,7 @@ static void save_file_forwardcompat_overwrite(bContext *C, void *arg_block, void
WM_generic_callback_free(callback);
}
static void save_file_forwardcompat_overwrite_button(uiBlock *block,
wmGenericCallback *post_action)
static void save_file_overwrite_confirm_button(uiBlock *block, wmGenericCallback *post_action)
{
uiBut *but = uiDefIconTextBut(block,
UI_BTYPE_BUT,
@ -4011,20 +4023,37 @@ static void save_file_forwardcompat_overwrite_button(uiBlock *block,
0,
0,
"");
UI_but_func_set(but, save_file_forwardcompat_overwrite, block, post_action);
UI_but_func_set(but, save_file_overwrite_confirm, 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*/)
static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*arg_data*/)
{
Main *bmain = CTX_data_main(C);
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);
PointerRNA props_ptr;
wmOperatorType *ot = WM_operatortype_find("WM_OT_save_as_mainfile", false);
WM_operator_properties_create_ptr(&props_ptr, ot);
if (bmain->is_asset_repository) {
/* If needed, substitute the 'proposed' Save As filepath by replacing the `.asset.blend` part
* of it by just `.blend`. */
std::string filepath = BKE_main_blendfile_path(bmain);
if (blender::StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) {
filepath.replace(
filepath.rfind(BLENDER_ASSET_FILE_SUFFIX), strlen(BLENDER_ASSET_FILE_SUFFIX), ".blend");
RNA_string_set(&props_ptr, "filepath", filepath.c_str());
}
}
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr);
WM_operator_properties_free(&props_ptr);
}
static void save_file_forwardcompat_saveas_button(uiBlock *block, wmGenericCallback *post_action)
static void save_file_overwrite_saveas_button(uiBlock *block, wmGenericCallback *post_action)
{
uiBut *but = uiDefIconTextBut(block,
UI_BTYPE_BUT,
@ -4041,19 +4070,17 @@ static void save_file_forwardcompat_saveas_button(uiBlock *block, wmGenericCallb
0,
0,
"");
UI_but_func_set(but, save_file_forwardcompat_saveas, block, post_action);
UI_but_func_set(but, save_file_overwrite_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)
static uiBlock *block_create_save_file_overwrite_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);
uiBlock *block = UI_block_begin(C, region, save_file_overwrite_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);
@ -4061,8 +4088,27 @@ static uiBlock *block_create_save_file_forwardcompat_dialog(bContext *C,
uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_WARNING);
/* Title. */
uiItemL_ex(
layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
if (bmain->has_forward_compatibility_issues) {
if (bmain->is_asset_repository) {
uiItemL_ex(
layout,
RPT_("Convert asset blend file to regular blend file with an older Blender version?"),
ICON_NONE,
true,
false);
}
else {
uiItemL_ex(
layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
}
}
else if (bmain->is_asset_repository) {
uiItemL_ex(
layout, RPT_("Convert asset blend file to regular blend file?"), ICON_NONE, true, false);
}
else {
BLI_assert_unreachable();
}
/* Filename. */
const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
@ -4079,7 +4125,7 @@ static uiBlock *block_create_save_file_forwardcompat_dialog(bContext *C,
uiItemL(layout, filename, ICON_NONE);
/* Detailed message info. */
file_forwardcompat_detailed_info_show(layout, bmain);
file_overwrite_detailed_info_show(layout, bmain);
uiItemS_ex(layout, 4.0f);
@ -4089,7 +4135,7 @@ static uiBlock *block_create_save_file_forwardcompat_dialog(bContext *C,
uiLayoutSetScaleY(split, 1.2f);
uiLayoutColumn(split, false);
save_file_forwardcompat_overwrite_button(block, post_action);
save_file_overwrite_confirm_button(block, post_action);
uiLayout *split_right = uiLayoutSplit(split, 0.1f, true);
@ -4097,25 +4143,25 @@ static uiBlock *block_create_save_file_forwardcompat_dialog(bContext *C,
/* Empty space. */
uiLayoutColumn(split_right, false);
save_file_forwardcompat_cancel_button(block, post_action);
save_file_overwrite_cancel_button(block, post_action);
uiLayoutColumn(split_right, false);
save_file_forwardcompat_saveas_button(block, post_action);
save_file_overwrite_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)
void wm_save_file_overwrite_dialog(bContext *C, wmOperator *op)
{
if (!UI_popup_block_name_exists(CTX_wm_screen(C), save_file_forwardcompat_dialog_name)) {
if (!UI_popup_block_name_exists(CTX_wm_screen(C), save_file_overwrite_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);
C, block_create_save_file_overwrite_dialog, callback, free_post_file_close_action);
}
}
@ -4245,7 +4291,7 @@ static void wm_block_file_close_discard_button(uiBlock *block, wmGenericCallback
static void wm_block_file_close_save_button(uiBlock *block,
wmGenericCallback *post_action,
const bool has_forwardcompat_issues)
const bool needs_overwrite_confirm)
{
uiBut *but = uiDefIconTextBut(
block,
@ -4253,7 +4299,7 @@ static void wm_block_file_close_save_button(uiBlock *block,
0,
ICON_NONE,
/* Forward compatibility issues force using 'save as' operator instead of 'save' one. */
has_forwardcompat_issues ? IFACE_("Save As...") : IFACE_("Save"),
needs_overwrite_confirm ? IFACE_("Save As...") : IFACE_("Save"),
0,
0,
0,
@ -4289,7 +4335,7 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo
uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION);
const bool has_forwardcompat_issues = bmain->has_forward_compatibility_issues;
const bool needs_overwrite_confirm = BKE_main_needs_overwrite_confirm(bmain);
/* Title. */
uiItemL_ex(layout, RPT_("Save changes before closing?"), ICON_NONE, true, false);
@ -4306,8 +4352,8 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo
uiItemL(layout, filename, ICON_NONE);
/* Potential forward compatibility issues message. */
if (has_forwardcompat_issues) {
file_forwardcompat_detailed_info_show(layout, bmain);
if (needs_overwrite_confirm) {
file_overwrite_detailed_info_show(layout, bmain);
}
/* Image Saving Warnings. */
@ -4416,7 +4462,7 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo
uiLayoutSetScaleY(split, 1.2f);
uiLayoutColumn(split, false);
wm_block_file_close_save_button(block, post_action, has_forwardcompat_issues);
wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm);
uiLayoutColumn(split, false);
wm_block_file_close_discard_button(block, post_action);
@ -4442,7 +4488,7 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo
wm_block_file_close_cancel_button(block, post_action);
uiLayoutColumn(split_right, false);
wm_block_file_close_save_button(block, post_action, has_forwardcompat_issues);
wm_block_file_close_save_button(block, post_action, needs_overwrite_confirm);
}
UI_block_bounds_set_centered(block, 14 * UI_SCALE_FAC);

View File

@ -97,7 +97,7 @@ bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWind
*
* 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_save_file_overwrite_dialog(bContext *C, wmOperator *op);
void WM_OT_save_homefile(wmOperatorType *ot);
void WM_OT_save_userpref(wmOperatorType *ot);