From 2096532d0e654a2f67c968879a86e1b4b8298007 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 27 Oct 2015 01:06:17 +0800 Subject: [PATCH] Make stk more-i18n friendly Now supertuxkart can do: Windows (fully test in win8 and win7 built with vs2015, mingw not tested): Input characters using ime (CJK tested). Fix crashes when try to paste unicode text into editbox in stk. Linux: Different keymap can be used in stk now, ie typing russian,hebrew (not bidi-aware) is now possible. IME not supported. Mac: No work has been done, sorry mac fanboys/girls:( Though testing is welcome, esp when pasting words into stk. Limitation: No ime box is shown in fullscreen (Windows)Only the used language in current setting of non-Unicode programs can be typed with its supported IME. --- lib/irrlicht/include/IEventReceiver.h | 39 ++++ lib/irrlicht/include/IOSOperator.h | 8 + lib/irrlicht/source/Irrlicht/CGUIEditBox.cpp | 12 ++ .../source/Irrlicht/CGUIEnvironment.cpp | 12 ++ .../source/Irrlicht/CIrrDeviceLinux.cpp | 199 +++++++++++++++--- .../source/Irrlicht/CIrrDeviceLinux.h | 8 + .../source/Irrlicht/CIrrDeviceWin32.cpp | 77 +++++++ lib/irrlicht/source/Irrlicht/COSOperator.cpp | 56 +++++ lib/irrlicht/source/Irrlicht/COSOperator.h | 8 + src/guiengine/widgets/CGUIEditBox.cpp | 163 ++++++++++++++ src/guiengine/widgets/CGUIEditBox.hpp | 5 + 11 files changed, 554 insertions(+), 33 deletions(-) diff --git a/lib/irrlicht/include/IEventReceiver.h b/lib/irrlicht/include/IEventReceiver.h index 59b184fc8..c19248a83 100644 --- a/lib/irrlicht/include/IEventReceiver.h +++ b/lib/irrlicht/include/IEventReceiver.h @@ -49,6 +49,14 @@ namespace irr user receiver then no text will be sent to the console. */ EET_LOG_TEXT_EVENT, +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + //! A input method event + /** Input method events are created by the input method message and passed to IrrlichtDevice::postEventFromUser. + Windows: Implemented. + Linux / Other: Not yet implemented. */ + EET_IMPUT_METHOD_EVENT, +#endif + //! A user event with user data. /** This is not used by Irrlicht and can be used to send user specific data though the system. The Irrlicht 'window handle' @@ -142,6 +150,20 @@ namespace irr EMBSM_FORCE_32_BIT = 0x7fffffff }; +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + //! Enumeration for all input method events + enum EINPUT_METHOD_EVENT + { + //! a character from input method. + EIME_CHAR_INPUT = 0, + + //! change position of composition window + EIME_CHANGE_POS, + + EIME_FORCE_32_BIT = 0x7fffffff + }; +#endif + namespace gui { @@ -411,6 +433,20 @@ struct SEvent s32 UserData2; }; +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + struct SInputMethodEvent + { + //! Parent window handle for IMM functions (Windows only) + void* Handle; + + //! Character from Input Method + wchar_t Char; + + //! Type of input method event + EINPUT_METHOD_EVENT Event; + }; +#endif + EEVENT_TYPE EventType; union { @@ -420,6 +456,9 @@ struct SEvent struct SJoystickEvent JoystickEvent; struct SLogEvent LogEvent; struct SUserEvent UserEvent; +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + struct SInputMethodEvent InputMethodEvent; +#endif }; }; diff --git a/lib/irrlicht/include/IOSOperator.h b/lib/irrlicht/include/IOSOperator.h index 22fb691ee..dac6342ab 100644 --- a/lib/irrlicht/include/IOSOperator.h +++ b/lib/irrlicht/include/IOSOperator.h @@ -26,11 +26,19 @@ public: } //! Copies text to the clipboard +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + virtual void copyToClipboard(const wchar_t* text) const = 0; +#else virtual void copyToClipboard(const c8* text) const = 0; +#endif //! Get text from the clipboard /** \return Returns 0 if no string is in there. */ +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + virtual const wchar_t* getTextFromClipboard() const = 0; +#else virtual const c8* getTextFromClipboard() const = 0; +#endif //! Get the processor speed in megahertz /** \param MHz The integer variable to store the speed in. diff --git a/lib/irrlicht/source/Irrlicht/CGUIEditBox.cpp b/lib/irrlicht/source/Irrlicht/CGUIEditBox.cpp index a937cc2bf..02e2130bb 100644 --- a/lib/irrlicht/source/Irrlicht/CGUIEditBox.cpp +++ b/lib/irrlicht/source/Irrlicht/CGUIEditBox.cpp @@ -286,7 +286,11 @@ bool CGUIEditBox::processKey(const SEvent& event) const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd; const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; +#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ + core::stringw s; +#else core::stringc s; +#endif s = Text.subString(realmbgn, realmend - realmbgn).c_str(); Operator->copyToClipboard(s.c_str()); } @@ -299,7 +303,11 @@ bool CGUIEditBox::processKey(const SEvent& event) const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; // copy +#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ + core::stringw sc; +#else core::stringc sc; +#endif sc = Text.subString(realmbgn, realmend - realmbgn).c_str(); Operator->copyToClipboard(sc.c_str()); @@ -329,7 +337,11 @@ bool CGUIEditBox::processKey(const SEvent& event) const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; // add new character +#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ + const wchar_t* p = Operator->getTextFromClipboard(); +#else const c8* p = Operator->getTextFromClipboard(); +#endif if (p) { if (MarkBegin == MarkEnd) diff --git a/lib/irrlicht/source/Irrlicht/CGUIEnvironment.cpp b/lib/irrlicht/source/Irrlicht/CGUIEnvironment.cpp index bf3fa8666..19bd510f9 100644 --- a/lib/irrlicht/source/Irrlicht/CGUIEnvironment.cpp +++ b/lib/irrlicht/source/Irrlicht/CGUIEnvironment.cpp @@ -599,6 +599,18 @@ bool CGUIEnvironment::postEventFromUser(const SEvent& event) } break; +#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ + case EET_IMPUT_METHOD_EVENT: + { + // todo : if CGUIEdit has not focus, close input method. Use WM_NOTIFY message. + if (Focus) + { + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return Focus->OnEvent(event); + } + } + break; +#endif default: break; } // end switch diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp index a0bd1eae2..cd2c05a16 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp @@ -14,6 +14,7 @@ extern bool GLContextDebugBit; #include #include #include +#include #include "IEventReceiver.h" #include "ISceneManager.h" #include "IGUIEnvironment.h" @@ -83,6 +84,7 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param) : CIrrDeviceStub(param), #ifdef _IRR_COMPILE_WITH_X11_ display(0), visual(0), screennr(0), window(0), StdHints(0), SoftwareImage(0), + XInputMethod(0), XInputContext(0), #ifdef _IRR_COMPILE_WITH_OPENGL_ glxWin(0), Context(0), @@ -134,6 +136,10 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param) if (!VideoDriver) return; +#ifdef _IRR_COMPILE_WITH_X11_ + createInputContext(); +#endif + createGUIAndScene(); } @@ -168,6 +174,8 @@ CIrrDeviceLinux::~CIrrDeviceLinux() VideoDriver = NULL; } + destroyInputContext(); + if (display) { #ifdef _IRR_COMPILE_WITH_OPENGL_ @@ -1154,6 +1162,123 @@ void CIrrDeviceLinux::createDriver() } } +#ifdef _IRR_COMPILE_WITH_X11_ +bool CIrrDeviceLinux::createInputContext() +{ + // One one side it would be nicer to let users do that - on the other hand + // not setting the environment locale will not work when using i18n X11 functions. + // So users would have to call it always or their input is broken badly. + // We can restore immediately - so won't mess with anything in users apps. + core::stringc oldLocale(setlocale(LC_CTYPE, NULL)); + setlocale(LC_CTYPE, ""); // use environment locale + + if ( !XSupportsLocale() ) + { + os::Printer::log("Locale not supported. Falling back to non-i18n input.", ELL_WARNING); + setlocale(LC_CTYPE, oldLocale.c_str()); + return false; + } + + XInputMethod = XOpenIM(display, NULL, NULL, NULL); + if ( !XInputMethod ) + { + setlocale(LC_CTYPE, oldLocale.c_str()); + os::Printer::log("XOpenIM failed to create an input method. Falling back to non-i18n input.", ELL_WARNING); + return false; + } + + XIMStyles *im_supported_styles; + XGetIMValues(XInputMethod, XNQueryInputStyle, &im_supported_styles, (char*)NULL); + XIMStyle bestStyle = 0; + // TODO: If we want to support languages like chinese or japanese as well we probably have to work with callbacks here. + XIMStyle supportedStyle = XIMPreeditNone | XIMStatusNone; + for (int i=0; i < im_supported_styles->count_styles; ++i) + { + XIMStyle style = im_supported_styles->supported_styles[i]; + if ((style & supportedStyle) == style) /* if we can handle it */ + { + bestStyle = style; + break; + } + } + XFree(im_supported_styles); + + if ( !bestStyle ) + { + XDestroyIC(XInputContext); + XInputContext = 0; + + os::Printer::log("XInputMethod has no input style we can use. Falling back to non-i18n input.", ELL_WARNING); + setlocale(LC_CTYPE, oldLocale.c_str()); + return false; + } + + XInputContext = XCreateIC(XInputMethod, + XNInputStyle, bestStyle, + XNClientWindow, window, + (char*)NULL); + if (!XInputContext ) + { + os::Printer::log("XInputContext failed to create an input context. Falling back to non-i18n input.", ELL_WARNING); + setlocale(LC_CTYPE, oldLocale.c_str()); + return false; + } + XSetICFocus(XInputContext); + setlocale(LC_CTYPE, oldLocale.c_str()); + return true; +} + +void CIrrDeviceLinux::destroyInputContext() +{ + if ( XInputContext ) + { + XUnsetICFocus(XInputContext); + XDestroyIC(XInputContext); + XInputContext = 0; + } + if ( XInputMethod ) + { + XCloseIM(XInputMethod); + XInputMethod = 0; + } +} + +EKEY_CODE CIrrDeviceLinux::getKeyCode(XEvent &event) +{ + EKEY_CODE keyCode = (EKEY_CODE)0; + + SKeyMap mp; + mp.X11Key = XkbKeycodeToKeysym(display, event.xkey.keycode, 0, 0); + // mp.X11Key = XKeycodeToKeysym(XDisplay, event.xkey.keycode, 0); // deprecated, if we still find platforms which need that we have to use some define + const s32 idx = KeyMap.binary_search(mp); + if (idx != -1) + { + keyCode = (EKEY_CODE)KeyMap[idx].Win32Key; + } + if (keyCode == 0) + { + // Any value is better than none, that allows at least using the keys. + // Worst case is that some keys will be identical, still better than _all_ + // unknown keys being identical. + if ( !mp.X11Key ) + { + keyCode = (EKEY_CODE)event.xkey.keycode; + os::Printer::log("No such X11Key, using event keycode", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION); + } + else if (idx == -1) + { + keyCode = (EKEY_CODE)mp.X11Key; + os::Printer::log("EKEY_CODE not found, using orig. X11 keycode", core::stringc(mp.X11Key).c_str(), ELL_INFORMATION); + } + else + { + keyCode = (EKEY_CODE)mp.X11Key; + os::Printer::log("EKEY_CODE is 0, using orig. X11 keycode", core::stringc(mp.X11Key).c_str(), ELL_INFORMATION); + } + } + return keyCode; +} +#endif //! runs the device. Returns false if device wants to be deleted bool CIrrDeviceLinux::run() @@ -1330,52 +1455,60 @@ bool CIrrDeviceLinux::run() (next_event.xkey.keycode == event.xkey.keycode) && (next_event.xkey.time - event.xkey.time) < 2) // usually same time, but on some systems a difference of 1 is possible { - /* Ignore the key release event */ + // Ignore the key release event break; } } - // fall-through in case the release should be handled + + irrevent.EventType = irr::EET_KEY_INPUT_EVENT; + irrevent.KeyInput.PressedDown = false; + irrevent.KeyInput.Char = 0; // on release that's undefined + irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0; + irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0; + irrevent.KeyInput.Key = getKeyCode(event); + + postEventFromUser(irrevent); + break; + case KeyPress: { SKeyMap mp; - char buf[8]={0}; - XLookupString(&event.xkey, buf, sizeof(buf), &mp.X11Key, NULL); - - irrevent.EventType = irr::EET_KEY_INPUT_EVENT; - irrevent.KeyInput.PressedDown = (event.type == KeyPress); -// mbtowc(&irrevent.KeyInput.Char, buf, sizeof(buf)); - irrevent.KeyInput.Char = ((wchar_t*)(buf))[0]; - irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0; - irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0; - - event.xkey.state &= ~(ControlMask|ShiftMask); // ignore shift-ctrl states for figuring out the key - XLookupString(&event.xkey, buf, sizeof(buf), &mp.X11Key, NULL); - const s32 idx = KeyMap.binary_search(mp); - if (idx != -1) + if ( XInputContext ) { - irrevent.KeyInput.Key = (EKEY_CODE)KeyMap[idx].Win32Key; - } - else - { - irrevent.KeyInput.Key = (EKEY_CODE)0; - } - if (irrevent.KeyInput.Key == 0) - { - // 1:1 mapping to windows-keys would require testing for keyboard type (us, ger, ...) - // So unless we do that we will have some unknown keys here. - if (idx == -1) + wchar_t buf[8]={0}; + Status status; + int strLen = XwcLookupString(XInputContext, &event.xkey, buf, sizeof(buf), &mp.X11Key, &status); + if ( status == XBufferOverflow ) { - os::Printer::log("Could not find EKEY_CODE, using orig. X11 keycode instead", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION); + os::Printer::log("XwcLookupString needs a larger buffer", ELL_INFORMATION); + } + if ( strLen > 0 && (status == XLookupChars || status == XLookupBoth) ) + { + if ( strLen > 1 ) + os::Printer::log("Additional returned characters dropped", ELL_INFORMATION); + irrevent.KeyInput.Char = buf[0]; } else { - os::Printer::log("EKEY_CODE is 0, using orig. X11 keycode instead", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION); + irrevent.KeyInput.Char = 0; } - // Any value is better than none, that allows at least using the keys. - // Worst case is that some keys will be identical, still better than _all_ - // unknown keys being identical. - irrevent.KeyInput.Key = (EKEY_CODE)event.xkey.keycode; } + else // Old version without InputContext. Does not support i18n, but good to have as fallback. + { + union + { + char buf[8]; + wchar_t wbuf[2]; + } tmp = {0}; + XLookupString(&event.xkey, tmp.buf, sizeof(tmp.buf), &mp.X11Key, NULL); + irrevent.KeyInput.Char = tmp.wbuf[0]; + } + + irrevent.EventType = irr::EET_KEY_INPUT_EVENT; + irrevent.KeyInput.PressedDown = true; + irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0; + irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0; + irrevent.KeyInput.Key = getKeyCode(event); postEventFromUser(irrevent); } diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h index c808b8ebd..a50c7fa74 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h @@ -153,6 +153,12 @@ namespace irr bool restoreResolution(); bool changeResolution(); +#ifdef _IRR_COMPILE_WITH_X11_ + bool createInputContext(); + void destroyInputContext(); + EKEY_CODE getKeyCode(XEvent &event); +#endif + //! Implementation of the linux cursor control class CCursorControl : public gui::ICursorControl { @@ -390,6 +396,8 @@ namespace irr XSetWindowAttributes attributes; XSizeHints* StdHints; XImage* SoftwareImage; + XIM XInputMethod; + XIC XInputContext; mutable core::stringc Clipboard; #ifdef _IRR_LINUX_X11_VIDMODE_ XF86VidModeModeInfo oldVideoMode; diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 432e1e640..13759996f 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -34,6 +34,9 @@ #endif #endif +#include +#pragma comment(lib, "imm32.lib") + namespace irr { namespace video @@ -692,6 +695,28 @@ irr::CIrrDeviceWin32* getDeviceFromHWnd(HWND hWnd) } +namespace irr +{ + void updateICPos(void* hWnd, s32 x, s32 y, s32 height) + { + COMPOSITIONFORM cf; + HWND hwnd = (HWND)hWnd; + HIMC hIMC = ImmGetContext(hwnd); + + if(hIMC) + { + cf.dwStyle = CFS_POINT; + cf.ptCurrentPos.x = x; + cf.ptCurrentPos.y = y - height; + + ImmSetCompositionWindow(hIMC, &cf); + + ImmReleaseContext(hwnd, hIMC); + } + } +} // end of namespace irr + + LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { #ifndef WM_MOUSEWHEEL @@ -802,6 +827,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } } } + + if (m->irrMessage == irr::EMIE_LMOUSE_PRESSED_DOWN || m->irrMessage == irr::EMIE_LMOUSE_LEFT_UP) + { + event.EventType = irr::EET_IMPUT_METHOD_EVENT; + event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; + event.InputMethodEvent.Handle = hWnd; + + if (dev) + dev->postEventFromUser(event); + } return 0; } @@ -818,6 +853,30 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_ERASEBKGND: return 0; + case WM_IME_CHAR: + { + event.EventType = irr::EET_IMPUT_METHOD_EVENT; + event.InputMethodEvent.Event = irr::EIME_CHAR_INPUT; + event.InputMethodEvent.Handle = hWnd; + + event.KeyInput.Char = 0; + event.KeyInput.Key = irr::KEY_OEM_CLEAR; + event.KeyInput.Shift = 0; + event.KeyInput.Control = 0; + + char bits[2] = { (char)((wParam & 0xff00) >> 8), (char)(wParam & 0xff) }; + if (bits[0] == 0) + event.InputMethodEvent.Char = (wchar_t)wParam; + else + MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, bits, 2, &event.InputMethodEvent.Char, 1); + + dev = getDeviceFromHWnd(hWnd); + if (dev) + dev->postEventFromUser(event); + + return 0; + } + case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_KEYDOWN: @@ -879,6 +938,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) event.KeyInput.Control = 0; dev = getDeviceFromHWnd(hWnd); + if (dev) + dev->postEventFromUser(event); + + event.EventType = irr::EET_IMPUT_METHOD_EVENT; + event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; + event.InputMethodEvent.Handle = hWnd; + if (dev) dev->postEventFromUser(event); @@ -932,6 +998,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) dev->switchToFullScreen(); } } + event.EventType = irr::EET_IMPUT_METHOD_EVENT; + event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; + event.InputMethodEvent.Handle = hWnd; + + if (dev) + dev->postEventFromUser(event); break; case WM_USER: @@ -1115,6 +1187,9 @@ CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params) // inform driver about the window size etc. resizeIfNecessary(); + + // for reset the position of composition window + updateICPos(HWnd, 0, 0, 0); } @@ -1870,6 +1945,8 @@ void CIrrDeviceWin32::handleSystemMessages() // No message translation because we don't use WM_CHAR and it would conflict with our // deadkey handling. + TranslateMessage(&msg); + if (ExternalWindow && msg.hwnd == HWnd) WndProc(HWnd, msg.message, msg.wParam, msg.lParam); else diff --git a/lib/irrlicht/source/Irrlicht/COSOperator.cpp b/lib/irrlicht/source/Irrlicht/COSOperator.cpp index 5ade8a5b4..ea407fdc4 100644 --- a/lib/irrlicht/source/Irrlicht/COSOperator.cpp +++ b/lib/irrlicht/source/Irrlicht/COSOperator.cpp @@ -52,6 +52,37 @@ const core::stringc& COSOperator::getOperatingSystemVersion() const //! copies text to the clipboard +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) +void COSOperator::copyToClipboard(const wchar_t* text) const +{ + if (wcslen(text)==0) + return; + +// Windows version +#if defined(_IRR_XBOX_PLATFORM_) +#elif defined(_IRR_WINDOWS_API_) + if (!OpenClipboard(NULL) || text == 0) + return; + + EmptyClipboard(); + + HGLOBAL clipbuffer; + wchar_t * buffer; + + clipbuffer = GlobalAlloc(GMEM_DDESHARE, wcslen(text)*sizeof(wchar_t) + sizeof(wchar_t)); + buffer = (wchar_t*)GlobalLock(clipbuffer); + + wcscpy(buffer, text); + + GlobalUnlock(clipbuffer); + SetClipboardData(CF_UNICODETEXT, clipbuffer); //Windwos converts between CF_UNICODETEXT and CF_TEXT automatically. + CloseClipboard(); + +#else + +#endif +} +#else void COSOperator::copyToClipboard(const c8* text) const { if (strlen(text)==0) @@ -89,10 +120,34 @@ void COSOperator::copyToClipboard(const c8* text) const #endif } +#endif //! gets text from the clipboard //! \return Returns 0 if no string is in there. +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) +const wchar_t* COSOperator::getTextFromClipboard() const +{ +#if defined(_IRR_XBOX_PLATFORM_) + return 0; +#elif defined(_IRR_WINDOWS_API_) + if (!OpenClipboard(NULL)) + return 0; + + wchar_t * buffer = 0; + + HANDLE hData = GetClipboardData( CF_UNICODETEXT ); //Windwos converts between CF_UNICODETEXT and CF_TEXT automatically. + buffer = (wchar_t*)GlobalLock( hData ); + GlobalUnlock( hData ); + CloseClipboard(); + return buffer; + +#else + + return 0; +#endif +} +#else const c8* COSOperator::getTextFromClipboard() const { #if defined(_IRR_XBOX_PLATFORM_) @@ -122,6 +177,7 @@ const c8* COSOperator::getTextFromClipboard() const return 0; #endif } +#endif bool COSOperator::getProcessorSpeedMHz(u32* MHz) const diff --git a/lib/irrlicht/source/Irrlicht/COSOperator.h b/lib/irrlicht/source/Irrlicht/COSOperator.h index 819805fb2..44f7680c2 100644 --- a/lib/irrlicht/source/Irrlicht/COSOperator.h +++ b/lib/irrlicht/source/Irrlicht/COSOperator.h @@ -27,11 +27,19 @@ public: virtual const core::stringc& getOperatingSystemVersion() const; //! copies text to the clipboard +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + virtual void copyToClipboard(const wchar_t* text) const; +#else virtual void copyToClipboard(const c8* text) const; +#endif //! gets text from the clipboard //! \return Returns 0 if no string is in there. +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + virtual const wchar_t* getTextFromClipboard() const; +#else virtual const c8* getTextFromClipboard() const; +#endif //! gets the processor speed in megahertz //! \param Mhz: diff --git a/src/guiengine/widgets/CGUIEditBox.cpp b/src/guiengine/widgets/CGUIEditBox.cpp index 5d2c0fe88..50567aa3e 100644 --- a/src/guiengine/widgets/CGUIEditBox.cpp +++ b/src/guiengine/widgets/CGUIEditBox.cpp @@ -26,6 +26,15 @@ numerical */ +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) +#define UTF16_IS_SURROGATE_LO(c) (((c) & 0xFC00) == 0xDC00) +#define UTF16_IS_SURROGATE_HI(c) (((c) & 0xFC00) == 0xD800) + +namespace irr +{ + void updateICPos(void* hWnd, s32 x, s32 y, s32 height); +} +#endif StkTime::TimeType getTime() { @@ -229,6 +238,12 @@ bool CGUIEditBox::OnEvent(const SEvent& event) } } break; +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + case EET_IMPUT_METHOD_EVENT: + if (processIMEEvent(event)) + return true; + break; +#endif case EET_KEY_INPUT_EVENT: if (processKey(event)) return true; @@ -282,7 +297,11 @@ bool CGUIEditBox::processKey(const SEvent& event) core::stringw s; s = Text.subString(realmbgn, realmend - realmbgn).c_str(); +#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ + Operator->copyToClipboard(s.c_str()); +#else Operator->copyToClipboard(StringUtils::wide_to_utf8(s.c_str()).c_str()); +#endif } break; case KEY_KEY_X: @@ -295,7 +314,11 @@ bool CGUIEditBox::processKey(const SEvent& event) // copy core::stringw sc; sc = Text.subString(realmbgn, realmend - realmbgn).c_str(); +#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ + Operator->copyToClipboard(sc.c_str()); +#else Operator->copyToClipboard(StringUtils::wide_to_utf8(sc.c_str()).c_str()); +#endif if (isEnabled()) { @@ -324,20 +347,32 @@ bool CGUIEditBox::processKey(const SEvent& event) const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin; // add new character +#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ + const wchar_t* p = Operator->getTextFromClipboard(); +#else const c8* p = Operator->getTextFromClipboard(); +#endif if (p) { if (MarkBegin == MarkEnd) { // insert text core::stringw s = Text.subString(0, CursorPos); +#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ s.append(StringUtils::utf8_to_wide(p)); +#else + s.append(p); +#endif s.append( Text.subString(CursorPos, Text.size()-CursorPos) ); if (!Max || s.size()<=Max) // thx to Fish FH for fix { Text = s; +#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ s = StringUtils::utf8_to_wide(p); +#else + s = p; +#endif CursorPos += s.size(); } } @@ -346,13 +381,21 @@ bool CGUIEditBox::processKey(const SEvent& event) // replace text core::stringw s = Text.subString(0, realmbgn); +#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ s.append(StringUtils::utf8_to_wide(p)); +#else + s.append(p); +#endif s.append( Text.subString(realmend, Text.size()-realmend) ); if (!Max || s.size()<=Max) // thx to Fish FH for fix { Text = s; +#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ s = StringUtils::utf8_to_wide(p); +#else + s = p; +#endif CursorPos = realmbgn + s.size(); } } @@ -734,10 +777,102 @@ bool CGUIEditBox::processKey(const SEvent& event) calculateScrollPos(); +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + switch(event.KeyInput.Key) + { + // If cursor points the surrogate low, send KEY_LEFT event. + case KEY_UP: + case KEY_DOWN: + if (MultiLine || (WordWrap && BrokenText.size() > 1) ) + { + if (UTF16_IS_SURROGATE_LO(Text[CursorPos])) + { + SEvent leftEvent; + leftEvent = event; + leftEvent.KeyInput.Key = KEY_LEFT; + Environment->postEventFromUser(leftEvent); + } + } + break; + // If cursor points the surrogate low, send a same event. + case KEY_LEFT: + case KEY_RIGHT: + case KEY_DELETE: + if (UTF16_IS_SURROGATE_LO(Text[CursorPos])) + Environment->postEventFromUser(event); + break; + // If cursor points front of the surrogate high, send a same event. + case KEY_BACK: + if (CursorPos > 0) + { + if (UTF16_IS_SURROGATE_HI(Text[CursorPos-1])) + Environment->postEventFromUser(event); + } + break; + default: + break; + } +#endif return true; } +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) +bool CGUIEditBox::processIMEEvent(const SEvent& event) +{ + switch(event.InputMethodEvent.Event) + { + case EIME_CHAR_INPUT: + inputChar(event.InputMethodEvent.Char); + return true; + case EIME_CHANGE_POS: + { + core::position2di pos = calculateICPos(); + + IGUIFont* font = OverrideFont; + IGUISkin* skin = Environment->getSkin(); + + if (!OverrideFont) + font = skin->getFont(); + + irr::updateICPos(event.InputMethodEvent.Handle, pos.X,pos.Y, font->getDimension(L"|").Height); + + return true; + } + default: + break; + } + + return false; +} + +//! calculate the position of input composition window +core::position2di CGUIEditBox::calculateICPos() +{ + core::position2di pos; + IGUIFont* font = OverrideFont; + IGUISkin* skin = Environment->getSkin(); + if (!OverrideFont) + font = skin->getFont(); + + //drop the text that clipping on the right side + if (WordWrap | MultiLine) + { + // todo : It looks like a heavy drinker. Strange!! + pos.X = CurrentTextRect.LowerRightCorner.X - font->getDimension(Text.subString(CursorPos, BrokenTextPositions[getLineFromPos(CursorPos)] + BrokenText[getLineFromPos(CursorPos)].size() - CursorPos).c_str()).Width; + pos.Y = CurrentTextRect.UpperLeftCorner.Y + font->getDimension(L"|").Height + (Border ? 3 : 0) - ((MultiLine | WordWrap) ? 3 : 0); + } + else + { + pos.X = CurrentTextRect.LowerRightCorner.X - font->getDimension(Text.subString(CursorPos, Text.size() - CursorPos).c_str()).Width; + pos.Y = AbsoluteRect.getCenter().Y + (Border ? 3 : 0); //bug? The text is always drawn in the height of the center. SetTextAlignment() doesn't influence. + } + + return pos; +} + + +#endif //! draws the element and its children void CGUIEditBox::draw() { @@ -1028,6 +1163,13 @@ bool CGUIEditBox::processMouse(const SEvent& event) if (Environment->hasFocus(this) && !m_rtl) { CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + if (UTF16_IS_SURROGATE_LO(Text[CursorPos])) + { + if (CursorPos > 0) + --CursorPos; + } +#endif if (MouseMarking) { setTextMarkers( MarkBegin, CursorPos ); @@ -1042,6 +1184,13 @@ bool CGUIEditBox::processMouse(const SEvent& event) if (MouseMarking) { CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + if (UTF16_IS_SURROGATE_LO(Text[CursorPos])) + { + if (CursorPos > 0) + --CursorPos; + } +#endif setTextMarkers( MarkBegin, CursorPos ); calculateScrollPos(); return true; @@ -1054,6 +1203,13 @@ bool CGUIEditBox::processMouse(const SEvent& event) BlinkStartTime = getTime(); MouseMarking = true; CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + if (UTF16_IS_SURROGATE_LO(Text[CursorPos])) + { + if (CursorPos > 0) + --CursorPos; + } +#endif setTextMarkers(CursorPos, CursorPos ); calculateScrollPos(); return true; @@ -1070,6 +1226,13 @@ bool CGUIEditBox::processMouse(const SEvent& event) // move cursor CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y); +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + if (UTF16_IS_SURROGATE_LO(Text[CursorPos])) + { + if (CursorPos > 0) + --CursorPos; + } +#endif s32 newMarkBegin = MarkBegin; if (!MouseMarking) newMarkBegin = CursorPos; diff --git a/src/guiengine/widgets/CGUIEditBox.hpp b/src/guiengine/widgets/CGUIEditBox.hpp index 790e440ff..fdc4d5e98 100644 --- a/src/guiengine/widgets/CGUIEditBox.hpp +++ b/src/guiengine/widgets/CGUIEditBox.hpp @@ -137,6 +137,11 @@ using namespace gui; bool processKey(const SEvent& event); bool processMouse(const SEvent& event); +#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) + bool processIMEEvent(const SEvent& event); + //! calculates the input composition position + core::position2di calculateICPos(); +#endif s32 getCursorPos(s32 x, s32 y); bool MouseMarking;