Merge branch 'xrc-text-escape'

Miscellaneous improvements for handling mnemonics in XRC.

See https://github.com/wxWidgets/wxWidgets/pull/667

Closes #18033.
This commit is contained in:
Vadim Zeitlin
2018-01-18 00:23:18 +01:00
15 changed files with 172 additions and 141 deletions

View File

@@ -1595,6 +1595,11 @@ can also have some optional XML @em attributes (not properties!):
Is the button enabled (default: 1)?} Is the button enabled (default: 1)?}
@row3col{hidden, @ref overview_xrcformat_type_bool, @row3col{hidden, @ref overview_xrcformat_type_bool,
Is the button hidden initially (default: 0)?} Is the button hidden initially (default: 0)?}
@row3col{label, @ref overview_xrcformat_type_bool,
Should this item text be interpreted as a label, i.e. escaping underscores
in it as done for the label properties of other controls? This attribute
exists since wxWidgets 3.1.1 and was always treated as having the value of
0, which still remains its default, until then.}
@endTable @endTable
Example: Example:
@@ -1611,7 +1616,7 @@ Example:
<item tooltip="E=mc^2">Energy 98.3</item> <item tooltip="E=mc^2">Energy 98.3</item>
<item helptext="Favourite chukcha's radio">CHUM FM</item> <item helptext="Favourite chukcha's radio">CHUM FM</item>
<item>92FM</item> <item>92FM</item>
<item hidden="1">Very quit station</item> <item hidden="1">Very quiet station</item>
</content> </content>
</object> </object>
@endcode @endcode

View File

@@ -513,7 +513,10 @@ public:
// - replaces \n, \r, \t by respective chars (according to C syntax) // - replaces \n, \r, \t by respective chars (according to C syntax)
// - replaces _ by & and __ by _ (needed for _File => &File because of XML) // - replaces _ by & and __ by _ (needed for _File => &File because of XML)
// - calls wxGetTranslations (unless disabled in wxXmlResource) // - calls wxGetTranslations (unless disabled in wxXmlResource)
wxString GetText(const wxString& param, bool translate = true) wxOVERRIDE; //
// The first two conversions can be disabled by using wxXRC_TEXT_NO_ESCAPE
// in flags and the last one -- by using wxXRC_TEXT_NO_TRANSLATE.
wxString GetNodeText(const wxXmlNode *node, int flags = 0) wxOVERRIDE;
// Returns the XRCID. // Returns the XRCID.
int GetID() wxOVERRIDE; int GetID() wxOVERRIDE;

View File

@@ -32,6 +32,13 @@ class WXDLLIMPEXP_FWD_CORE wxXmlResourceHandler;
// by wxXmlResourceHandler implementation itself. // by wxXmlResourceHandler implementation itself.
#define XRC_ADD_STYLE(style) AddStyle(wxT(#style), style) #define XRC_ADD_STYLE(style) AddStyle(wxT(#style), style)
// Flags for GetNodeText().
enum
{
wxXRC_TEXT_NO_TRANSLATE = 1,
wxXRC_TEXT_NO_ESCAPE = 2
};
// Abstract base class for the implementation object used by // Abstract base class for the implementation object used by
// wxXmlResourceHandlerImpl. The real implementation is in // wxXmlResourceHandlerImpl. The real implementation is in
// wxXmlResourceHandlerImpl class in the "xrc" library while this class is in // wxXmlResourceHandlerImpl class in the "xrc" library while this class is in
@@ -61,7 +68,7 @@ public:
virtual wxString GetParamValue(const wxString& param) = 0; virtual wxString GetParamValue(const wxString& param) = 0;
virtual wxString GetParamValue(const wxXmlNode* node) = 0; virtual wxString GetParamValue(const wxXmlNode* node) = 0;
virtual int GetStyle(const wxString& param = wxT("style"), int defaults = 0) = 0; virtual int GetStyle(const wxString& param = wxT("style"), int defaults = 0) = 0;
virtual wxString GetText(const wxString& param, bool translate = true) = 0; virtual wxString GetNodeText(const wxXmlNode *node, int flags = 0) = 0;
virtual int GetID() = 0; virtual int GetID() = 0;
virtual wxString GetName() = 0; virtual wxString GetName() = 0;
virtual bool GetBool(const wxString& param, bool defaultv = false) = 0; virtual bool GetBool(const wxString& param, bool defaultv = false) = 0;
@@ -254,9 +261,14 @@ protected:
{ {
return GetImpl()->GetStyle(param, defaults); return GetImpl()->GetStyle(param, defaults);
} }
wxString GetNodeText(const wxXmlNode *node, int flags = 0)
{
return GetImpl()->GetNodeText(node, flags);
}
wxString GetText(const wxString& param, bool translate = true) wxString GetText(const wxString& param, bool translate = true)
{ {
return GetImpl()->GetText(param, translate); return GetImpl()->GetNodeText(GetImpl()->GetParamNode(param),
translate ? 0 : wxXRC_TEXT_NO_TRANSLATE);
} }
int GetID() const int GetID() const
{ {

View File

@@ -1225,6 +1225,7 @@ wxRadioBox =
attribute helptext { t_string }?, attribute helptext { t_string }?,
attribute enabled { t_bool }?, attribute enabled { t_bool }?,
attribute hidden { t_bool }?, attribute hidden { t_bool }?,
attribute label { t_bool }?,
t_text t_text
}* }*
}? }?

View File

@@ -779,7 +779,7 @@ lay them out using wxSizers, absolute positioning, everything you like!
<item tooltip="E=mc^2">Energy 98.3</item> <item tooltip="E=mc^2">Energy 98.3</item>
<item helptext="Favourite chukcha's radio">CHUM FM</item> <item helptext="Favourite chukcha's radio">CHUM FM</item>
<item>92FM</item> <item>92FM</item>
<item hidden="1">Very quite station</item> <item hidden="1">Very quiet station</item>
</content> </content>
</object> </object>
</object> </object>

View File

@@ -94,10 +94,7 @@ wxObject *wxCheckListBoxXmlHandler::DoCreateResource()
// handle <item checked="boolean">Label</item> // handle <item checked="boolean">Label</item>
// add to the list // add to the list
wxString str = GetNodeContent(m_node); strList.Add(GetNodeText(m_node, wxXRC_TEXT_NO_ESCAPE));
if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
str = wxGetTranslation(str, m_resource->GetDomain());
strList.Add(str);
return NULL; return NULL;
} }
} }

View File

@@ -70,10 +70,7 @@ wxObject *wxChoiceXmlHandler::DoCreateResource()
// handle <item>Label</item> // handle <item>Label</item>
// add to the list // add to the list
wxString str = GetNodeContent(m_node); strList.Add(GetNodeText(m_node, wxXRC_TEXT_NO_ESCAPE));
if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
str = wxGetTranslation(str, m_resource->GetDomain());
strList.Add(str);
return NULL; return NULL;
} }

View File

@@ -77,10 +77,7 @@ wxObject *wxComboBoxXmlHandler::DoCreateResource()
// handle <item>Label</item> // handle <item>Label</item>
// add to the list // add to the list
wxString str = GetNodeContent(m_node); strList.Add(GetNodeText(m_node, wxXRC_TEXT_NO_ESCAPE));
if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
str = wxGetTranslation(str, m_resource->GetDomain());
strList.Add(str);
return NULL; return NULL;
} }

View File

@@ -99,10 +99,7 @@ wxObject *wxEditableListBoxXmlHandler::DoCreateResource()
} }
else if ( m_insideBox && m_node->GetName() == EDITLBOX_ITEM_NAME ) else if ( m_insideBox && m_node->GetName() == EDITLBOX_ITEM_NAME )
{ {
wxString str = GetNodeContent(m_node); m_items.push_back(GetNodeText(m_node, wxXRC_TEXT_NO_ESCAPE));
if ( m_resource->GetFlags() & wxXRC_USE_LOCALE )
str = wxGetTranslation(str, m_resource->GetDomain());
m_items.push_back(str);
return NULL; return NULL;
} }

View File

@@ -69,10 +69,7 @@ wxObject *wxSimpleHtmlListBoxXmlHandler::DoCreateResource()
// handle <item>Label</item> // handle <item>Label</item>
// add to the list // add to the list
wxString str = GetNodeContent(m_node); strList.Add(GetNodeText(m_node, wxXRC_TEXT_NO_ESCAPE));
if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
str = wxGetTranslation(str, m_resource->GetDomain());
strList.Add(str);
return NULL; return NULL;
} }

View File

@@ -77,10 +77,7 @@ wxObject *wxListBoxXmlHandler::DoCreateResource()
// handle <item>Label</item> // handle <item>Label</item>
// add to the list // add to the list
wxString str = GetNodeContent(m_node); strList.Add(GetNodeText(m_node, wxXRC_TEXT_NO_ESCAPE));
if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
str = wxGetTranslation(str, m_resource->GetDomain());
strList.Add(str);
return NULL; return NULL;
} }

View File

@@ -85,10 +85,7 @@ wxObject *wxOwnerDrawnComboBoxXmlHandler::DoCreateResource()
// handle <item>Label</item> // handle <item>Label</item>
// add to the list // add to the list
wxString str = GetNodeContent(m_node); strList.Add(GetNodeText(m_node, wxXRC_TEXT_NO_ESCAPE));
if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
str = wxGetTranslation(str, m_resource->GetDomain());
strList.Add(str);
return NULL; return NULL;
} }

View File

@@ -107,30 +107,21 @@ wxObject *wxRadioBoxXmlHandler::DoCreateResource()
// we handle handle <item>Label</item> constructs here, and the item // we handle handle <item>Label</item> constructs here, and the item
// tag can have tooltip, helptext, enabled and hidden attributes // tag can have tooltip, helptext, enabled and hidden attributes
wxString label = GetNodeContent(m_node); // For compatibility, labels are not escaped in XRC by default and
// label="1" attribute needs to be explicitly specified to handle them
wxString tooltip; // consistently with the other labels.
m_node->GetAttribute(wxT("tooltip"), &tooltip); m_labels.push_back(GetNodeText(m_node,
GetBoolAttr("label", 0)
wxString helptext; ? 0
bool hasHelptext = m_node->GetAttribute(wxT("helptext"), &helptext); : wxXRC_TEXT_NO_ESCAPE));
if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
{
label = wxGetTranslation(label, m_resource->GetDomain());
if ( !tooltip.empty() )
tooltip = wxGetTranslation(tooltip, m_resource->GetDomain());
if ( hasHelptext )
helptext = wxGetTranslation(helptext, m_resource->GetDomain());
}
m_labels.push_back(label);
#if wxUSE_TOOLTIPS #if wxUSE_TOOLTIPS
m_tooltips.push_back(tooltip); m_tooltips.push_back(GetNodeText(GetParamNode(wxT("tooltip")),
wxXRC_TEXT_NO_ESCAPE));
#endif // wxUSE_TOOLTIPS #endif // wxUSE_TOOLTIPS
#if wxUSE_HELP #if wxUSE_HELP
m_helptexts.push_back(helptext); const wxXmlNode* const nodeHelp = GetParamNode(wxT("helptext"));
m_helptextSpecified.push_back(hasHelptext); m_helptexts.push_back(GetNodeText(nodeHelp, wxXRC_TEXT_NO_ESCAPE));
m_helptextSpecified.push_back(nodeHelp != NULL);
#endif // wxUSE_HELP #endif // wxUSE_HELP
m_isEnabled.push_back(GetBoolAttr("enabled", 1)); m_isEnabled.push_back(GetBoolAttr("enabled", 1));
m_isShown.push_back(!GetBoolAttr("hidden", 0)); m_isShown.push_back(!GetBoolAttr("hidden", 0));

View File

@@ -1506,73 +1506,83 @@ int wxXmlResourceHandlerImpl::GetStyle(const wxString& param, int defaults)
wxString wxXmlResourceHandlerImpl::GetText(const wxString& param, bool translate) wxString wxXmlResourceHandlerImpl::GetNodeText(const wxXmlNode* node, int flags)
{ {
wxXmlNode *parNode = GetParamNode(param); wxString str1(GetNodeContent(node));
wxString str1(GetNodeContent(parNode)); if ( str1.empty() )
return str1;
wxString str2; wxString str2;
// "\\" wasn't translated to "\" prior to 2.5.3.0: if ( !(flags & wxXRC_TEXT_NO_ESCAPE) )
const bool escapeBackslash = (m_handler->m_resource->CompareVersion(2,5,3,0) >= 0);
// VS: First version of XRC resources used $ instead of & (which is
// illegal in XML), but later I realized that '_' fits this purpose
// much better (because &File means "File with F underlined").
const wxChar amp_char = (m_handler->m_resource->CompareVersion(2,3,0,1) < 0)
? '$' : '_';
for ( wxString::const_iterator dt = str1.begin(); dt != str1.end(); ++dt )
{ {
// Remap amp_char to &, map double amp_char to amp_char (for things // "\\" wasn't translated to "\" prior to 2.5.3.0:
// like "&File..." -- this is illegal in XML, so we use "_File..."): const bool escapeBackslash = (m_handler->m_resource->CompareVersion(2,5,3,0) >= 0);
if ( *dt == amp_char )
// VS: First version of XRC resources used $ instead of & (which is
// illegal in XML), but later I realized that '_' fits this purpose
// much better (because &File means "File with F underlined").
const wxChar amp_char = (m_handler->m_resource->CompareVersion(2,3,0,1) < 0)
? '$' : '_';
for ( wxString::const_iterator dt = str1.begin(); dt != str1.end(); ++dt )
{ {
if ( dt+1 == str1.end() || *(++dt) == amp_char ) // Remap amp_char to &, map double amp_char to amp_char (for things
str2 << amp_char; // like "&File..." -- this is illegal in XML, so we use "_File..."):
else if ( *dt == amp_char )
str2 << wxT('&') << *dt;
}
// Remap \n to CR, \r to LF, \t to TAB, \\ to \:
else if ( *dt == wxT('\\') )
{
switch ( (*(++dt)).GetValue() )
{ {
case wxT('n'): if ( dt+1 == str1.end() || *(++dt) == amp_char )
str2 << wxT('\n'); str2 << amp_char;
break; else
str2 << wxT('&') << *dt;
case wxT('t'): }
str2 << wxT('\t'); // Remap \n to CR, \r to LF, \t to TAB, \\ to \:
break; else if ( *dt == wxT('\\') )
{
case wxT('r'): switch ( (*(++dt)).GetValue() )
str2 << wxT('\r'); {
break; case wxT('n'):
str2 << wxT('\n');
case wxT('\\') :
// "\\" wasn't translated to "\" prior to 2.5.3.0:
if ( escapeBackslash )
{
str2 << wxT('\\');
break; break;
}
wxFALLTHROUGH;// else fall-through to default: branch below
default: case wxT('t'):
str2 << wxT('\\') << *dt; str2 << wxT('\t');
break; break;
case wxT('r'):
str2 << wxT('\r');
break;
case wxT('\\') :
// "\\" wasn't translated to "\" prior to 2.5.3.0:
if ( escapeBackslash )
{
str2 << wxT('\\');
break;
}
wxFALLTHROUGH;// else fall-through to default: branch below
default:
str2 << wxT('\\') << *dt;
break;
}
}
else
{
str2 << *dt;
} }
} }
else }
{ else // Don't escape anything in this string.
str2 << *dt; {
} // We won't use str1 at all, so move its contents to str2.
str2.swap(str1);
} }
if (m_handler->m_resource->GetFlags() & wxXRC_USE_LOCALE) if (m_handler->m_resource->GetFlags() & wxXRC_USE_LOCALE)
{ {
if (translate && parNode && if (!(flags & wxXRC_TEXT_NO_TRANSLATE) && node &&
parNode->GetAttribute(wxT("translate"), wxEmptyString) != wxT("0")) node->GetAttribute(wxT("translate"), wxEmptyString) != wxT("0"))
{ {
return wxGetTranslation(str2, m_handler->m_resource->GetDomain()); return wxGetTranslation(str2, m_handler->m_resource->GetDomain());
} }
@@ -2508,14 +2518,14 @@ void wxXmlResourceHandlerImpl::SetupWindow(wxWindow *wnd)
wnd->SetFocus(); wnd->SetFocus();
#if wxUSE_TOOLTIPS #if wxUSE_TOOLTIPS
if (HasParam(wxT("tooltip"))) if (HasParam(wxT("tooltip")))
wnd->SetToolTip(GetText(wxT("tooltip"))); wnd->SetToolTip(GetNodeText(GetParamNode(wxT("tooltip"))));
#endif #endif
if (HasParam(wxT("font"))) if (HasParam(wxT("font")))
wnd->SetFont(GetFont(wxT("font"), wnd)); wnd->SetFont(GetFont(wxT("font"), wnd));
if (HasParam(wxT("ownfont"))) if (HasParam(wxT("ownfont")))
wnd->SetOwnFont(GetFont(wxT("ownfont"), wnd)); wnd->SetOwnFont(GetFont(wxT("ownfont"), wnd));
if (HasParam(wxT("help"))) if (HasParam(wxT("help")))
wnd->SetHelpText(GetText(wxT("help"))); wnd->SetHelpText(GetNodeText(GetParamNode(wxT("help"))));
} }

View File

@@ -949,6 +949,51 @@ static wxString ConvertText(const wxString& str)
} }
enum ContentsKind
{
Contents_NotTrans, // Not a translatable text at all.
Contents_TransOnly, // Translatable but not escaped text.
Contents_Text // Text, i.e. both translatable and escaped.
};
// Check if the given node contains translatable text and, if it does, whether
// it's escaped (i.e. parsed using GetText()) or not.
ContentsKind
GetNodeContentsKind(wxXmlNode& node, const wxString& contents)
{
if ( node.GetName() == wxT("label") ||
(node.GetName() == wxT("value") && !contents.IsNumber()) ||
node.GetName() == wxT("help") ||
node.GetName() == wxT("hint") ||
node.GetName() == wxT("longhelp") ||
node.GetName() == wxT("tooltip") ||
node.GetName() == wxT("htmlcode") ||
node.GetName() == wxT("title") ||
node.GetName() == wxT("message") ||
node.GetName() == wxT("note") ||
node.GetName() == wxT("defaultdirectory") ||
node.GetName() == wxT("defaultfilename") ||
node.GetName() == wxT("defaultfolder") ||
node.GetName() == wxT("filter") ||
node.GetName() == wxT("caption") )
{
return Contents_Text;
}
// This one is special: it is translated in XRC, but its contents is not
// escaped, except for the special case of wxRadioBox when it can be, if
// "label" attribute is supplied.
if ( node.GetName() == wxT("item") )
{
return node.GetAttribute(wxT("label"), wxT("0")) == wxT("1")
? Contents_Text
: Contents_TransOnly;
}
return Contents_NotTrans;
}
ExtractedStrings ExtractedStrings
XmlResApp::FindStrings(const wxString& filename, wxXmlNode *node) XmlResApp::FindStrings(const wxString& filename, wxXmlNode *node)
{ {
@@ -963,41 +1008,26 @@ XmlResApp::FindStrings(const wxString& filename, wxXmlNode *node)
if ((node->GetType() == wxXML_ELEMENT_NODE) && if ((node->GetType() == wxXML_ELEMENT_NODE) &&
// parent is an element, i.e. has subnodes... // parent is an element, i.e. has subnodes...
(n->GetType() == wxXML_TEXT_NODE || (n->GetType() == wxXML_TEXT_NODE ||
n->GetType() == wxXML_CDATA_SECTION_NODE) && n->GetType() == wxXML_CDATA_SECTION_NODE))
// ...it is textnode... // ...it is textnode...
(
node/*not n!*/->GetName() == wxT("label") ||
(node/*not n!*/->GetName() == wxT("value") &&
!n->GetContent().IsNumber()) ||
node/*not n!*/->GetName() == wxT("help") ||
node/*not n!*/->GetName() == wxT("hint") ||
node/*not n!*/->GetName() == wxT("longhelp") ||
node/*not n!*/->GetName() == wxT("tooltip") ||
node/*not n!*/->GetName() == wxT("htmlcode") ||
node/*not n!*/->GetName() == wxT("title") ||
node/*not n!*/->GetName() == wxT("item") ||
node/*not n!*/->GetName() == wxT("message") ||
node/*not n!*/->GetName() == wxT("note") ||
node/*not n!*/->GetName() == wxT("defaultdirectory") ||
node/*not n!*/->GetName() == wxT("defaultfilename") ||
node/*not n!*/->GetName() == wxT("defaultfolder") ||
node/*not n!*/->GetName() == wxT("filter") ||
node/*not n!*/->GetName() == wxT("caption")
))
// ...and known to contain translatable string
{ {
if (!flagGettext || wxString s = n->GetContent();
node->GetAttribute(wxT("translate"), wxT("1")) != wxT("0")) switch ( GetNodeContentsKind(*node, s) )
{ {
arr.push_back case Contents_NotTrans:
( break;
ExtractedString
( case Contents_Text:
ConvertText(n->GetContent()), s = ConvertText(s);
filename, wxFALLTHROUGH;
n->GetLineNumber()
) case Contents_TransOnly:
); if (!flagGettext ||
node->GetAttribute(wxT("translate"), wxT("1")) != wxT("0"))
{
arr.push_back(ExtractedString(s, filename, n->GetLineNumber()));
}
break;
} }
} }