The Locale Explorer: LCMapString
|
|
This is an example of using WideCharToMultiByte. The code in the lower edit control is the code that would be written to retrieve the highlighted feature. This code can be copied and pasted into an application and either used directly or form the basis of your own code. The rich edit controls provide an input control and a corresponding hex editing control. For UTF-8 and UTF-7 encodings, the sequences of characters are highlighted. There are dropdowns that can be used to recover an individual session's test strings. A small spin control to the right of the top control provides for a "zoom" of the font. An owner-draw pushbutton shows an arrow that indicates the direction of transfer.
This contains the characters using the selected Unicode font. The font should be something like "MS Arial Unicode" to take advantage of most of the displayable characters available.
In small font heights, it is often difficult to see the details of the characters. The spin control to the right of the Wide control selects the font height. The combo box dropdown at the left allows selection of the most recent set of tests, so it is easy to retrieve strings to repeat tests. The strings are not remembered across sessions.
This is a hexadecimal display, showing the wide-character representation (Unicode characters). Selecting a sequence of wide characters will reflect the selection in the Wide Character display, and selecting characters in the Wide Character display will select the corresponding hex characters. This is an overwrite-edit control, so if the selection is empty, the character after the caret will be replaced by whatever character is typed.
This displays the multibyte sequence. Note that the characters are interpreted as if they are 8-bit ANSI characters in the native code page. The combo box dropdown at the left allows the selection of the most recent set of tests, as in the Wide Character display.
This is the hexadecimal representation of the multibyte-character text. This hexadecimal text can be edited, and the effects of the edit will be seen in the multibyte-character text window above it. This is an "overwrite-mode" control, so you cannot do a replacement if you have any nonempty selection. Typing characters will replace the character in front of the caret. As with the Wide Character pair, selecting characters in the MultiByte display will highlight the characters in the hex display, and vice-versa.
When CP_UTF7 or CP_UTF8 are specified, each character is shown as an underlined group. For example, given the Unicode string
0061 3061 0062
then the conversions are displayed as shown below.
UTF8
61
e3 81 a1 62UTF7
61 2b 4d 47 45 2d 62
The characters 2b and 2d are the UTF-7 "shift-in" and "shift-out" characters, "+" and "-". To represent an individual "+" in the wide character set will generate the sequence "2b 2d" in the UTF-7, e.g.
a+b-c
(Unicode representation)
0061 002b 0062 002d 0063
is converted to UTF7 as
61 2b 2d 62 2d 63
It continues to be amazing how incredibly poorly the rich-edit controls are implemented. If there had been any actual design effort made on the programming interface, many of the horrific kludges I describe here would not be required. Unfortunately, a feature as fundamental as overwrite mode cannot be set programmatically, nor can it be detected programmatically. It is a "by guess and by golly" approach that has to be taken, due to the inadequate design.
The key here is to force the control into an overwrite mode and keep it from leaving the overwrite mode. An operation this trivial should consist of two API calls: one which forces the overwrite mode on, and one which allows checking of the current state of the overwrite mode.
To build the hex edit control, I write characters separated by protected blankspace. I only allow overwrite if the selection size is 0, and only the character after the caret can be changed, because the control is in overwrite mode and the character is simply replaced using the built-in overwrite support of the rich edit control
I have to simulate going into overwrite mode. There is an OVR Boolean value which starts off as FALSE. When focus is set, if the OVR is FALSE, a pair of messages simulating the Insert key (the user interface that lets the user set the state from the keyboard, and the only way the mode can be set). If I see the Insert key being hit, and the OVR is false, I let it be handled by the control, otherwise I short-circuit it and discard it.
BOOL CHexEditCtrl::OnSetfocus() { if(!OVR) { /* needs to set OVR */ SendInsert(); } /* needs to set OVR */ return FALSE; } // CHexEditCtrl::OnSetfocus //********************************************************************* void CHexEditCtrl::SendInsert() { Sending = TRUE; // Indicate that the VK_INSERTs should pass thru SendMessage(WM_KEYDOWN, VK_INSERT, 0x00510001); SendMessage(WM_KEYUP, VK_INSERT, 0xC0510001); } // CHexEditCtrl::SendInsert
There are some additional problems with the Rich Edit control. For example, if there are protected fields in the Rich Edit control, Ctrl+C is disabled, and you can't copy a selection to the clipboard. So I had to override the Ctrl+C operation and send an explicit WM_COPY command to the control.
In the code below, note that an Insert key only works if the special Sending flag is set by SendInsert.
The RichEditCtrlEx is a class that allows for complete support of the Rich Edit 2.0 functionality in versions of MFC that do not offer support for it.
void CHexEditCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { //---------------------------------------------------------------- // If we are not in the special mode used to force recognition of VK_INSERT // reject the keystroke //---------------------------------------------------------------- if(!Sending && nChar == VK_INSERT) { /* VK_INSERT */ return; // do not allow INSERT key } /* VK_INSERT */ //---------------------------------------------------------------- // Reject other keys that would change the contents //---------------------------------------------------------------- if(nChar == _T('\r')) { /* \r */ return; } /* \r */ if(nChar == _T('\n')) { /* \n */ return; } /* \n */ if(nChar == VK_DELETE) { /* VK_DELETE */ return; } /* VK_DELETE */ if(nChar == VK_BACK) { /* VK_BACK */ return; } /* VK_BACK */ //---------------------------------------------------------------- // Otherwise pass the key on through //---------------------------------------------------------------- CRichEditCtrlEx::OnKeyDown(nChar, nRepCnt, nFlags); } // CHexEditCtrl::OnKeyDownThe OnKeyDown handler blocks any key, such as carriage return, line feed, delete, or backspace, that might modify the contents of the edit control. The OnKeyUp handler provides completion for the setting-overwrite mode mechanism:
void CHexEditCtrl::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { //---------------------------------------------------------------- // If we were in the special sending mode to allow VK_INSERT // to go through, the WM_KEYUP has completed that cycle and // we clear the Sending flag, and set OVR to TRUE // Otherwise we reject the VK_INSERT key-up request //---------------------------------------------------------------- if(Sending && nChar == VK_INSERT) { /* end send */ Sending = FALSE; OVR = TRUE; } /* end send */ else if(!Sending && nChar == VK_INSERT) { /* VK_INSERT */ return; } /* VK_INSERT */ //---------------------------------------------------------------- // Reject delete and backspace //---------------------------------------------------------------- if(nChar == VK_DELETE) { /* VK_DELETE */ return; } /* VK_DELETE */ if(nChar == VK_BACK) { /* VK_BACK */ return; } /* VK_BACK */ //---------------------------------------------------------------- // Pass it on //---------------------------------------------------------------- CRichEditCtrlEx::OnKeyUp(nChar, nRepCnt, nFlags); }
Most of this code was arrived at empirically, by studying the desired effects on the Rich Edit control and doing my best to defeat anything that was unattractice.
//******************************************************************** void CHexEditCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { long low; long high; CRichEditCtrlEx::GetSel(low, high); #define CTRL(x) ((x) - _T('@')) switch(nChar) { /* check ctrl codes */ case CTRL(_T('C')): if(low == high) { /* no selection */ MessageBeep(0); return; } /* no selection */ SendMessage(WM_COPY); return; } /* check ctrl codes */ // Typing is illegal if there is a nonempty selection if(low != high) { /* no replace */ MessageBeep(0); return; } /* no replace */ switch(nChar) { /* character */ case _T('0'): case _T('1'): case _T('2'): case _T('3'): case _T('4'): case _T('5'): case _T('6'): case _T('7'): case _T('8'): case _T('9'): case _T('a'): case _T('b'): case _T('c'): case _T('d'): case _T('e'): case _T('f'): case _T('A'): case _T('B'): case _T('C'): case _T('D'): case _T('E'): case _T('F'): break; default: MessageBeep(0); return; } /* character */ Changing = TRUE; CRichEditCtrlEx::OnChar(nChar, nRepCnt, nFlags); }
This is the gist of the code that creates the hex edit control. There are some minor details which can be found in the source code. Of course, Microsoft could completely break this code (if they broke it by creating the right kind of interface to the Rich Edit control it would be great).
The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.