From 284e59d608a90589956780fe1dd81ecc2ba11362 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 15 Mar 2013 14:32:29 +0000 Subject: Add the possibility to define the translation context for py rna classes (operators, panels and menus). Thanks to Campell and Brecht for the reviews! --- source/blender/blenfont/BLF_translation.h | 2 + source/blender/blenfont/intern/blf_translation.c | 2 +- source/blender/blenkernel/BKE_screen.h | 10 +++-- .../blender/editors/interface/interface_layout.c | 2 +- source/blender/editors/interface/interface_panel.c | 4 +- source/blender/makesrna/intern/rna_ui.c | 21 +++++++++- source/blender/makesrna/intern/rna_wm.c | 49 ++++++++++++++++++++-- source/blender/python/intern/bpy_operator_wrap.c | 10 +++++ source/blender/python/intern/bpy_rna.c | 2 +- source/blender/windowmanager/WM_types.h | 1 + source/blender/windowmanager/intern/wm_operators.c | 4 +- 11 files changed, 92 insertions(+), 15 deletions(-) (limited to 'source') diff --git a/source/blender/blenfont/BLF_translation.h b/source/blender/blenfont/BLF_translation.h index 2f40cc3f571..db449995b8f 100644 --- a/source/blender/blenfont/BLF_translation.h +++ b/source/blender/blenfont/BLF_translation.h @@ -110,12 +110,14 @@ const char *BLF_translate_do_tooltip(const char *msgctxt, const char *msgid); * All i18n contexts must be defined here. * This is a nice way to be sure not to use a context twice for different * things, and limit the number of existing contexts! + * WARNING! Contexts should not be longer than BKE_ST_MAXNAME - 1! */ /* Default, void context. * WARNING! The "" context is not the same as no (NULL) context at mo/boost::locale level! * NOTE: We translate BLF_I18NCONTEXT_DEFAULT as BLF_I18NCONTEXT_DEFAULT_BPY in Python, as we can't use "natural" * None value in rna string properties... :/ + * The void string "" is also interpreted as BLF_I18NCONTEXT_DEFAULT. * For perf reason, we only use the first char to detect this context, so other contexts should never start * with the same char! */ diff --git a/source/blender/blenfont/intern/blf_translation.c b/source/blender/blenfont/intern/blf_translation.c index 2483c59d3ec..4e9408c512a 100644 --- a/source/blender/blenfont/intern/blf_translation.c +++ b/source/blender/blenfont/intern/blf_translation.c @@ -132,7 +132,7 @@ const char *BLF_pgettext(const char *msgctxt, const char *msgid) if (msgid && msgid[0]) { /*if (msgctxt && !strcmp(msgctxt, BLF_I18NCONTEXT_DEFAULT_BPY_INTERN)) { */ - if (msgctxt && msgctxt[0] == BLF_I18NCONTEXT_DEFAULT_BPY[0]) { + if (msgctxt && (!msgctxt[0] || msgctxt[0] == BLF_I18NCONTEXT_DEFAULT_BPY[0])) { /* BLF_I18NCONTEXT_DEFAULT_BPY context is reserved and considered the same as default NULL one. */ msgctxt = BLF_I18NCONTEXT_DEFAULT; } diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 629acab9e34..f1a169c4fac 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -163,9 +163,10 @@ typedef struct ARegionType { typedef struct PanelType { struct PanelType *next, *prev; - char idname[BKE_ST_MAXNAME]; /* unique name */ - char label[BKE_ST_MAXNAME]; /* for panel header */ - char context[BKE_ST_MAXNAME]; /* for buttons window */ + char idname[BKE_ST_MAXNAME]; /* unique name */ + char label[BKE_ST_MAXNAME]; /* for panel header */ + char translation_context[BKE_ST_MAXNAME]; + char context[BKE_ST_MAXNAME]; /* for buttons window */ int space_type; int region_type; @@ -227,7 +228,8 @@ typedef struct MenuType { char idname[BKE_ST_MAXNAME]; /* unique name */ char label[BKE_ST_MAXNAME]; /* for button text */ - char *description; + char translation_context[BKE_ST_MAXNAME]; + char *description; /* verify if the menu should draw or not */ int (*poll)(const struct bContext *, struct MenuType *); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 1e95b4df762..ea6ecadd924 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -1567,7 +1567,7 @@ void uiItemM(uiLayout *layout, bContext *UNUSED(C), const char *menuname, const } if (!name) { - name = IFACE_(mt->label); + name = CTX_IFACE_(mt->translation_context, mt->label); } if (layout->root->type == UI_LAYOUT_MENU && !icon) diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index e466c481151..a741ea432a5 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -191,7 +191,7 @@ static void ui_panel_copy_offset(Panel *pa, Panel *papar) Panel *uiBeginPanel(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, int *open) { Panel *pa, *patab, *palast, *panext; - char *drawname = pt->label; + const char *drawname = CTX_IFACE_(pt->translation_context, pt->label); char *idname = pt->idname; char *tabname = pt->idname; char *hookname = NULL; @@ -469,7 +469,7 @@ static void ui_draw_aligned_panel_header(uiStyle *style, uiBlock *block, rcti *r Panel *panel = block->panel; rcti hrect; int pnl_icons; - const char *activename = IFACE_(panel->drawname[0] ? panel->drawname : panel->panelname); + const char *activename = panel->drawname[0] ? panel->drawname : panel->panelname; /* + 0.001f to avoid flirting with float inaccuracy */ if (panel->control & UI_PNL_CLOSE) pnl_icons = (panel->labelofs + 2 * PNL_ICON + 5) / block->aspect + 0.001f; diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index e5585b4f72d..05056574a71 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -29,6 +29,8 @@ #include "DNA_screen_types.h" +#include "BLF_translation.h" + #include "RNA_define.h" #include "rna_internal.h" @@ -226,6 +228,7 @@ static StructRNA *rna_Panel_register(Main *bmain, ReportList *reports, void *dat memcpy(pt, &dummypt, sizeof(dummypt)); pt->ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, pt->idname, &RNA_Panel); + RNA_def_struct_translation_context(pt->ext.srna, pt->translation_context); pt->ext.data = data; pt->ext.call = call; pt->ext.free = free; @@ -573,6 +576,7 @@ static StructRNA *rna_Menu_register(Main *bmain, ReportList *reports, void *data } mt->ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, mt->idname, &RNA_Menu); + RNA_def_struct_translation_context(mt->ext.srna, mt->translation_context); mt->ext.data = data; mt->ext.call = call; mt->ext.free = free; @@ -773,6 +777,7 @@ static void rna_def_panel(BlenderRNA *brna) RNA_def_struct_sdna(srna, "Panel"); RNA_def_struct_refine_func(srna, "rna_Panel_refine"); RNA_def_struct_register_funcs(srna, "rna_Panel_register", "rna_Panel_unregister", NULL); + RNA_def_struct_translation_context(srna, BLF_I18NCONTEXT_DEFAULT); /* poll */ func = RNA_def_function(srna, "poll", NULL); @@ -819,7 +824,13 @@ static void rna_def_panel(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Label", "The panel label, shows up in the panel header at the right of the " "triangle used to collapse the panel"); - + + prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "type->translation_context"); + RNA_def_property_string_default(prop, BLF_I18NCONTEXT_DEFAULT); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_define_verify_sdna(TRUE); + prop = RNA_def_property(srna, "bl_space_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "type->space_type"); RNA_def_property_enum_items(prop, space_type_items); @@ -956,6 +967,7 @@ static void rna_def_menu(BlenderRNA *brna) RNA_def_struct_sdna(srna, "Menu"); RNA_def_struct_refine_func(srna, "rna_Menu_refine"); RNA_def_struct_register_funcs(srna, "rna_Menu_register", "rna_Menu_unregister", NULL); + RNA_def_struct_translation_context(srna, BLF_I18NCONTEXT_DEFAULT); /* poll */ func = RNA_def_function(srna, "poll", NULL); @@ -972,7 +984,7 @@ static void rna_def_menu(BlenderRNA *brna) parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_property_flag(parm, PROP_REQUIRED); - RNA_define_verify_sdna(0); /* not in sdna */ + RNA_define_verify_sdna(FALSE); /* not in sdna */ prop = RNA_def_property(srna, "layout", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "layout"); @@ -994,6 +1006,11 @@ static void rna_def_menu(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_REGISTER); RNA_def_property_ui_text(prop, "Label", "The menu label"); + prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "type->translation_context"); + RNA_def_property_string_default(prop, BLF_I18NCONTEXT_DEFAULT); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->description"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 9a160d8bb1e..0dd95d17260 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -33,6 +33,8 @@ #include "BLI_utildefines.h" +#include "BLF_translation.h" + #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -1050,6 +1052,7 @@ void macro_wrapper(wmOperatorType *ot, void *userdata); static char _operator_idname[OP_MAX_TYPENAME]; static char _operator_name[OP_MAX_TYPENAME]; static char _operator_descr[RNA_DYN_DESCR_MAX]; +static char _operator_ctxt[RNA_DYN_DESCR_MAX]; static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void *data, const char *identifier, StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free) { @@ -1063,10 +1066,11 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void * dummyot.idname = _operator_idname; /* only assigne the pointer, string is NULL'd */ dummyot.name = _operator_name; /* only assigne the pointer, string is NULL'd */ dummyot.description = _operator_descr; /* only assigne the pointer, string is NULL'd */ + dummyot.translation_context = _operator_ctxt; /* only assigne the pointer, string is NULL'd */ RNA_pointer_create(NULL, &RNA_Operator, &dummyop, &dummyotr); /* clear in case they are left unset */ - _operator_idname[0] = _operator_name[0] = _operator_descr[0] = '\0'; + _operator_idname[0] = _operator_name[0] = _operator_descr[0] = _operator_ctxt[0] = '\0'; /* validate the python class */ if (validate(&dummyotr, data, have_function) != 0) @@ -1117,9 +1121,10 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void * int idlen = strlen(_operator_idname) + 4; int namelen = strlen(_operator_name) + 1; int desclen = strlen(_operator_descr) + 1; + int ctxtlen = strlen(_operator_ctxt) + 1; char *ch; /* 2 terminators and 3 to convert a.b -> A_OT_b */ - ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen), "_operator_idname"); + ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen), "_operator_idname"); WM_operator_bl_idname(ch, _operator_idname); /* convert the idname from python */ dummyot.idname = ch; ch += idlen; @@ -1128,6 +1133,9 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void * ch += namelen; strcpy(ch, _operator_descr); dummyot.description = ch; + ch += desclen; + strcpy(ch, _operator_ctxt); + dummyot.translation_context = ch; } } @@ -1144,6 +1152,7 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void * /* create a new operator type */ dummyot.ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, dummyot.idname, &RNA_Operator); RNA_def_struct_flag(dummyot.ext.srna, STRUCT_NO_IDPROPERTIES); /* operator properties are registered separately */ + RNA_def_struct_translation_context(dummyot.ext.srna, dummyot.translation_context); dummyot.ext.data = data; dummyot.ext.call = call; dummyot.ext.free = free; @@ -1182,6 +1191,7 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v dummyot.idname = _operator_idname; /* only assigne the pointer, string is NULL'd */ dummyot.name = _operator_name; /* only assigne the pointer, string is NULL'd */ dummyot.description = _operator_descr; /* only assigne the pointer, string is NULL'd */ + dummyot.translation_context = _operator_ctxt; /* only assigne the pointer, string is NULL'd */ RNA_pointer_create(NULL, &RNA_Macro, &dummyop, &dummyotr); /* validate the python class */ @@ -1193,9 +1203,10 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v int idlen = strlen(_operator_idname) + 4; int namelen = strlen(_operator_name) + 1; int desclen = strlen(_operator_descr) + 1; + int ctxtlen = strlen(_operator_ctxt) + 1; char *ch; /* 2 terminators and 3 to convert a.b -> A_OT_b */ - ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen), "_operator_idname"); + ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen), "_operator_idname"); WM_operator_bl_idname(ch, _operator_idname); /* convert the idname from python */ dummyot.idname = ch; ch += idlen; @@ -1204,6 +1215,9 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v ch += namelen; strcpy(ch, _operator_descr); dummyot.description = ch; + ch += desclen; + strcpy(ch, _operator_ctxt); + dummyot.translation_context = ch; } if (strlen(identifier) >= sizeof(dummyop.idname)) { @@ -1224,6 +1238,7 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v /* create a new operator type */ dummyot.ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, dummyot.idname, &RNA_Operator); + RNA_def_struct_translation_context(dummyot.ext.srna, dummyot.translation_context); dummyot.ext.data = data; dummyot.ext.call = call; dummyot.ext.free = free; @@ -1273,6 +1288,16 @@ static void rna_Operator_bl_label_set(PointerRNA *ptr, const char *value) assert(!"setting the bl_label on a non-builtin operator"); } +static void rna_Operator_bl_translation_context_set(PointerRNA *ptr, const char *value) +{ + wmOperator *data = (wmOperator *)(ptr->data); + char *str = (char *)data->type->translation_context; + if (!str[0]) + BLI_strncpy(str, value, RNA_DYN_DESCR_MAX); /* utf8 already ensured */ + else + assert(!"setting the bl_translation_context on a non-builtin operator"); +} + static void rna_Operator_bl_description_set(PointerRNA *ptr, const char *value) { wmOperator *data = (wmOperator *)(ptr->data); @@ -1303,6 +1328,7 @@ static void rna_def_operator(BlenderRNA *brna) #ifdef WITH_PYTHON RNA_def_struct_register_funcs(srna, "rna_Operator_register", "rna_Operator_unregister", "rna_Operator_instance"); #endif + RNA_def_struct_translation_context(srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -1341,6 +1367,14 @@ static void rna_def_operator(BlenderRNA *brna) /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ RNA_def_property_flag(prop, PROP_REGISTER); + prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "type->translation_context"); + RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_translation_context_set"); + RNA_def_property_string_default(prop, BLF_I18NCONTEXT_OPERATOR_DEFAULT); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */ + prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->description"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ @@ -1377,6 +1411,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) RNA_def_struct_register_funcs(srna, "rna_MacroOperator_register", "rna_Operator_unregister", "rna_Operator_instance"); #endif + RNA_def_struct_translation_context(srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -1405,6 +1440,14 @@ static void rna_def_macro_operator(BlenderRNA *brna) /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ RNA_def_property_flag(prop, PROP_REGISTER); + prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "type->translation_context"); + RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_translation_context_set"); + RNA_def_property_string_default(prop, BLF_I18NCONTEXT_OPERATOR_DEFAULT); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */ + prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->description"); RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index 9d92ff51213..1c722243424 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -127,6 +127,11 @@ void operator_wrapper(wmOperatorType *ot, void *userdata) *ot = *((wmOperatorType *)userdata); ot->srna = srna; /* restore */ + /* Use i18n context from ext.srna if possible (py operators). */ + if (ot->ext.srna) { + RNA_def_struct_translation_context(ot->srna, RNA_struct_translation_context(ot->ext.srna)); + } + operator_properties_init(ot); } @@ -143,6 +148,11 @@ void macro_wrapper(wmOperatorType *ot, void *userdata) ot->ui = data->ui; ot->ext = data->ext; + /* Use i18n context from ext.srna if possible (py operators). */ + if (ot->ext.srna) { + RNA_def_struct_translation_context(ot->srna, RNA_struct_translation_context(ot->ext.srna)); + } + operator_properties_init(ot); } diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index bdb2f8fcc12..0adb5e2e90e 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -6950,7 +6950,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v } \ } \ Py_XDECREF(item); \ - } /* intendionally allow else here */ + } /* intentionally allow else here */ if BPY_REPLACEMENT_STRING("bl_idname", bpy_intern_str___name__) else if BPY_REPLACEMENT_STRING("bl_description", bpy_intern_str___doc__) diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index f2750aa6d99..5c2aae11a60 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -512,6 +512,7 @@ typedef struct wmTimer { typedef struct wmOperatorType { const char *name; /* text for ui, undo */ const char *idname; /* unique identifier */ + const char *translation_context; const char *description; /* tooltips and python docs */ /* this callback executes the operator without any interactive input, diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index cb2caa52d15..8ea507f97e8 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -381,7 +381,9 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *nam RNA_def_struct_ui_text(ot->srna, ot->name, ot->description); RNA_def_struct_identifier(ot->srna, ot->idname); - RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT); + /* Use i18n context from ext.srna if possible (py operators). */ + RNA_def_struct_translation_context(ot->srna, ot->ext.srna ? RNA_struct_translation_context(ot->ext.srna) : + BLF_I18NCONTEXT_OPERATOR_DEFAULT); BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot); -- cgit v1.2.3