Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Putting IME in a custom text box, derived from Control

I have created my own single-line textbox control in C# .net 3.5

I've written this completely from scratch, including my own selection code, scrolling, copy/paste etc. It can even select left-to-right text, not that I'd ever use that...

The problem I'm having is with IME (input method editor) integration. The IME does work to an extent, but I have a few problems. I'll just list one of them here.

Normally, using a TextBox, the IME construction is rendered inside the TextBox. However, in my Control, the IME is displayed at the bottom right of the screen. I tried looking at other custom controls, included the one used in SharpDevelop. However, this had the same problem. Typing in Chinese, Japanese, or Korean caused the IME stuff to be done at the bottom-right of the screen.

I need some pointers on how to get this to be done in my own textbox, and I looked at the Windows API for IME, but there are so many functions, and I have no idea where to start.

So, any ideas?

It's fine if any code samples are in C++ (managed/unmanaged) or VB.net. I can move it across easily enough.

like image 463
Vincent McNabb Avatar asked Jul 20 '10 05:07

Vincent McNabb


1 Answers

OK, it looks like there aren't many IME experts on StackOverflow... or no one is interested.

Anyway, I figured it out.

Basically, I have to trap the following Windows messages:

WM_INPUTLANGCHANGE = 0x51
WM_KEYUP = 0x101
WM_CHAR = 0x102
WM_CONVERTREQUESTEX = 0x108
WM_IME_STARTCOMPOSITION = 0x10D
WM_IME_ENDCOMPOSITION = 0x10E
WM_IME_COMPOSITION = 0x10F
WM_IME_SETCONTEXT = 0x281
WM_IME_NOTIFY = 0x282
WM_IME_CONTROL = 0x283
WM_IME_COMPOSITIONFULL = 0x284
WM_IME_SELECT = 0x285
WM_IME_CHAR = 0x286
WM_IME_REQUEST = 0x0288
WM_IME_KEYDOWN = 0x290
WM_IME_KEYUP = 0x291

I'm trapping WM_KEYUP and WM_CHAR, because if I click somewhere in the middle of compositing a Korean character, I do not get a composition message, but I need to add that character to my text box nonetheless. This is a weird behaviour, I wonder if it's a bug.

Once that happens, there are different behaviours between Korean, Chinese and Japanese.

Korean is a really easy one (I'm not sure about Hanja convert though, because I don't know how to use that anyway).

Basically, for all languages, whenever I get a WM_IME_COMPOSITION, I have to call ImmGetCompositionString in Imm32.dll as I have described in answer to this question. I then display this as a composition in progress, but don't add it to my stored text.

When a string has been composited the message from Windows is different for each IME. Each time, I can get it from the WM_IME_COMPOSITION message.

In Korean, LParam will simply be GCS_RESULTSTR, and WParam will be the entered character, which I can just cast to char

In Japanese 'LParam' will be GCS_RESULTREADSTR | GCS_RESULTREADCLAUSE | GCS_RESULTSTR0 | GCS_RESULTCLAUSE. I have to use the result of ImmGetCompositionString that I stored from the previous WM_IME_COMPOSITION message, because at this time it will be an empty string.

In Chinese, LParam will be GCS_RESULTREADCLAUSE | GCS_RESULTSTR0 | GCS_RESULTCLAUSE. It's the same as Japanese, except when the previously stored ImmGetCompositionString is empty, in which case I need to cast WParam to a char.

In all three cases, I have to make sure that my displayed comp in progress is cleared.

If I receive WM_IME_STARTCOMPOSITION, I set a compositing flag (and display the compositing string in progress)

If I receive WM_IME_ENDCOMPOSITION I clear that flag (and clear the compositing string in progress).

Sometimes I don't receive WM_IME_ENDCOMPOSITION, so I clear my flag on receiving WM_CHAR.


All in all, it was a very interesting learning experience, and one that is still in progress - but IME is now usable on my control, at last! I stayed at work till 1am to get it done.

like image 157
Vincent McNabb Avatar answered Nov 17 '22 17:11

Vincent McNabb