changeset 20709:55b7d139bae4 draft

-Change: [Win32] Draw the composition string ourselves if possible.
author Michael Lutz <michi@icosahedron.de>
date Mon, 01 Jul 2013 22:53:23 +0200
parents bd8f5e4c1617
children c1cb521445a4
files src/console_gui.cpp src/gfx_func.h src/textbuf.cpp src/textbuf_type.h src/video/win32_v.cpp src/window.cpp src/window_gui.h
diffstat 7 files changed, 101 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/src/console_gui.cpp
+++ b/src/console_gui.cpp
@@ -311,9 +311,9 @@
 		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();
--- a/src/gfx_func.h
+++ b/src/gfx_func.h
@@ -71,7 +71,7 @@
 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);
--- a/src/textbuf.cpp
+++ b/src/textbuf.cpp
@@ -151,10 +151,17 @@
  * 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 @@
 
 		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 @@
 
 	if (!GetClipboardContents(utf8_buf, lengthof(utf8_buf))) return false;
 
-	return this->InsertString(utf8_buf);
+	return this->InsertString(utf8_buf, false);
 }
 
 /** Discard any marked text. */
--- a/src/textbuf_type.h
+++ b/src/textbuf_type.h
@@ -56,7 +56,7 @@
 	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);
--- a/src/video/win32_v.cpp
+++ b/src/video/win32_v.cpp
@@ -46,6 +46,9 @@
 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;
@@ -495,6 +498,12 @@
 }
 
 #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)
 {
@@ -567,12 +576,50 @@
 			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);
 
@@ -585,10 +632,13 @@
 	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) {}
@@ -605,6 +655,9 @@
 		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:
@@ -731,13 +784,29 @@
 		}
 
 #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;
--- a/src/window.cpp
+++ b/src/window.cpp
@@ -2546,12 +2546,12 @@
  * @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);
 	}
@@ -2560,12 +2560,14 @@
 /**
  * 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);
 }
 
 /**
--- a/src/window_gui.h
+++ b/src/window_gui.h
@@ -491,7 +491,7 @@
 	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;