From 0883cf76e34527f3bf4d8a19c6f68b655969798c Mon Sep 17 00:00:00 2001 From: michi_cc Date: Mon, 5 Aug 2013 20:37:25 +0000 Subject: [PATCH] (svn r25684) -Change: [Win32] Draw the composition string ourselves if possible. --- src/console_gui.cpp | 4 +-- src/gfx_func.h | 2 +- src/textbuf.cpp | 25 ++++++++++++--- src/textbuf_type.h | 2 +- src/video/win32_v.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++- src/window.cpp | 10 +++--- src/window_gui.h | 2 +- 7 files changed, 101 insertions(+), 15 deletions(-) diff --git a/src/console_gui.cpp b/src/console_gui.cpp index 9a48f72a61..69cf7a4d7b 100644 --- a/src/console_gui.cpp +++ b/src/console_gui.cpp @@ -311,9 +311,9 @@ struct IConsoleWindow : Window return ES_HANDLED; } - virtual void InsertTextString(int wid, const char *str) + virtual void InsertTextString(int wid, const char *str, bool marked, const char *caret) { - if (_iconsole_cmdline.InsertString(str)) { + if (_iconsole_cmdline.InsertString(str, marked, caret)) { IConsoleWindow::scroll = 0; IConsoleResetHistoryPos(); this->SetDirty(); diff --git a/src/gfx_func.h b/src/gfx_func.h index 421f9cff69..5dee846b23 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -71,7 +71,7 @@ extern Dimension _cur_resolution; extern Palette _cur_palette; ///< Current palette void HandleKeypress(uint keycode, WChar key); -void HandleTextInput(const char *str); +void HandleTextInput(const char *str, bool marked = false, const char *caret = NULL); void HandleCtrlChanged(); void HandleMouseEvents(); void CSleep(int milliseconds); diff --git a/src/textbuf.cpp b/src/textbuf.cpp index aea7c49183..e99e89662e 100644 --- a/src/textbuf.cpp +++ b/src/textbuf.cpp @@ -151,10 +151,17 @@ bool Textbuf::InsertChar(WChar key) * we don't care about the visual-length but only about the physical * length of the string. * @param str String to insert. + * @param marked Replace the currently marked text with the new text. + * @param caret Move the caret to this point in the insertion string. * @return True on successful change of Textbuf, or false otherwise. */ -bool Textbuf::InsertString(const char *str) +bool Textbuf::InsertString(const char *str, bool marked, const char *caret) { + uint16 insertpos = (marked && this->marklength != 0) ? this->markpos : this->caretpos; + + if (marked) this->DiscardMarkedText(str == NULL); + + if (str == NULL) return false; uint16 bytes = 0, chars = 0; WChar c; @@ -167,16 +174,24 @@ bool Textbuf::InsertString(const char *str) bytes += len; chars++; + + /* Move caret if needed. */ + if (ptr == caret) this->caretpos = insertpos + bytes; } if (bytes == 0) return false; - memmove(this->buf + this->caretpos + bytes, this->buf + this->caretpos, this->bytes - this->caretpos); - memcpy(this->buf + this->caretpos, str, bytes); + if (marked) { + this->markpos = insertpos; + this->markend = insertpos + bytes; + } + + memmove(this->buf + insertpos + bytes, this->buf + insertpos, this->bytes - insertpos); + memcpy(this->buf + insertpos, str, bytes); this->bytes += bytes; this->chars += chars; - this->caretpos += bytes; + if (!marked && caret == NULL) this->caretpos += bytes; assert(this->bytes <= this->max_bytes); assert(this->chars <= this->max_chars); this->buf[this->bytes - 1] = '\0'; // terminating zero @@ -201,7 +216,7 @@ bool Textbuf::InsertClipboard() if (!GetClipboardContents(utf8_buf, lengthof(utf8_buf))) return false; - return this->InsertString(utf8_buf); + return this->InsertString(utf8_buf, false); } /** diff --git a/src/textbuf_type.h b/src/textbuf_type.h index 910e7fa5f6..e34e2e2f4e 100644 --- a/src/textbuf_type.h +++ b/src/textbuf_type.h @@ -56,7 +56,7 @@ struct Textbuf { bool InsertClipboard(); bool InsertChar(uint32 key); - bool InsertString(const char *str); + bool InsertString(const char *str, bool marked, const char *caret = NULL); bool DeleteChar(uint16 keycode); bool MovePos(uint16 keycode); diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index a6ca85c974..2e7b409027 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -52,6 +52,9 @@ bool _window_maximize; uint _display_hz; uint _fullscreen_bpp; static Dimension _bck_resolution; +#if !defined(WINCE) || _WIN32_WCE >= 0x400 +DWORD _imm_props; +#endif /** Whether the drawing is/may be done in a separate thread. */ static bool _draw_threaded; @@ -501,6 +504,12 @@ static LRESULT HandleCharMsg(uint keycode, WChar charcode) } #if !defined(WINCE) || _WIN32_WCE >= 0x400 +/** Should we draw the composition string ourself, i.e is this a normal IME? */ +static bool DrawIMECompositionString() +{ + return (_imm_props & IME_PROP_AT_CARET) && !(_imm_props & IME_PROP_SPECIAL_UI); +} + /** Set position of the composition window to the caret position. */ static void SetCompositionPos(HWND hwnd) { @@ -571,12 +580,50 @@ static LRESULT HandleIMEComposition(HWND hwnd, WPARAM wParam, LPARAM lParam) str[len / sizeof(TCHAR)] = '\0'; /* Transmit text to windowing system. */ - if (len > 0) HandleTextInput(FS2OTTD(str)); + if (len > 0) { + HandleTextInput(NULL, true); // Clear marked string. + HandleTextInput(FS2OTTD(str)); + } SetCompositionPos(hwnd); /* Don't pass the result string on to the default window proc. */ lParam &= ~(GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR); } + + if ((lParam & GCS_COMPSTR) && DrawIMECompositionString()) { + /* Read composition string from the IME. */ + LONG len = ImmGetCompositionString(hIMC, GCS_COMPSTR, NULL, 0); // Length is always in bytes, even in UNICODE build. + TCHAR *str = (TCHAR *)_alloca(len + sizeof(TCHAR)); + len = ImmGetCompositionString(hIMC, GCS_COMPSTR, str, len); + str[len / sizeof(TCHAR)] = '\0'; + + if (len > 0) { + static char utf8_buf[1024]; + convert_from_fs(str, utf8_buf, lengthof(utf8_buf)); + + /* Convert caret position from bytes in the input string to a position in the UTF-8 encoded string. */ + LONG caret_bytes = ImmGetCompositionString(hIMC, GCS_CURSORPOS, NULL, 0); + const char *caret = utf8_buf; + for (const TCHAR *c = str; *c != '\0' && *caret != '\0' && caret_bytes > 0; c++, caret_bytes--) { + /* Skip DBCS lead bytes or leading surrogates. */ +#ifdef UNICODE + if (Utf16IsLeadSurrogate(*c)) { +#else + if (IsDBCSLeadByte(*c)) { +#endif + c++; + caret_bytes--; + } + Utf8Consume(&caret); + } + + HandleTextInput(utf8_buf, true, caret); + } else { + HandleTextInput(NULL, true); + } + + lParam &= ~(GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART); + } } ImmReleaseContext(hwnd, hIMC); @@ -589,10 +636,13 @@ static void CancelIMEComposition(HWND hwnd) HIMC hIMC = ImmGetContext(hwnd); if (hIMC != NULL) ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0); ImmReleaseContext(hwnd, hIMC); + /* Clear any marked string from the current edit box. */ + HandleTextInput(NULL, true); } #else +static bool DrawIMECompositionString() { return false; } static void SetCompositionPos(HWND hwnd) {} static void SetCandidatePos(HWND hwnd) {} static void CancelIMEComposition(HWND hwnd) {} @@ -609,6 +659,9 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP case WM_CREATE: SetTimer(hwnd, TID_POLLMOUSE, MOUSE_POLL_DELAY, (TIMERPROC)TrackMouseTimerProc); SetCompositionPos(hwnd); +#if !defined(WINCE) || _WIN32_WCE >= 0x400 + _imm_props = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY); +#endif break; case WM_ENTERSIZEMOVE: @@ -735,13 +788,29 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP } #if !defined(WINCE) || _WIN32_WCE >= 0x400 + case WM_INPUTLANGCHANGE: + _imm_props = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY); + break; + + case WM_IME_SETCONTEXT: + /* Don't show the composition window if we draw the string ourself. */ + if (DrawIMECompositionString()) lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW; + break; + case WM_IME_STARTCOMPOSITION: SetCompositionPos(hwnd); + if (DrawIMECompositionString()) return 0; break; case WM_IME_COMPOSITION: return HandleIMEComposition(hwnd, wParam, lParam); + case WM_IME_ENDCOMPOSITION: + /* Clear any pending composition string. */ + HandleTextInput(NULL, true); + if (DrawIMECompositionString()) return 0; + break; + case WM_IME_NOTIFY: if (wParam == IMN_OPENCANDIDATE) SetCandidatePos(hwnd); break; diff --git a/src/window.cpp b/src/window.cpp index 2805301f03..1197b0c6e2 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2551,12 +2551,12 @@ void HandleCtrlChanged() * @param wid Edit box widget. * @param str Text string to insert. */ -/* virtual */ void Window::InsertTextString(int wid, const char *str) +/* virtual */ void Window::InsertTextString(int wid, const char *str, bool marked, const char *caret) { QueryString *query = this->GetQueryString(wid); if (query == NULL) return; - if (query->text.InsertString(str)) { + if (query->text.InsertString(str, marked, caret) || marked) { this->SetWidgetDirty(wid); this->OnEditboxChanged(wid); } @@ -2565,12 +2565,14 @@ void HandleCtrlChanged() /** * Handle text input. * @param str Text string to input. + * @param marked Is the input a marked composition string from an IME? + * @param caret Move the caret to this point in the insertion string. */ -void HandleTextInput(const char *str) +void HandleTextInput(const char *str, bool marked, const char *caret) { if (!EditBoxInGlobalFocus()) return; - _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->index, str); + _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->index, str, marked, caret); } /** diff --git a/src/window_gui.h b/src/window_gui.h index fe2c8ef626..0fd76cfccb 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -491,7 +491,7 @@ public: bool SetFocusedWidget(int widget_index); EventState HandleEditBoxKey(int wid, WChar key, uint16 keycode); - virtual void InsertTextString(int wid, const char *str); + virtual void InsertTextString(int wid, const char *str, bool marked, const char *caret); void HandleButtonClick(byte widget); int GetRowFromWidget(int clickpos, int widget, int padding, int line_height = -1) const;