Don't split UTF8 when handling accelerators (#983)

Marking up accelerator keys still assumed all characters were one byte
wide. Change this to accept multi-byte characters.
heavy-resources
Nicolas Hake 2014-03-11 00:23:24 +01:00
parent adbe3c629b
commit 4a3ece5f5d
7 changed files with 63 additions and 41 deletions

View File

@ -35,35 +35,57 @@ namespace C4GUI
// --------------------------------------------------
// Generic helpers
bool ExpandHotkeyMarkup(StdStrBuf &sText, char &rcHotkey)
bool ExpandHotkeyMarkup(StdStrBuf &sText, uint32_t &rcHotkey)
{
const char *HotkeyMarkup = "<c ffffff7f>x</c>";
size_t iHotkeyMarkupLength = 17;
size_t iHotkeyMarkupHotkeyPos = 12;
static const char HotkeyMarkup[] = "<c ffffff7f>%s</c>";
int iHotkeyPos;
const char *szCheckText = sText.getData();
if (!szCheckText || (iHotkeyPos=SCharPos('&', szCheckText))<0)
StdStrBuf output;
const char *input = sText.getData();
rcHotkey = 0;
// Iterate over all input characters
while (input && *input)
{
// hotkey not available
rcHotkey = 0;
if (*input != '&')
{
// This will correctly copy UTF-8 chars too
output.AppendChar(*input++);
}
else
{
++input;
if (*input == '\0' || *input == '&')
{
// If the ampersand is followed by another ampersand, or it is the last character, copy it verbatimly
// Note: This means you can't use an ampersand as an accelerator key.
output.AppendChar(*input);
}
else
{
// Store the start of the hotkey so we can copy it later
const char *accel_start = input;
rcHotkey = GetNextCharacter(&input);
// Using std::string because StdStrBuf doesn't have a ctor from two iterators
std::string accel(accel_start, input);
output.AppendFormat(HotkeyMarkup, accel.c_str());
// Converting a char code to upper case isn't trivial for unicode. (This should really just use ICU.)
if (Inside(rcHotkey, static_cast<uint32_t>('a'), static_cast<uint32_t>('z')))
{
rcHotkey += static_cast<uint32_t>('A') - 'a';
}
}
}
}
if (rcHotkey == 0)
{
// No accelerator found
return false;
}
// set hotkey
sText.Grow(iHotkeyMarkupLength-2);
char *szText = sText.GrabPointer();
char *szTextBegin = szText;
rcHotkey = szText[iHotkeyPos+1]; char cOrigHotkey = rcHotkey;
if (Inside(rcHotkey, 'a', 'z')) rcHotkey+=((int32_t)'A'-'a');
// mark hotkey
size_t iTextLen = SLen(szText);
szText += iHotkeyPos; iTextLen -= iHotkeyPos;
memmove(szText+iHotkeyMarkupLength*sizeof(char), szText+2*sizeof(char), (iTextLen-1)*sizeof(char));
memcpy(szText, HotkeyMarkup, iHotkeyMarkupLength);
szText[iHotkeyMarkupHotkeyPos] = cOrigHotkey; // set original here, so no conversion to UpperCase
//szText[iTextLen+iHotkeyMarkupLength-2] = 0;
// write back string
sText.Take(szTextBegin);
sText.Take(output);
// done, success
return true;
}

View File

@ -217,7 +217,7 @@ namespace C4GUI
// expand text like "Te&xt" to "Te<c ffff00>x</c>t"
bool ExpandHotkeyMarkup(StdStrBuf &sText, char &rcHotkey);
bool ExpandHotkeyMarkup(StdStrBuf &sText, uint32_t &rcHotkey);
// make color readable on black: max alpha to 0x1f, max color hues
DWORD MakeColorReadableOnBlack(DWORD &rdwClr);
@ -421,7 +421,7 @@ namespace C4GUI
virtual void DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); // called by mouse: dragging process
virtual void StopDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); // called by mouse: mouse released after dragging process
virtual bool OnHotkey(char cHotkey) { return false; } // return true when hotkey has been processed
virtual bool OnHotkey(uint32_t cHotkey) { return false; } // return true when hotkey has been processed
public:
bool DoContext(); // open context menu if assigned
@ -483,7 +483,7 @@ namespace C4GUI
DWORD dwFgClr; // text color
int32_t x0, iAlign; // x-textstart-pos; horizontal alignment
CStdFont *pFont;
char cHotkey; // hotkey for this label
uint32_t cHotkey; // hotkey for this label
bool fAutosize;
bool fMarkup;
@ -494,7 +494,7 @@ namespace C4GUI
virtual void DrawElement(C4TargetFacet &cgo); // output label
virtual void UpdateOwnPos();
virtual bool OnHotkey(char cHotkey); // focus control on correct hotkey
virtual bool OnHotkey(uint32_t cHotkey); // focus control on correct hotkey
virtual int32_t GetLeftIndent() { return 0; }
@ -736,7 +736,7 @@ namespace C4GUI
virtual void AfterElementRemoval()
{ if (pParent) pParent->AfterElementRemoval(); } // called by ScrollWindow to parent after an element has been removed
virtual bool OnHotkey(char cHotkey); // check all contained elements for hotkey
virtual bool OnHotkey(uint32_t cHotkey); // check all contained elements for hotkey
public:
Container(); // ctor
@ -1024,7 +1024,7 @@ namespace C4GUI
DWORD dwCustomFontClr; // text font color (valid only if pCustomFont)
bool fDown; // if set, button is currently held down
bool fMouseOver; // if set, the mouse hovers over the button
char cHotkey; // hotkey for this button
uint32_t cHotkey; // hotkey for this button
bool fEnabled;
virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse movement or buttons
@ -1041,7 +1041,7 @@ namespace C4GUI
void SetDown(); // mark down and play sound
void SetUp(bool fPress); // mark up and play sound
virtual bool OnHotkey(char cHotkey); // press btn on correct hotkey
virtual bool OnHotkey(uint32_t cHotkey); // press btn on correct hotkey
public:
Button(const char *szBtnText, const C4Rect &rtBounds); // ctor
@ -1368,7 +1368,7 @@ namespace C4GUI
bool fEnabled;
CStdFont *pFont;
uint32_t dwEnabledClr, dwDisabledClr;
char cHotkey;
uint32_t cHotkey;
public:
CheckBox(const C4Rect &rtBounds, const char *szCaption, bool fChecked); // ctor
@ -1388,7 +1388,7 @@ namespace C4GUI
virtual bool IsFocusOnClick() { return false; } // just check/uncheck on click; do not gain keyboard focus as well
virtual Control *IsFocusElement() { return fEnabled ? Control::IsFocusElement() : NULL; }; // this control can gain focus if enabled
virtual void DrawElement(C4TargetFacet &cgo); // draw checkbox
virtual bool OnHotkey(char cHotkey); // return true when hotkey has been processed
virtual bool OnHotkey(uint32_t cHotkey); // return true when hotkey has been processed
public:
void SetChecked(bool fToVal) { fChecked = fToVal; } // set w/o callback
@ -1523,7 +1523,7 @@ namespace C4GUI
protected:
StdStrBuf sTitle; // sheet label
int32_t icoTitle; // sheet label icon
char cHotkey;
uint32_t cHotkey;
uint32_t dwCaptionClr; // caption color - default if 0
bool fHasCloseButton, fCloseButtonHighlighted;
bool fTitleMarkup;
@ -1710,7 +1710,7 @@ namespace C4GUI
protected:
StdStrBuf sText; // entry text
char cHotkey; // entry hotkey
uint32_t cHotkey; // entry hotkey
Icons icoIcon; // icon to be drawn left of text
MenuHandler *pMenuHandler; // callback when item is selected
ContextHandler *pSubmenuHandler; // callback when submenu is opened
@ -1724,7 +1724,7 @@ namespace C4GUI
virtual void MouseLeave(CMouse &rMouse)
{ if (GetParent()) ((ContextMenu *) GetParent())->MouseLeaveEntry(rMouse, this); }
virtual bool OnHotkey(char cKey) { return cKey==cHotkey; }
virtual bool OnHotkey(uint32_t cKey) { return cKey==cHotkey; }
virtual bool IsMenu() { return true; }

View File

@ -71,7 +71,7 @@ namespace C4GUI
}
}
bool Button::OnHotkey(char cHotkey)
bool Button::OnHotkey(uint32_t cHotkey)
{
// if hotkey matches, press the button
if (this->cHotkey == cHotkey && fEnabled)

View File

@ -62,7 +62,7 @@ namespace C4GUI
{
}
bool CheckBox::OnHotkey(char cHotkey)
bool CheckBox::OnHotkey(uint32_t cHotkey)
{
if (cHotkey != this->cHotkey) return false;
ToggleCheck(true);

View File

@ -198,7 +198,7 @@ namespace C4GUI
return this;
}
bool Container::OnHotkey(char cHotkey)
bool Container::OnHotkey(uint32_t cHotkey)
{
if (!IsVisible()) return false;
// check all nested elements

View File

@ -105,7 +105,7 @@ namespace C4GUI
}
}
bool Label::OnHotkey(char cHotkey)
bool Label::OnHotkey(uint32_t cHotkey)
{
// if hotkey matches and focus control is assigned, set focus
if (this->cHotkey == cHotkey && pClickFocusControl)

View File

@ -307,7 +307,7 @@ namespace C4GUI
if (Inside<C4KeyCode, C4KeyCode, C4KeyCode>(wKey, 'A', 'Z') || Inside<C4KeyCode, C4KeyCode, C4KeyCode>(wKey, '0', '9'))
{
// process hotkeys
char ch=char(wKey);
uint32_t ch = wKey;
for (Element *pCurr = GetFirst(); pCurr; pCurr = pCurr->GetNext())
if (pCurr->OnHotkey(ch))
{