Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSeverin <julian_eisel@web.de>2014-12-07 02:58:17 +0300
committerSeverin <julian_eisel@web.de>2014-12-07 02:58:17 +0300
commite81d077c852f43b635f10972479e36909a66d60c (patch)
tree35eabcec49938e0d66c083354fa6c0c600372a12
parent06515475b9c87c553d75481abfa600a0f7a5faf8 (diff)
Input Method Editor (IME) support for text buttons
Original patch by @random (D765) with some minor work done by @campbell and me. At this place, I'd like call out a number of people who were involved and deserve a big "Thank you!": * At the first place @randon who developed and submitted the patch * The Blendercn community which helped a lot with testing - espacially * @yuzukyo, @leon_cheung and @kjym3 * @campbellbarton, @mont29 and @sergey for their help and advises during * review * @ton who realized the importance of this early on and asked me for * reviewing We are still not finished, as this is only the first part of the implementaion, but there's more to come!
-rw-r--r--CMakeLists.txt12
-rw-r--r--SConstruct1
-rw-r--r--build_files/scons/config/win32-mingw-config.py5
-rw-r--r--build_files/scons/config/win32-vc-config.py5
-rw-r--r--build_files/scons/config/win64-mingw-config.py5
-rw-r--r--build_files/scons/config/win64-vc-config.py5
-rw-r--r--build_files/scons/tools/btools.py3
-rw-r--r--intern/ghost/CMakeLists.txt10
-rw-r--r--intern/ghost/GHOST_C-api.h24
-rw-r--r--intern/ghost/GHOST_IWindow.h23
-rw-r--r--intern/ghost/GHOST_Types.h20
-rw-r--r--intern/ghost/SConscript7
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp18
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp65
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h9
-rw-r--r--intern/ghost/intern/GHOST_Window.h16
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp13
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h19
-rw-r--r--source/blender/blenloader/intern/readfile.c3
-rw-r--r--source/blender/editors/include/UI_interface.h1
-rw-r--r--source/blender/editors/interface/CMakeLists.txt6
-rw-r--r--source/blender/editors/interface/SConscript4
-rw-r--r--source/blender/editors/interface/interface.c5
-rw-r--r--source/blender/editors/interface/interface_draw.c6
-rw-r--r--source/blender/editors/interface/interface_handlers.c107
-rw-r--r--source/blender/editors/interface/interface_intern.h6
-rw-r--r--source/blender/editors/interface/interface_widgets.c103
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h15
-rw-r--r--source/blender/windowmanager/CMakeLists.txt6
-rw-r--r--source/blender/windowmanager/SConscript4
-rw-r--r--source/blender/windowmanager/WM_api.h4
-rw-r--r--source/blender/windowmanager/WM_types.h18
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c34
-rw-r--r--source/blender/windowmanager/intern/wm_window.c17
-rw-r--r--source/blender/windowmanager/wm_event_types.h7
-rw-r--r--source/blender/windowmanager/wm_window.h5
36 files changed, 602 insertions, 9 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f2ffffeec31..4ee277b9248 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -318,6 +318,9 @@ mark_as_advanced(WITH_LIBMV_SCHUR_SPECIALIZATIONS)
option(WITH_FREESTYLE "Enable Freestyle (advanced edges rendering)" ON)
# Misc
+if(WIN32)
+ option(WITH_INPUT_IME "Enable Input Method Editor (IME) for complex Asian character input" ON)
+endif()
option(WITH_INPUT_NDOF "Enable NDOF input devices (SpaceNavigator and friends)" ${_init_INPUT_NDOF})
option(WITH_RAYOPTIMIZATION "Enable use of SIMD (SSE) optimizations for the raytracer" ON)
option(WITH_OPENNL "Enable use of Open Numerical Library" ON)
@@ -1151,6 +1154,10 @@ elseif(WIN32)
list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 advapi32 shfolder shell32 ole32 oleaut32 uuid psapi)
+ if(WITH_INPUT_IME)
+ list(APPEND PLATFORM_LINKLIBS imm32)
+ endif()
+
add_definitions(
-D_CRT_NONSTDC_NO_DEPRECATE
-D_CRT_SECURE_NO_DEPRECATE
@@ -1506,6 +1513,11 @@ elseif(WIN32)
endif()
list(APPEND PLATFORM_LINKLIBS -lshell32 -lshfolder -lgdi32 -lmsvcrt -lwinmm -lmingw32 -lm -lws2_32 -lz -lstdc++ -lole32 -luuid -lwsock32 -lpsapi)
+
+ if(WITH_INPUT_IME)
+ list(APPEND PLATFORM_LINKLIBS -limm32)
+ endif()
+
set(PLATFORM_CFLAGS "-pipe -funsigned-char -fno-strict-aliasing")
if(WITH_MINGW64)
diff --git a/SConstruct b/SConstruct
index 4c2097f8879..4dacf89fcb0 100644
--- a/SConstruct
+++ b/SConstruct
@@ -266,6 +266,7 @@ if 'blenderlite' in B.targets:
target_env_defs['WITH_BF_BOOLEAN'] = False
target_env_defs['WITH_BF_REMESH'] = False
target_env_defs['WITH_BF_PYTHON'] = False
+ target_env_defs['WITH_BF_IME'] = False
target_env_defs['WITH_BF_3DMOUSE'] = False
target_env_defs['WITH_BF_LIBMV'] = False
target_env_defs['WITH_BF_FREESTYLE'] = False
diff --git a/build_files/scons/config/win32-mingw-config.py b/build_files/scons/config/win32-mingw-config.py
index 85aa72af19c..0fb84bfa4a5 100644
--- a/build_files/scons/config/win32-mingw-config.py
+++ b/build_files/scons/config/win32-mingw-config.py
@@ -170,6 +170,8 @@ BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
WITH_BF_RAYOPTIMIZATION = True
BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse']
+WITH_BF_IME = True
+
WITH_BF_OPENMP = True
#CUDA
@@ -198,6 +200,9 @@ CC_WARN = [ '-Wall' ]
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi']
+if WITH_BF_IME:
+ LLIBS.append('-limm32')
+
PLATFORM_LINKFLAGS = ['-Xlinker', '--stack=2097152']
## DISABLED, causes linking errors!
diff --git a/build_files/scons/config/win32-vc-config.py b/build_files/scons/config/win32-vc-config.py
index 967e9636fc2..d447a1ec08e 100644
--- a/build_files/scons/config/win32-vc-config.py
+++ b/build_files/scons/config/win32-vc-config.py
@@ -151,6 +151,8 @@ BF_OPENCOLLADA_INC = '${BF_OPENCOLLADA}/include/opencollada'
BF_OPENCOLLADA_LIB = 'OpenCOLLADAStreamWriter OpenCOLLADASaxFrameworkLoader OpenCOLLADAFramework OpenCOLLADABaseUtils GeneratedSaxParser MathMLSolver xml pcre buffer ftoa'
BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib/opencollada'
+WITH_BF_IME = True
+
WITH_BF_3DMOUSE = True
WITH_BF_OPENMP = True
@@ -237,6 +239,9 @@ CXX_WARN = []
LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi']
+if WITH_BF_IME:
+ LLIBS.append('imm32')
+
PLATFORM_LINKFLAGS = ['/SUBSYSTEM:CONSOLE','/MACHINE:IX86','/STACK:2097152','/INCREMENTAL:NO', '/LARGEADDRESSAWARE', '/NODEFAULTLIB:msvcrt.lib', '/NODEFAULTLIB:msvcmrt.lib', '/NODEFAULTLIB:msvcurt.lib', '/NODEFAULTLIB:msvcrtd.lib']
# # Todo
diff --git a/build_files/scons/config/win64-mingw-config.py b/build_files/scons/config/win64-mingw-config.py
index 0314ab13d07..f93be284813 100644
--- a/build_files/scons/config/win64-mingw-config.py
+++ b/build_files/scons/config/win64-mingw-config.py
@@ -170,6 +170,8 @@ BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
WITH_BF_RAYOPTIMIZATION = True
BF_RAYOPTIMIZATION_SSE_FLAGS = ['-mmmx', '-msse', '-msse2']
+WITH_BF_IME = True
+
WITH_BF_OPENMP = True
#Freestyle
@@ -193,6 +195,9 @@ CC_WARN = [ '-Wall' ]
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi', '-lpthread']
+if WITH_BF_IME:
+ LLIBS.append('-limm32')
+
PLATFORM_LINKFLAGS = ['-Xlinker', '--stack=2097152']
## DISABLED, causes linking errors!
diff --git a/build_files/scons/config/win64-vc-config.py b/build_files/scons/config/win64-vc-config.py
index 74d8c204963..b8e4f3d70fe 100644
--- a/build_files/scons/config/win64-vc-config.py
+++ b/build_files/scons/config/win64-vc-config.py
@@ -154,6 +154,8 @@ BF_OPENCOLLADA_INC = '${BF_OPENCOLLADA}/include/opencollada'
BF_OPENCOLLADA_LIB = 'OpenCOLLADAStreamWriter OpenCOLLADASaxFrameworkLoader OpenCOLLADAFramework OpenCOLLADABaseUtils GeneratedSaxParser MathMLSolver xml pcre buffer ftoa'
BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib/opencollada'
+WITH_BF_IME = True
+
WITH_BF_3DMOUSE = True
WITH_BF_OPENMP = True
@@ -244,6 +246,9 @@ CXX_WARN = []
LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi']
+if WITH_BF_IME:
+ LLIBS.append('imm32')
+
PLATFORM_LINKFLAGS = ['/SUBSYSTEM:CONSOLE','/MACHINE:X64','/STACK:2097152','/OPT:NOREF','/INCREMENTAL:NO', '/NODEFAULTLIB:msvcrt.lib', '/NODEFAULTLIB:msvcmrt.lib', '/NODEFAULTLIB:msvcurt.lib', '/NODEFAULTLIB:msvcrtd.lib']
BF_CYCLES_CUDA_ENV="C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd"
diff --git a/build_files/scons/tools/btools.py b/build_files/scons/tools/btools.py
index cf9962d1bff..eb5036f9996 100644
--- a/build_files/scons/tools/btools.py
+++ b/build_files/scons/tools/btools.py
@@ -174,6 +174,7 @@ def validate_arguments(args, bc):
'WITH_BF_CXX_GUARDEDALLOC',
'WITH_BF_JEMALLOC', 'WITH_BF_STATICJEMALLOC', 'BF_JEMALLOC', 'BF_JEMALLOC_INC', 'BF_JEMALLOC_LIBPATH', 'BF_JEMALLOC_LIB', 'BF_JEMALLOC_LIB_STATIC',
'BUILDBOT_BRANCH',
+ 'WITH_BF_IME',
'WITH_BF_3DMOUSE', 'WITH_BF_STATIC3DMOUSE', 'BF_3DMOUSE', 'BF_3DMOUSE_INC', 'BF_3DMOUSE_LIB', 'BF_3DMOUSE_LIBPATH', 'BF_3DMOUSE_LIB_STATIC',
'WITH_BF_CYCLES', 'WITH_BF_CYCLES_CUDA_BINARIES', 'BF_CYCLES_CUDA_NVCC', 'BF_CYCLES_CUDA_NVCC', 'WITH_BF_CYCLES_CUDA_THREADED_COMPILE', 'BF_CYCLES_CUDA_ENV',
'WITH_BF_OIIO', 'WITH_BF_STATICOIIO', 'BF_OIIO', 'BF_OIIO_INC', 'BF_OIIO_LIB', 'BF_OIIO_LIB_STATIC', 'BF_OIIO_LIBPATH',
@@ -507,6 +508,8 @@ def read_opts(env, cfg, args):
(BoolVariable('WITH_BF_PLAYER', 'Build blenderplayer if true', False)),
(BoolVariable('WITH_BF_NOBLENDER', 'Do not build blender if true', False)),
+ (BoolVariable('WITH_BF_IME', 'Enable Input Method Editor (IME) for complex Asian character input', False)),
+
(BoolVariable('WITH_BF_3DMOUSE', 'Build blender with support of 3D mouses', False)),
(BoolVariable('WITH_BF_STATIC3DMOUSE', 'Staticly link to 3d mouse library', False)),
('BF_3DMOUSE', '3d mouse library base path', ''),
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 3ce269c2b4f..dc55a81b0d8 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -280,6 +280,16 @@ elseif(WIN32)
)
endif()
+ if(WITH_INPUT_IME)
+ add_definitions(-DWITH_INPUT_IME)
+
+ list(APPEND SRC
+ intern/GHOST_ImeWin32.cpp
+
+ intern/GHOST_ImeWin32.h
+ )
+ endif()
+
if(WITH_INPUT_NDOF)
list(APPEND SRC
intern/GHOST_NDOFManagerWin32.cpp
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index 7b47f0526a2..c0f2651dd8b 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -897,6 +897,30 @@ extern int GHOST_UseNativePixels(void);
*/
extern float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle);
+/**
+ * Enable IME attached to the given window, i.e. allows user-input
+ * events to be dispatched to the IME.
+ * \param windowhandle Window handle of the caller
+ * \param x Requested x-coordinate of the rectangle
+ * \param y Requested y-coordinate of the rectangle
+ * \param w Requested width of the rectangle
+ * \param h Requested height of the rectangle
+ * \param complete Whether or not to complete the ongoing composition
+ * true: Start a new composition
+ * false: Move the IME windows to the given position without finishing it.
+ */
+extern void GHOST_BeginIME(GHOST_WindowHandle windowhandle,
+ GHOST_TInt32 x,
+ GHOST_TInt32 y,
+ GHOST_TInt32 w,
+ GHOST_TInt32 h,
+ int complete);
+/**
+ * Disable the IME attached to the given window, i.e. prohibits any user-input
+ * events from being dispatched to the IME.
+ * \param windowhandle The window handle of the caller
+ */
+extern void GHOST_EndIME(GHOST_WindowHandle windowhandle);
#ifdef __cplusplus
}
diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h
index 71dc193a81b..3f8215dc7c2 100644
--- a/intern/ghost/GHOST_IWindow.h
+++ b/intern/ghost/GHOST_IWindow.h
@@ -331,6 +331,29 @@ public:
virtual float getNativePixelSize(void) = 0;
+#ifdef WITH_INPUT_IME
+ /**
+ * Enable IME attached to the given window, i.e. allows user-input
+ * events to be dispatched to the IME.
+ * \param x Requested x-coordinate of the rectangle
+ * \param y Requested y-coordinate of the rectangle
+ * \param w Requested width of the rectangle
+ * \param h Requested height of the rectangle
+ * \param complete Whether or not to complete the ongoing composition
+ * true: Start a new composition
+ * false: Move the IME windows to the given position without finishing it.
+ */
+ virtual void beginIME(
+ GHOST_TInt32 x, GHOST_TInt32 y,
+ GHOST_TInt32 w, GHOST_TInt32 h,
+ int completed) = 0;
+
+ /**
+ * Disable the IME attached to the given window, i.e. prohibits any user-input
+ * events from being dispatched to the IME.
+ */
+ virtual void endIME() = 0;
+#endif /* WITH_INPUT_IME */
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_IWindow")
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index 7333ba025a5..c4a7490c71c 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -190,6 +190,10 @@ typedef enum {
GHOST_kEventTimer,
+ GHOST_kEventImeCompositionStart,
+ GHOST_kEventImeComposition,
+ GHOST_kEventImeCompositionEnd,
+
GHOST_kNumEventTypes
} GHOST_TEventType;
@@ -436,6 +440,22 @@ typedef struct {
GHOST_TEventDataPtr data;
} GHOST_TEventDragnDropData;
+/** similar to wmImeData */
+typedef struct {
+ /** size_t */
+ GHOST_TUserDataPtr result_len, composite_len;
+ /** char * utf8 encoding */
+ GHOST_TUserDataPtr result, composite;
+ /** Cursor position in the IME composition. */
+ int cursor_position;
+ /** Represents the position of the beginning of the selection */
+ int target_start;
+ /** Represents the position of the end of the selection */
+ int target_end;
+ /** custom temporal data */
+ GHOST_TUserDataPtr tmp;
+} GHOST_TEventImeData;
+
typedef struct {
int count;
GHOST_TUns8 **strings;
diff --git a/intern/ghost/SConscript b/intern/ghost/SConscript
index 5a4572c164d..a41fadf9ac7 100644
--- a/intern/ghost/SConscript
+++ b/intern/ghost/SConscript
@@ -154,6 +154,13 @@ if env['BF_GHOST_DEBUG']:
else:
sources.remove('intern' + os.sep + 'GHOST_EventPrinter.cpp')
+if env['WITH_BF_IME']:
+ if window_system in ('win32-vc', 'win32-mingw', 'win64-vc', 'win64-mingw'):
+ defs.append('WITH_INPUT_IME')
+ else:
+ sources.remove('intern' + os.sep + 'GHOST_ImeWin32.h')
+ sources.remove('intern' + os.sep + 'GHOST_ImeWin32.cpp')
+
if env['WITH_BF_3DMOUSE']:
defs.append('WITH_INPUT_NDOF')
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index 5a2e638f01a..0da77ac5e20 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -915,3 +915,21 @@ float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle)
return 1.0f;
}
+#ifdef WITH_INPUT_IME
+
+void GHOST_BeginIME(GHOST_WindowHandle windowhandle,
+ GHOST_TInt32 x, GHOST_TInt32 y,
+ GHOST_TInt32 w, GHOST_TInt32 h,
+ int complete)
+{
+ GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
+ window->beginIME(x, y, w, h, complete);
+}
+
+void GHOST_EndIME(GHOST_WindowHandle windowhandle)
+{
+ GHOST_IWindow *window = (GHOST_IWindow *) windowhandle;
+ window->endIME();
+}
+
+#endif /* WITH_INPUT_IME */
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 3b793fda968..4247c4f31ce 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -792,6 +792,15 @@ GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_
return new GHOST_Event(system->getMilliSeconds(), type, window);
}
+#ifdef WITH_INPUT_IME
+GHOST_Event *GHOST_SystemWin32::processImeEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TEventImeData *data)
+{
+ GHOST_System *system = (GHOST_System *)getSystem();
+ return new GHOST_EventIME(system->getMilliSeconds(), type, window, data);
+}
+#endif
+
+
GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(
GHOST_TEventType eventType,
GHOST_TDragnDropTypes draggedObjectType,
@@ -904,6 +913,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
LRESULT lResult = 0;
GHOST_SystemWin32 *system = ((GHOST_SystemWin32 *)getSystem());
+ GHOST_EventManager *eventManager = system->getEventManager();
GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized");
if (hwnd) {
@@ -912,8 +922,13 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
switch (msg) {
// we need to check if new key layout has AltGr
case WM_INPUTLANGCHANGE:
+ {
system->handleKeyboardChange();
+#ifdef WITH_INPUT_IME
+ window->getImeInput()->SetInputLanguage();
+#endif
break;
+ }
////////////////////////////////////////////////////////////////////////
// Keyboard events, processed
////////////////////////////////////////////////////////////////////////
@@ -949,6 +964,56 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
break;
}
+#ifdef WITH_INPUT_IME
+ ////////////////////////////////////////////////////////////////////////
+ // IME events, processed, read more in GHOST_IME.h
+ ////////////////////////////////////////////////////////////////////////
+ case WM_IME_SETCONTEXT:
+ {
+ window->getImeInput()->SetInputLanguage();
+ window->getImeInput()->CreateImeWindow(window->getHWND());
+ window->getImeInput()->CleanupComposition(window->getHWND());
+ window->getImeInput()->CheckFirst(window->getHWND());
+ break;
+ }
+ case WM_IME_STARTCOMPOSITION:
+ {
+ eventHandled = true;
+ /* remove input event before start comp event, avoid redundant input */
+ eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
+ window->getImeInput()->CreateImeWindow(window->getHWND());
+ window->getImeInput()->ResetComposition(window->getHWND());
+ event = processImeEvent(
+ GHOST_kEventImeCompositionStart,
+ window,
+ &window->getImeInput()->eventImeData);
+ break;
+ }
+ case WM_IME_COMPOSITION:
+ {
+ eventHandled = true;
+ window->getImeInput()->UpdateImeWindow(window->getHWND());
+ window->getImeInput()->UpdateInfo(window->getHWND());
+ event = processImeEvent(
+ GHOST_kEventImeComposition,
+ window,
+ &window->getImeInput()->eventImeData);
+ break;
+ }
+ case WM_IME_ENDCOMPOSITION:
+ {
+ eventHandled = true;
+ /* remove input event after end comp event, avoid redundant input */
+ eventManager->removeTypeEvents(GHOST_kEventKeyDown, window);
+ window->getImeInput()->ResetComposition(window->getHWND());
+ window->getImeInput()->DestroyImeWindow(window->getHWND());
+ event = processImeEvent(
+ GHOST_kEventImeCompositionEnd,
+ window,
+ &window->getImeInput()->eventImeData);
+ break;
+ }
+#endif /* WITH_INPUT_IME */
////////////////////////////////////////////////////////////////////////
// Keyboard events, ignored
////////////////////////////////////////////////////////////////////////
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 79fed06c6a5..cb3b8ee3cfc 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -307,6 +307,15 @@ protected:
static GHOST_Event *processWindowEvent(GHOST_TEventType type, GHOST_IWindow *window);
/**
+ * Creates a IME event.
+ * \param type The type of event to create.
+ * \param window The window receiving the event (the active window).
+ * \param data IME data.
+ * \return The event created.
+ */
+ static GHOST_Event *processImeEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TEventImeData *data);
+
+ /**
* Handles minimum window size.
* \param minmax The MINMAXINFO structure.
*/
diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h
index 15b81998aab..ac31c54666b 100644
--- a/intern/ghost/intern/GHOST_Window.h
+++ b/intern/ghost/intern/GHOST_Window.h
@@ -295,6 +295,22 @@ public:
return 1.0f;
}
+#ifdef WITH_INPUT_IME
+ virtual void beginIME(GHOST_TInt32 x,
+ GHOST_TInt32 y,
+ GHOST_TInt32 w,
+ GHOST_TInt32 h,
+ int completed)
+ {
+ /* do nothing temporarily if not in windows */
+ }
+
+ virtual void endIME()
+ {
+ /* do nothing temporarily if not in windows */
+ }
+#endif /* WITH_INPUT_IME */
+
protected:
/**
* Tries to install a rendering context in this window.
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index 64ea7192616..4e384881f2c 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -1050,3 +1050,16 @@ GHOST_TSuccess GHOST_WindowWin32::endProgressBar()
return GHOST_kFailure;
}
+
+#ifdef WITH_INPUT_IME
+void GHOST_WindowWin32::beginIME(GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed)
+{
+ this->getImeInput()->BeginIME(this->getHWND(), GHOST_Rect(x, y - h , x, y), (bool)completed);
+}
+
+
+void GHOST_WindowWin32::endIME()
+{
+ this->getImeInput()->EndIME(this->getHWND());
+}
+#endif /* WITH_INPUT_IME */
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index 7b12d8c583e..c4575d0f9b0 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -39,6 +39,9 @@
#include "GHOST_Window.h"
#include "GHOST_TaskbarWin32.h"
+#ifdef WITH_INPUT_IME
+# include "GHOST_ImeWin32.h"
+#endif
#include <wintab.h>
#define PACKETDATA (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR)
@@ -253,6 +256,17 @@ public:
/** if the window currently resizing */
bool m_inLiveResize;
+#ifdef WITH_INPUT_IME
+ GHOST_ImeWin32 *getImeInput() {return &m_imeImput;}
+
+ virtual void beginIME(
+ GHOST_TInt32 x, GHOST_TInt32 y,
+ GHOST_TInt32 w, GHOST_TInt32 h,
+ int completed);
+
+ virtual void endIME();
+#endif /* WITH_INPUT_IME */
+
private:
/**
@@ -339,6 +353,11 @@ private:
/** Hwnd to parent window */
GHOST_TEmbedderWindowID m_parentWindowHwnd;
+
+#ifdef WITH_INPUT_IME
+ /** Handle input method editors event */
+ GHOST_ImeWin32 m_imeImput;
+#endif
};
#endif // __GHOST_WINDOWWIN32_H__
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index ac903c7a8d2..0950edac46a 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -5677,6 +5677,9 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
win->eventstate = NULL;
win->curswin = NULL;
win->tweak = NULL;
+#ifdef WIN32
+ win->ime_data = NULL;
+#endif
BLI_listbase_clear(&win->queue);
BLI_listbase_clear(&win->handlers);
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 42f67379dfd..2251f3fd0e4 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -313,6 +313,7 @@ void UI_draw_box_shadow(unsigned char alpha, float minx, float miny, float maxx,
void UI_draw_roundbox_gl_mode(int mode, float minx, float miny, float maxx, float maxy, float rad);
void UI_draw_roundbox_shade_x(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown);
void UI_draw_roundbox_shade_y(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadeLeft, float shadeRight);
+void UI_draw_text_underline(int pos_x, int pos_y, int len, int height);
/* state for scrolldrawing */
#define UI_SCROLL_PRESSED (1 << 0)
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index b921d17104c..972eca747b9 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -71,6 +71,12 @@ if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
+if(WIN32)
+ if(WITH_INPUT_IME)
+ add_definitions(-DWITH_INPUT_IME)
+ endif()
+endif()
+
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_editor_interface "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/interface/SConscript b/source/blender/editors/interface/SConscript
index 303ab7ff286..5af8bba5a9f 100644
--- a/source/blender/editors/interface/SConscript
+++ b/source/blender/editors/interface/SConscript
@@ -48,6 +48,10 @@ incs = [
defs = env['BF_GL_DEFINITIONS']
+if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'win64-vc', 'win64-mingw'):
+ if env['WITH_BF_IME']:
+ defs.append('WITH_INPUT_IME')
+
if env['WITH_BF_INTERNATIONAL']:
defs.append('WITH_INTERNATIONAL')
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 92017e7a967..4cbb02c4dd5 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -209,6 +209,11 @@ void ui_window_to_region(const ARegion *ar, int *x, int *y)
*y -= ar->winrct.ymin;
}
+void ui_region_to_window(const ARegion *ar, int *x, int *y)
+{
+ *x += ar->winrct.xmin;
+ *y += ar->winrct.ymin;
+}
/* ******************* block calc ************************* */
void ui_block_translate(uiBlock *block, int x, int y)
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 4dd952f0753..bcd9b9a5547 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -395,6 +395,12 @@ void UI_draw_roundbox(float minx, float miny, float maxx, float maxy, float rad)
ui_draw_anti_roundbox(GL_POLYGON, minx, miny, maxx, maxy, rad, roundboxtype & UI_RB_ALPHA);
}
+void UI_draw_text_underline(int pos_x, int pos_y, int len, int height)
+{
+ int ofs_y = 4 * U.pixelsize;
+ glRecti(pos_x, pos_y - ofs_y, pos_x + len, pos_y - ofs_y + (height * U.pixelsize));
+}
+
/* ************** SPECIAL BUTTON DRAWING FUNCTIONS ************* */
void ui_draw_but_IMAGE(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *rect)
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 3a312808cee..b39c786fba7 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -87,6 +87,10 @@
#include "WM_api.h"
#include "WM_types.h"
+#ifdef WITH_INPUT_IME
+# include "wm_window.h"
+#endif
+
/* place the mouse at the scaled down location when un-grabbing */
#define USE_CONT_MOUSE_CORRECT
/* support dragging toggle buttons */
@@ -2425,8 +2429,50 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in
return changed;
}
+#ifdef WITH_INPUT_IME
+/* enable ime, and set up uibut ime data */
+static void ui_textedit_ime_begin(wmWindow *win, uiBut *UNUSED(but))
+{
+ /* XXX Is this really needed? */
+ int x, y;
+
+ /* enable IME and position to cursor, it's a trick */
+ x = win->eventstate->x;
+ /* flip y and move down a bit, prevent the IME panel cover the edit button */
+ y = win->eventstate->y - 12;
+
+ wm_window_IME_begin(win, x, y, 0, 0, true);
+}
+
+/* disable ime, and clear uibut ime data */
+static void ui_textedit_ime_end(wmWindow *win, uiBut *UNUSED(but))
+{
+ wm_window_IME_end(win);
+}
+
+void ui_but_ime_reposition(uiBut *but, int x, int y, bool complete)
+{
+ BLI_assert(but->active);
+
+ ui_region_to_window(but->active->region, &x, &y);
+ wm_window_IME_begin(but->active->window, x, y - 4, 0, 0, complete);
+}
+
+/* should be ui_but_ime_data_get */
+wmIMEData *ui_but_get_ime_data(uiBut *but)
+{
+ if (but->active && but->active->window) {
+ return but->active->window->ime_data;
+ }
+ else {
+ return NULL;
+ }
+}
+#endif /* WITH_INPUT_IME */
+
static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
{
+ wmWindow *win = CTX_wm_window(C);
int len;
if (data->str) {
@@ -2482,12 +2528,18 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
but->flag &= ~UI_BUT_REDALERT;
ui_but_update(but);
-
- WM_cursor_modal_set(CTX_wm_window(C), BC_TEXTEDITCURSOR);
+
+ WM_cursor_modal_set(win, BC_TEXTEDITCURSOR);
+
+#ifdef WITH_INPUT_IME
+ ui_textedit_ime_begin(win, but);
+#endif
}
static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
{
+ wmWindow *win = CTX_wm_window(C);
+
if (but) {
if (ui_but_is_utf8(but)) {
int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr));
@@ -2518,7 +2570,11 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
but->pos = -1;
}
- WM_cursor_modal_restore(CTX_wm_window(C));
+ WM_cursor_modal_restore(win);
+
+#ifdef WITH_INPUT_IME
+ ui_textedit_ime_end(win, but);
+#endif
}
static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
@@ -2583,6 +2639,14 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
int retval = WM_UI_HANDLER_CONTINUE;
bool changed = false, inbox = false, update = false;
+#ifdef WITH_INPUT_IME
+ wmWindow *win = CTX_wm_window(C);
+ wmIMEData *ime_data = win->ime_data;
+ bool is_ime_composing = ime_data && ime_data->is_ime_composing;
+#else
+ bool is_ime_composing = false;
+#endif
+
switch (event->type) {
case MOUSEMOVE:
case MOUSEPAN:
@@ -2603,6 +2667,12 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
case RIGHTMOUSE:
case ESCKEY:
if (event->val == KM_PRESS) {
+#ifdef WITH_INPUT_IME
+ /* skips button handling since it is not wanted */
+ if (is_ime_composing) {
+ break;
+ }
+#endif
data->cancel = true;
data->escapecancel = true;
button_activate_state(C, but, BUTTON_STATE_EXIT);
@@ -2660,7 +2730,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
}
}
- if (event->val == KM_PRESS) {
+ if (event->val == KM_PRESS && !is_ime_composing) {
switch (event->type) {
case VKEY:
case XKEY:
@@ -2776,7 +2846,15 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
break;
}
- if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)) {
+ if ((event->ascii || event->utf8_buf[0]) &&
+ (retval == WM_UI_HANDLER_CONTINUE)
+#ifdef WITH_INPUT_IME
+ &&
+ !is_ime_composing &&
+ !WM_event_is_ime_switch(event)
+#endif
+ )
+ {
char ascii = event->ascii;
const char *utf8_buf = event->utf8_buf;
@@ -2811,6 +2889,25 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
update = true;
}
+#ifdef WITH_INPUT_IME
+ if (event->type == WM_IME_COMPOSITE_START || event->type == WM_IME_COMPOSITE_EVENT) {
+ changed = true;
+
+ if (event->type == WM_IME_COMPOSITE_START && but->selend > but->selsta) {
+ ui_textedit_delete_selection(but, data);
+ }
+ if (event->type == WM_IME_COMPOSITE_EVENT && ime_data->result_len) {
+ ui_textedit_type_buf(
+ but, data,
+ ime_data->str_result,
+ ime_data->result_len);
+ }
+ }
+ else if (event->type == WM_IME_COMPOSITE_END) {
+ changed = true;
+ }
+#endif
+
if (changed) {
/* only update when typing for TAB key */
if (update && data->interactive) {
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 328822e47ef..2c3a239b2a2 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -433,6 +433,7 @@ extern void ui_block_to_window_rctf(const struct ARegion *ar, uiBlock *block, rc
extern void ui_window_to_block_fl(const struct ARegion *ar, uiBlock *block, float *x, float *y);
extern void ui_window_to_block(const struct ARegion *ar, uiBlock *block, int *x, int *y);
extern void ui_window_to_region(const ARegion *ar, int *x, int *y);
+extern void ui_region_to_window(const struct ARegion *ar, int *x, int *y);
extern double ui_but_value_get(uiBut *but);
extern void ui_but_value_set(uiBut *but, double value);
@@ -627,6 +628,11 @@ void ui_panel_menu(struct bContext *C, ARegion *ar, Panel *pa);
uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new);
uiBut *ui_but_find_new(uiBlock *block_old, const uiBut *but_new);
+#ifdef WITH_INPUT_IME
+void ui_but_ime_reposition(uiBut *but, int x, int y, bool complete);
+struct wmIMEData *ui_but_get_ime_data(uiBut *but);
+#endif
+
/* interface_widgets.c */
void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3);
void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy, float rad, bool use_alpha);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index c9def075666..d6c51b6b220 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -59,6 +59,10 @@
#include "interface_intern.h"
+#ifdef WITH_INPUT_IME
+# include "WM_types.h"
+#endif
+
/* icons are 80% of height of button (16 pixels inside 20 height) */
#define ICON_SIZE_FROM_BUTRECT(rect) (0.8f * BLI_rcti_size_y(rect))
@@ -1232,6 +1236,50 @@ static void ui_text_clip_right_label(uiFontStyle *fstyle, uiBut *but, const rcti
BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
}
+#ifdef WITH_INPUT_IME
+static void widget_draw_text_ime_underline(
+ uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, const rcti *rect,
+ const wmIMEData *ime_data, const char *drawstr)
+{
+ int ofs_x, width;
+ int rect_x = BLI_rcti_size_x(rect);
+ int sel_start = ime_data->sel_start, sel_end = ime_data->sel_end;
+
+ if (drawstr[0] != 0) {
+ if (but->pos >= but->ofs) {
+ ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->pos - but->ofs);
+ }
+ else {
+ ofs_x = 0;
+ }
+
+ width = BLF_width(fstyle->uifont_id, drawstr + but->ofs,
+ ime_data->composite_len + but->pos - but->ofs);
+
+ glColor4ubv((unsigned char *)wcol->text);
+ UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 1);
+
+ /* draw the thick line */
+ if (sel_start != -1 && sel_end != -1) {
+ sel_end -= sel_start;
+ sel_start += but->pos;
+
+ if (sel_start >= but->ofs) {
+ ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, sel_start - but->ofs);
+ }
+ else {
+ ofs_x = 0;
+ }
+
+ width = BLF_width(fstyle->uifont_id, drawstr + but->ofs,
+ sel_end + sel_start - but->ofs);
+
+ UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 2);
+ }
+ }
+}
+#endif /* WITH_INPUT_IME */
+
static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect)
{
int drawstr_left_len = UI_MAX_DRAW_STR;
@@ -1239,6 +1287,10 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
const char *drawstr_right = NULL;
bool use_right_only = false;
+#ifdef WITH_INPUT_IME
+ const wmIMEData *ime_data;
+#endif
+
UI_fontstyle_set(fstyle);
if (but->editstr || (but->drawflag & UI_BUT_TEXT_LEFT))
@@ -1266,13 +1318,30 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
/* max length isn't used in this case,
* we rely on string being NULL terminated. */
drawstr_left_len = INT_MAX;
- drawstr = but->editstr;
+
+#ifdef WITH_INPUT_IME
+ /* FIXME, IME is modifying 'const char *drawstr! */
+ ime_data = ui_but_get_ime_data(but);
+
+ if (ime_data && ime_data->composite_len) {
+ /* insert composite string into cursor pos */
+ BLI_snprintf((char *)drawstr, UI_MAX_DRAW_STR, "%s%s%s",
+ but->editstr, ime_data->str_composite,
+ but->editstr + but->pos);
+ }
+ else
+#endif
+ {
+ drawstr = but->editstr;
+ }
}
}
- /* text button selection and cursor */
+ /* text button selection, cursor, composite underline */
if (but->editstr && but->pos != -1) {
+ int but_pos_ofs;
+ int tx, ty;
/* text button selection */
if ((but->selend - but->selsta) > 0) {
@@ -1298,18 +1367,44 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
}
/* text cursor */
+ but_pos_ofs = but->pos;
+
+#ifdef WITH_INPUT_IME
+ /* if is ime compositing, move the cursor */
+ if (ime_data && ime_data->composite_len && ime_data->cursor_pos != -1) {
+ but_pos_ofs += ime_data->cursor_pos;
+ }
+#endif
+
if (but->pos >= but->ofs) {
int t;
if (drawstr[0] != 0) {
- t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->pos - but->ofs);
+ t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but_pos_ofs - but->ofs);
}
else {
t = 0;
}
glColor3f(0.20, 0.6, 0.9);
- glRecti(rect->xmin + t, rect->ymin + 2, rect->xmin + t + 2, rect->ymax - 2);
+
+ tx = rect->xmin + t + 2;
+ ty = rect->ymin + 2;
+
+ /* draw cursor */
+ glRecti(rect->xmin + t, ty, tx, rect->ymax - 2);
}
+
+#ifdef WITH_INPUT_IME
+ if (ime_data && ime_data->composite_len) {
+ /* ime cursor following */
+ if (but->pos >= but->ofs) {
+ ui_but_ime_reposition(but, tx + 5, ty + 3, false);
+ }
+
+ /* composite underline */
+ widget_draw_text_ime_underline(fstyle, wcol, but, rect, ime_data, drawstr);
+ }
+#endif
}
if (fstyle->kerning == 1)
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 73a70b48712..1a0562ae8c5 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -165,6 +165,13 @@ enum {
WM_INIT_KEYMAP = (1<<1),
};
+/* IME is win32 only! */
+#ifndef WIN32
+# ifdef __GNUC__
+# define ime_data ime_data __attribute__ ((deprecated))
+# endif
+#endif
+
/* the savable part, rest of data is local in ghostwinlay */
typedef struct wmWindow {
struct wmWindow *next, *prev;
@@ -197,6 +204,10 @@ typedef struct wmWindow {
struct wmGesture *tweak; /* internal for wm_operators.c */
+ /* Input Method Editor data - complex character input (esp. for asian character input)
+ * Currently WIN32, runtime-only data */
+ struct wmIMEData *ime_data;
+
int drawmethod, drawfail; /* internal for wm_draw.c only */
void *drawdata; /* internal for wm_draw.c only */
@@ -208,6 +219,10 @@ typedef struct wmWindow {
ListBase gesture; /* gesture stuff */
} wmWindow;
+#ifdef ime_data
+# undef ime_data
+#endif
+
/* These two Lines with # tell makesdna this struct can be excluded. */
/* should be something like DNA_EXCLUDE
* but the preprocessor first removes all comments, spaces etc */
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 4cbadda1732..78b5d499644 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -130,6 +130,12 @@ if(WITH_BUILDINFO)
add_definitions(-DWITH_BUILDINFO)
endif()
+if(WIN32)
+ if(WITH_INPUT_IME)
+ add_definitions(-DWITH_INPUT_IME)
+ endif()
+endif()
+
if(WITH_COMPOSITOR)
add_definitions(-DWITH_COMPOSITOR)
endif()
diff --git a/source/blender/windowmanager/SConscript b/source/blender/windowmanager/SConscript
index 3c44ff6107c..a6f64f7cdae 100644
--- a/source/blender/windowmanager/SConscript
+++ b/source/blender/windowmanager/SConscript
@@ -69,6 +69,10 @@ if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', '
if env['BF_BUILDINFO']:
defs.append('WITH_BUILDINFO')
+if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'win64-vc', 'win64-mingw'):
+ if env['WITH_BF_IME']:
+ defs.append('WITH_INPUT_IME')
+
if env['WITH_BF_INTERNATIONAL']:
defs.append('WITH_INTERNATIONAL')
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index ced7222b046..d2abfd419d1 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -462,6 +462,10 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4
float WM_event_tablet_data(const struct wmEvent *event, int *pen_flip, float tilt[2]);
bool WM_event_is_tablet(const struct wmEvent *event);
+#ifdef WITH_INPUT_IME
+bool WM_event_is_ime_switch(const struct wmEvent *event);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 4b6b5e95366..d9924cfb053 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -570,6 +570,24 @@ typedef struct wmOperatorType {
} wmOperatorType;
+#ifdef WITH_INPUT_IME
+/* *********** Input Method Editor (IME) *********** */
+
+/* similar to GHOST_TEventImeData */
+typedef struct wmIMEData {
+ size_t result_len, composite_len;
+
+ char *str_result; /* utf8 encoding */
+ char *str_composite; /* utf8 encoding */
+
+ int cursor_pos; /* cursor position in the IME composition. */
+ int sel_start; /* beginning of the selection */
+ int sel_end; /* end of the selection */
+
+ bool is_ime_composing;
+} wmIMEData;
+#endif
+
/* **************** Paint Cursor ******************* */
typedef void (*wmPaintCursorDraw)(struct bContext *C, int, int, void *customdata);
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index cca267a9fbb..3de34e7ca3f 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -3373,6 +3373,33 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
break;
}
+#ifdef WITH_INPUT_IME
+ case GHOST_kEventImeCompositionStart:
+ {
+ event.val = KM_PRESS;
+ win->ime_data = customdata;
+ win->ime_data->is_ime_composing = true;
+ event.type = WM_IME_COMPOSITE_START;
+ wm_event_add(win, &event);
+ break;
+ }
+ case GHOST_kEventImeComposition:
+ {
+ event.val = KM_PRESS;
+ event.type = WM_IME_COMPOSITE_EVENT;
+ wm_event_add(win, &event);
+ break;
+ }
+ case GHOST_kEventImeCompositionEnd:
+ {
+ event.val = KM_PRESS;
+ win->ime_data->is_ime_composing = false;
+ event.type = WM_IME_COMPOSITE_END;
+ wm_event_add(win, &event);
+ break;
+ }
+#endif /* WITH_INPUT_IME */
+
}
#if 0
@@ -3479,5 +3506,12 @@ bool WM_event_is_tablet(const struct wmEvent *event)
return (event->tablet_data) ? true : false;
}
+#ifdef WITH_INPUT_IME
+/* most os using ctrl/oskey + space to switch ime, avoid added space */
+bool WM_event_is_ime_switch(const struct wmEvent *event) {
+ return event->val == KM_PRESS && event->type == SPACEKEY &&
+ (event->ctrl || event->oskey || event->shift || event->alt);
+}
+#endif
/** \} */
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 46a20d3bf88..0b33dbb2c32 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -1524,3 +1524,20 @@ bool WM_window_is_fullscreen(wmWindow *win)
return win->windowstate == GHOST_kWindowStateFullScreen;
}
+
+#ifdef WITH_INPUT_IME
+void wm_window_IME_begin(wmWindow *win, int x, int y, int w, int h, bool complete)
+{
+ BLI_assert(win && (win->ime_data == NULL));
+
+ GHOST_BeginIME(win->ghostwin, x, win->sizey - y, w, h, complete);
+}
+
+void wm_window_IME_end(wmWindow *win)
+{
+ BLI_assert(win && win->ime_data);
+
+ GHOST_EndIME(win->ghostwin);
+ win->ime_data = NULL;
+}
+#endif /* WITH_INPUT_IME */
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index fe6d3431183..2301405a8ea 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -86,6 +86,13 @@ enum {
* paint and drawing tools however will want to handle these. */
INBETWEEN_MOUSEMOVE = 0x0011,
+/* IME event, GHOST_kEventImeCompositionStart in ghost */
+ WM_IME_COMPOSITE_START = 0x0014,
+/* IME event, GHOST_kEventImeComposition in ghost */
+ WM_IME_COMPOSITE_EVENT = 0x0015,
+/* IME event, GHOST_kEventImeCompositionEnd in ghost */
+ WM_IME_COMPOSITE_END = 0x0016,
+
/* *** Start of keyboard codes. *** */
/* standard keyboard.
diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h
index 9c9c79d2f54..833234b0f13 100644
--- a/source/blender/windowmanager/wm_window.h
+++ b/source/blender/windowmanager/wm_window.h
@@ -69,6 +69,11 @@ wmWindow *wm_window_copy (bContext *C, wmWindow *winorig);
void wm_window_testbreak (void);
+#ifdef WITH_INPUT_IME
+void wm_window_IME_begin (wmWindow *win, int x, int y, int w, int h, bool complete);
+void wm_window_IME_end (wmWindow *win);
+#endif
+
/* *************** window operators ************** */
int wm_window_duplicate_exec(bContext *C, struct wmOperator *op);
int wm_window_fullscreen_toggle_exec(bContext *C, struct wmOperator *op);