1
1

Compare commits

...

78 Commits

Author SHA1 Message Date
Severin
1254e98a48 Merge branch 'master' into ime 2014-12-04 20:48:11 +01:00
Severin
151f2467a4 IME: Add Missing #ifdef WITH_INPUT_IMEs 2014-12-04 20:32:10 +01:00
Severin
6919ea1726 IME: Fix Crash on Escape Cancel 2014-12-04 20:12:49 +01:00
Severin
b64b18f8ac IME: Refactor Underline Drawing Code
* ED_text_draw_underline is now UI_text_draw_underline and is used for all
IDE appearances
* usual minor cleanup
2014-12-04 19:12:05 +01:00
Severin
36fc98cb0b IME: Various fixes + usual cleanup 2014-12-02 01:24:16 +01:00
Severin
36652982dd Merge branch 'master' into ime 2014-12-01 17:35:11 +01:00
Severin
a54d863a93 IME: Fix IME not ending composition after region change
+ adding some asserts
2014-12-01 17:20:13 +01:00
Severin
cce89d04ee Merge branch 'master' into ime 2014-12-01 15:25:05 +01:00
Severin
d399927749 IME: Deduplicate wmImeData + minor cleanup
* deduplicate use of wmImeData
* add missing "ifdef WITH_INPUT_IME"s
* usual minor cleanup
2014-12-01 15:12:24 +01:00
Severin
80496abbf4 Minor cleanup
Adressing some of the inline comments from @sergey and @campbellbarton
2014-11-30 20:06:29 +01:00
Severin
b2a5b96ff4 Merge branch 'master' into ime 2014-11-30 16:28:02 +01:00
Severin
4fc691f619 Merge branch 'master' into ime 2014-11-26 00:47:09 +01:00
Severin
316bae9fe8 General Review Changes
Main changes:
* avoid using MEM_mallocN on every redraw
* continue implementation of WITH_INPUT_IME CMake flag
* whitespace and code style cleanups
2014-11-26 00:34:24 +01:00
Severin
3e5962656a Merge remote-tracking branch 'origin/master' into ime
Conflicts:
	source/blender/editors/space_node/node_add.c
	source/blender/editors/space_text/text_draw.c
	source/blender/editors/space_view3d/view3d_edit.c
2014-11-24 22:22:48 +01:00
Severin
1087b9236f Merge branch master into input_method_editor 2014-11-24 00:41:32 +01:00
Severin
a677da8638 Finish support for CMake flag WITH_INPUT_IME
Note: implementation not finished, needs a bit cleanup
2014-11-24 00:41:32 +01:00
Severin
b5cf3cd415 Cleanup: Moar whitespace cleanup, plus remove missed printf
Hope that was the last cleanup commit for now :/
2014-11-24 00:41:31 +01:00
Severin
f65879fd51 Add missing files 2014-11-24 00:41:31 +01:00
Severin
ff27b12904 Cleanup: Whitespace, Comments, etc.
It may be a bit early to do such cleanups, there are more important things
to do first, but those things are extremely time consuming and I wanted to
get rid of them first ;)
2014-11-24 00:41:30 +01:00
Severin
30654e186f set up ime branch 2014-11-24 00:41:30 +01:00
Severin
60a035c52a set up ime branch 2014-11-24 00:41:29 +01:00
fde03ec578 Minor edits to T42649 fix
- only modify paths for newly loaded images
- don't attempt to read from library paths
2014-11-24 00:34:00 +01:00
0f159b7306 Fix BLI_Bitmap - was not usable in BKE area (strict compile flags). 2014-11-24 00:34:00 +01:00
98beae0c11 Fix freestyle compile.
Do not know why this shows up now, probably a recent tweak in BLI_utildefines.h or so...
2014-11-24 00:33:59 +01:00
julianeisel
b68166355a Fix T42649: Use Relative Paths for Node Editor & 3D View Images
Images are now added with relative paths to the Node Editor and the 3D View.
2014-11-24 00:33:59 +01:00
2bf7215130 Refactor: BLI_path_util (part 2)
Use BKE_appdir/tempdir naming prefix for functions extracted from BLI_path_util
2014-11-24 00:33:58 +01:00
14502a3557 Refactor: BLI_path_util (split out app directory access)
This module is intended for path manipulation functions
but had utility functions added to access various directories.
2014-11-24 00:33:58 +01:00
e0283f8b6c Cleanup: warnings 2014-11-24 00:33:57 +01:00
fffc35c34a Cycles UI: Several fixes for recent additions...
* Alpha Property was removed (Fix T42690)
* Some tweaks to make the panel look better again.
* Use abreviated form "Multiple Importance" everywhere, for consistency.
2014-11-24 00:33:56 +01:00
8299602f1f Math Lib: add constant: M_SQRT1_3 1/sqrt(3) 2014-11-24 00:33:56 +01:00
3d6b912293 Fix T42688: python crash: bpy.context.scene.update().
`ED_render_scene_update()` should ensure it does have some WM to work on...
2014-11-24 00:33:55 +01:00
4a55637a0d Bugfix T42549: Grease pencil layers are not scene-specific for "full copy scenes" 2014-11-24 00:33:54 +01:00
4135458c3d Fix for previous commit
gpencil_data_duplicate() was being used for gp drawing undo buffers, where using an
exact copy is exactly what we want/need. Instead, the code here now has an additional
arg for determining whether a direct copy is warranted or not.
2014-11-24 00:33:54 +01:00
c3eab0e2f4 Bugfix: Duplicating Grease Pencil datablocks wasn't doing so in a safe way
Grease Pencil data (bGPdata) is now a datablock, so it isn't safe to use
MEM_dupallocN() for copying new instances of these anymore.
2014-11-24 00:33:53 +01:00
9901eed588 Fix leftover f postfix from C code. 2014-11-24 00:33:53 +01:00
48b3a09c41 Text Editor: incorrect clipping at bottom
Patch T42637 by @donfabio
2014-11-24 00:33:52 +01:00
ea671a6e87 fix T42676 replaced windows specific include by BLI_utildefines.h 2014-11-24 00:30:39 +01:00
a36778ab8e BMesh: check for loop side-of-loop & side-of-edge 2014-11-24 00:30:38 +01:00
b4337e870e Cleanup: typo 2014-11-24 00:30:37 +01:00
ed01da08fd Cycles: support for specular color in solid shading mode, available in
the material panel.

Patch by Phillipp Oeser (D62) with some minor modifications, thanks!
2014-11-24 00:30:37 +01:00
e2855b81a4 Bugfix T42048: Keyframes missing when animating particle system blend texture parameters
Textures attached to particle systems are now get their animation data listed
under the particle systems they are attached to now. This is the most convenient
and direct way that these can get included
2014-11-24 00:30:36 +01:00
6650436d4a CMake: disable OSL if its not found 2014-11-24 00:30:36 +01:00
e2e69f6011 Bugfix T41527: Animations of world texture properties invisible in anim editors
Was caused by a typo - "items" was used in place of "tmp_items", causing animation
in the texture to get ignored if nothing else was present
2014-11-24 00:30:35 +01:00
6147ad3596 Bugfix T42648: Invert Selection operator is not working for animation channels
The wrong selection mode was being used/passed to operators.
2014-11-24 00:30:35 +01:00
b0ea6aecb3 Fix T42421: HDR reader could easily read past buffer (truncated HDR files e.g.) and segfault.
Now readers get an 'mem_eof' guard pointer, and they abort in case they try to go past it.
2014-11-24 00:30:34 +01:00
a7536dfac5 Task scheduler: Add an option to limit number of threads per pool
This way we can have scheduler capable of scheduling tasks on all the CPUs
but in the same time we can limit tasks like baking (in the future) to use
no more than given number of threads.
2014-11-24 00:30:34 +01:00
d472651713 Compsitor: White space cleanup 2014-11-24 00:30:33 +01:00
808c6e383b Cleanup: ints/shorts -> bool. 2014-11-24 00:30:33 +01:00
70dee80a8b Fix T42622, environment texture GLSL result different from rendering.
Also included mirror ball shader, which was missing.
2014-11-24 00:30:32 +01:00
77447f0401 Fix T42639, editcurve flags not getting restored on undo.
Error here could be reproduced by tweaking curve properties such as
2d-3d or fill type and undoing.
2014-11-24 00:30:32 +01:00
157e6ef22d Fix T42662 hide unselected does not reveal selected.
Not sure if this is a bugfix exactly but should help the gooseberry team
with their workflow.
2014-11-24 00:30:31 +01:00
78b91eb23f Fix T42660 snapping not working nicely on graph editor.
Basically, get the grid increments and reuse them when snapping. System
is slightly crappy here, we should calculate those factors only once,
but leaving as todo for later.
2014-11-24 00:30:31 +01:00
c6b2422db0 Fix color tweaking in vertex painting not getting an undo push (would
cause color reset between strokes)
2014-11-24 00:30:30 +01:00
d6931a6ce6 Fix T42647, vertex and weight painting mode do not display solid shaded
when VBOs is off.
2014-11-24 00:30:29 +01:00
d5e0613d6e SCons: Proper solution for local symbols map
Configuration used to override the link flags, it better restore them
once the configuration is done.
2014-11-24 00:30:29 +01:00
6a6336ce77 Fix T42638: Roll angle inconsistent flip in edit mode.
Basically, `angle_compat_rad()` was completely broken -
example of result it could produce:

| new angle | compat angle |    result
| -0.000000 |   3.141593   | -> 3.141593

... Where 0.0 (or 2 * PI) would be expected!
2014-11-24 00:30:28 +01:00
e0b960a77e Cycles: Remove dynamic library helper files
They were only needed for CUDA wrangler. Since we've switched to CUEW
this utility functions are no longer needed.
2014-11-24 00:30:28 +01:00
7bd15aa347 Cycles: Fix typo on graphiz graph dumper 2014-11-24 00:30:27 +01:00
bbe68ddf7e Bugfix T42661: shortcut "." and "," for changing pivot center don't work on Graph Editor 2014-11-24 00:30:27 +01:00
339147666a SCons: Remove duplicate PLATFORM_LINKFLAGS from makesdna and makesrna
Hopefully it'll fix "anonymous version tag cannot be combined with other version"
compilation error.
2014-11-24 00:30:26 +01:00
716642379f mathutils.kdtree: fix docstrings 2014-11-24 00:30:26 +01:00
b6192bfa3b Cleanup: Remove SD_BSDF_GLOSSY flag, unused. 2014-11-24 00:30:25 +01:00
15a212ee06 Cleanup: #define -> enums. 2014-11-24 00:30:25 +01:00
d813f70382 Switch to SDL2 on OSX 2014-11-24 00:30:24 +01:00
3e5de9dc3f Fix: Shift-H now works in main graph editor area too
In the process, I've removed the old operator (ANIM_OT_channels_visibility_set)
and folded that option in with the hide operator, to make this consistent
with how this is done in the 3D view and other parts of Blender.
2014-11-24 00:30:23 +01:00
656b868db9 Tweaks to hide/reveal hotkeys for Graph Editor
Now the hotkeys here work in line with what's done for other parts of Blender
* H = Hide selected
* Shift-H = Hide unselected  (i.e. old VKEY behaviour)
* Alt-H = Reveal all
2014-11-24 00:30:23 +01:00
aee3d19082 Cleanup: name hide/reveal, like rest of operators 2014-11-24 00:30:22 +01:00
56d4aed17d Graph Editor: H/Shift-H now hide and unhide selected curves (Gooseberry Request)
Revised the tools for managing which FCurves are visible in the Graph Editor
curves area. Now, there are the following tools in place:
* V (channels region only) = Hide all curves except those in selected channels  [OLD]

* H       = Hide all selected curves  [NEW]
* Shift-H = Show all previously hidden curves [NEW]

I've removed the old operator to toggle visibility status of selected curves,
as it doesn't seem that useful anymore.
2014-11-24 00:30:22 +01:00
c6acfdf2a0 Refactor: Move part of vgroup handling code from ED_mesh/object_vgroup.c to BKE_object_deform.
Along with some minor cleanup and simplifications.

Reviewers: campbellbarton

Subscribers: sergey

Differential Revision: https://developer.blender.org/D903
2014-11-24 00:30:21 +01:00
Severin
baccf32076 Merge branch 'master' of git.blender.org:blender into input_method_editor 2014-11-18 23:12:09 +01:00
Severin
e42f974ae6 Finish support for CMake flag WITH_INPUT_IME
Note: implementation not finished, needs a bit cleanup
2014-11-18 20:05:05 +01:00
Severin
122ad8ac6d Cleanup: Moar whitespace cleanup, plus remove missed printf
Hope that was the last cleanup commit for now :/
2014-11-18 15:57:03 +01:00
Severin
f086c5a1ea CMake: add WITH_INPUT_IME option + various fixes/cleanup
*note:* implementation of WITH_INPUT_IME is only halfway finished, you'll
get build errors if you disable it
2014-11-18 01:44:12 +01:00
Severin
c4b7440bc0 Cleanup: Whitespace before comments
Missed that in rB23c7716bbbdef99
2014-11-18 00:02:47 +01:00
Severin
7d281401fd Add missing files 2014-11-17 23:57:27 +01:00
Severin
23c7716bbb Cleanup: Whitespace, Comments, etc.
It may be a bit early to do such cleanups, there are more important things
to do first, but those things are extremely time consuming and I wanted to
get rid of them first ;)
2014-11-17 23:28:59 +01:00
Severin
98f0333110 Cleanup: Use *win instead of *window (moar consistent) 2014-11-17 18:30:01 +01:00
Severin
7907014ed7 set up ime branch 2014-11-17 18:18:11 +01:00
53 changed files with 2370 additions and 80 deletions

View File

@@ -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)" 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)
@@ -1149,7 +1152,7 @@ elseif(WIN32)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO")
list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 advapi32 shfolder shell32 ole32 oleaut32 uuid psapi)
list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 advapi32 shfolder shell32 ole32 oleaut32 uuid psapi imm32)
add_definitions(
-D_CRT_NONSTDC_NO_DEPRECATE
@@ -1505,7 +1508,7 @@ elseif(WIN32)
endif()
endif()
list(APPEND PLATFORM_LINKLIBS -lshell32 -lshfolder -lgdi32 -lmsvcrt -lwinmm -lmingw32 -lm -lws2_32 -lz -lstdc++ -lole32 -luuid -lwsock32 -lpsapi)
list(APPEND PLATFORM_LINKLIBS -lshell32 -lshfolder -lgdi32 -lmsvcrt -lwinmm -lmingw32 -lm -lws2_32 -lz -lstdc++ -lole32 -luuid -lwsock32 -lpsapi -limm32)
set(PLATFORM_CFLAGS "-pipe -funsigned-char -fno-strict-aliasing")
if(WITH_MINGW64)

View File

@@ -196,7 +196,7 @@ C_WARN = ['-Wno-char-subscripts', '-Wdeclaration-after-statement', '-Wstrict-pro
CC_WARN = [ '-Wall' ]
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi']
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi', '-limm32']
PLATFORM_LINKFLAGS = ['-Xlinker', '--stack=2097152']

View File

@@ -235,7 +235,7 @@ C_WARN = []
CC_WARN = []
CXX_WARN = []
LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi']
LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi', 'imm32']
PLATFORM_LINKFLAGS = ['/SUBSYSTEM:CONSOLE','/MACHINE:IX86','/STACK:2097152','/INCREMENTAL:NO', '/LARGEADDRESSAWARE', '/NODEFAULTLIB:msvcrt.lib', '/NODEFAULTLIB:msvcmrt.lib', '/NODEFAULTLIB:msvcurt.lib', '/NODEFAULTLIB:msvcrtd.lib']

View File

@@ -190,7 +190,7 @@ C_WARN = ['-Wno-char-subscripts', '-Wdeclaration-after-statement', '-Wstrict-pro
CC_WARN = [ '-Wall' ]
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi', '-lpthread']
LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi', '-lpthread', '-limm32']
PLATFORM_LINKFLAGS = ['-Xlinker', '--stack=2097152']

View File

@@ -242,7 +242,7 @@ C_WARN = []
CC_WARN = []
CXX_WARN = []
LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi']
LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi', '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']

View File

@@ -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

View File

@@ -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
}

View File

@@ -331,6 +331,30 @@ 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")

View File

@@ -0,0 +1,401 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved.
* All rights reserved.
*
* The Original Code is: some of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file ghost/intern/GHOST_ImeWin32.h
* \ingroup GHOST
*/
#ifndef __GHOST_IME_H__
#define __GHOST_IME_H__
#include <windows.h>
#include <string>
#include "GHOST_Event.h"
#include "GHOST_Rect.h"
#include <vector>
class GHOST_EventIME : public GHOST_Event
{
public:
/**
* Constructor.
* \param msec The time this event was generated.
* \param type The type of key event.
* \param key The key code of the key.
*/
GHOST_EventIME(GHOST_TUns64 msec,
GHOST_TEventType type,
GHOST_IWindow *window, void *customdata)
: GHOST_Event(msec, type, window)
{
this->m_data = customdata;
}
};
/**
* This header file defines a struct and a class used for encapsulating IMM32
* APIs, controls IMEs attached to a window, and enables the 'on-the-spot'
* input without deep knowledge about the APIs, i.e. knowledge about the
* language-specific and IME-specific behaviors.
* The following items enumerates the simplest steps for an (window)
* application to control its IMEs with the struct and the class defined
* this file.
* 1. Add an instance of the GHOST_ImeWin32 class to its window class.
* (The GHOST_ImeWin32 class needs a window handle.)
* 2. Add messages handlers listed in the following subsections, follow the
* instructions written in each subsection, and use the GHOST_ImeWin32 class.
* 2.1. WM_IME_SETCONTEXT (0x0281)
* Call the functions listed below:
* - GHOST_ImeWin32::CreateImeWindow();
* - GHOST_ImeWin32::CleanupComposition(), and;
* - GHOST_ImeWin32::SetImeWindowStyle().
* An application MUST prevent from calling ::DefWindowProc().
* 2.2. WM_IME_STARTCOMPOSITION (0x010D)
* Call the functions listed below:
* - GHOST_ImeWin32::CreateImeWindow(), and;
* - GHOST_ImeWin32::ResetComposition().
* An application MUST prevent from calling ::DefWindowProc().
* 2.3. WM_IME_COMPOSITION (0x010F)
* Call the functions listed below:
* - GHOST_ImeWin32::UpdateImeWindow();
* - GHOST_ImeWin32::GetResult();
* - GHOST_ImeWin32::GetComposition(), and;
* - GHOST_ImeWin32::ResetComposition() (optional).
* An application MUST prevent from calling ::DefWindowProc().
* 2.4. WM_IME_ENDCOMPOSITION (0x010E)
* Call the functions listed below:
* - GHOST_ImeWin32::ResetComposition(), and;
* - GHOST_ImeWin32::DestroyImeWindow().
* An application CAN call ::DefWindowProc().
* 2.5. WM_INPUTLANGCHANGE (0x0051)
* Call the functions listed below:
* - GHOST_ImeWin32::SetInputLanguage().
* An application CAN call ::DefWindowProc().
*/
/* This struct represents the status of an ongoing composition. */
struct ImeComposition {
/* Represents the 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;
/**
* Represents the type of the string in the 'ime_string' parameter.
* Its possible values and description are listed bwlow:
* Value Description
* 0 The parameter is not used.
* GCS_RESULTSTR The parameter represents a result string.
* GCS_COMPSTR The parameter represents a composition string.
*/
int string_type;
/* Represents the string retrieved from IME (Input Method Editor) */
std::wstring ime_string;
std::vector<char> utf8_buf;
std::vector<unsigned char> format;
};
/**
* This class controls the IMM (Input Method Manager) through IMM32 APIs and
* enables it to retrieve the string being controled by the IMM. (I wrote
* a note to describe the reason why I do not use 'IME' but 'IMM' below.)
* NOTE(hbono):
* Fortunately or unfortunately, TSF (Text Service Framework) and
* CUAS (Cicero Unaware Application Support) allows IMM32 APIs for
* retrieving not only the inputs from IMEs (Input Method Editors), used
* only for inputting East-Asian language texts, but also the ones from
* tablets (on Windows XP Tablet PC Edition and Windows Vista), voice
* recognizers (e.g. ViaVoice and Microsoft Office), etc.
* We can disable TSF and CUAS in Windows XP Tablet PC Edition. On the other
* hand, we can NEVER disable either TSF or CUAS in Windows Vista, i.e.
* THIS CLASS IS NOT ONLY USED ON THE INPUT CONTEXTS OF EAST-ASIAN
* LANGUAGES BUT ALSO USED ON THE INPUT CONTEXTS OF ALL LANGUAGES.
*/
class GHOST_ImeWin32 {
public:
GHOST_ImeWin32();
~GHOST_ImeWin32();
/* Retrieves whether or not there is an ongoing composition. */
bool is_composing() const { return is_composing_; }
/**
* Retrieves the input language from Windows and update it.
* Return values
* * true
* The given input language has IMEs.
* * false
* The given input language does not have IMEs.
*/
bool SetInputLanguage();
/**
* Create the IME windows, and allocate required resources for them.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void CreateImeWindow(HWND window_handle);
/**
* Update the style of the IME windows.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
* * message [in] (UINT)
* * wparam [in] (WPARAM)
* * lparam [in] (LPARAM)
* Represent the windows message of the caller.
* These parameters are used for verifying if this function is called
* in a handler function for WM_IME_SETCONTEXT messages because this
* function uses ::DefWindowProc() to update the style.
* A caller just has to pass the input parameters for the handler
* function without modifications.
* * handled [out] (BOOL*)
* Returns ::DefWindowProc() is really called in this function.
* PLEASE DO NOT CALL ::DefWindowProc() IF THIS VALUE IS TRUE!
* All the window styles set in this function are over-written when
* calling ::DefWindowProc() after returning this function.
*/
void SetImeWindowStyle(HWND window_handle, UINT message,
WPARAM wparam, LPARAM lparam, BOOL* handled);
/**
* Destroy the IME windows and all the resources attached to them.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void DestroyImeWindow(HWND window_handle);
/**
* Update the position of the IME windows.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void UpdateImeWindow(HWND window_handle);
/**
* Clean up the all resources attached to the given GHOST_ImeWin32 object, and
* reset its composition status.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void CleanupComposition(HWND window_handle);
/**
* Reset the composition status.
* Cancel the ongoing composition if it exists.
* NOTE(hbono): This method does not release the allocated resources.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void ResetComposition(HWND window_handle);
/**
* Retrieve a composition result of the ongoing composition if it exists.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
* * lparam [in] (LPARAM)
* Specifies the updated members of the ongoing composition, and must be
* the same parameter of a WM_IME_COMPOSITION message handler.
* This parameter is used for checking if the ongoing composition has
* its result string,
* * composition [out] (ImeComposition)
* Represents the struct contains the composition result.
* Return values
* * true
* The ongoing composition has a composition result.
* * false
* The ongoing composition does not have composition results.
* Remarks
* This function is designed for being called from WM_IME_COMPOSITION
* message handlers.
*/
bool GetResult(HWND window_handle, LPARAM lparam,
ImeComposition* composition);
/**
* Retrieve the current composition status of the ongoing composition.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
* * lparam [in] (LPARAM)
* Specifies the updated members of the ongoing composition, and must be
* the same parameter of a WM_IME_COMPOSITION message handler.
* This parameter is used for checking if the ongoing composition has
* its result string,
* * composition [out] (ImeComposition)
* Represents the struct contains the composition status.
* Return values
* * true
* The status of the ongoing composition is updated.
* * false
* The status of the ongoing composition is not updated.
* Remarks
* This function is designed for being called from WM_IME_COMPOSITION
* message handlers.
*/
bool GetComposition(HWND window_handle, LPARAM lparam,
ImeComposition* composition);
/**
* Enable the IME attached to the given window, i.e. allows user-input
* events to be dispatched to the IME.
* In Chrome, this function is used when:
* * a renderer process moves its input focus to another edit control, or;
* * a renrerer process moves the position of the focused edit control.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
* * caret_rect [in] (const gfx::Rect&)
* Represent the rectangle of the input caret.
* This rectangle is used for controlling the positions of IME windows.
* * complete [in] (bool)
* Represents whether or not to complete the ongoing composition.
* + true
* After finishing the ongoing composition and close its IME windows,
* start another composition and display its IME windows to the given
* position.
* + false
* Just move the IME windows of the ongoing composition to the given
* position without finishing it.
*/
void EnableIME(HWND window_handle,
const GHOST_Rect& caret_rect,
bool complete);
/**
* Disable the IME attached to the given window, i.e. prohibits any user-input
* events from being dispatched to the IME.
* In Chrome, this function is used when:
* * a renreder process sets its input focus to a password input.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void DisableIME(HWND window_handle);
/* Updatg resultInfo and compInfo */
void UpdateInfo(HWND window_handle);
/* disable ime when start up */
void CheckFirst(HWND window_handle);
ImeComposition resultInfo, compInfo;
GHOST_TEventImeData eventImeData;
protected:
/* Determines whether or not the given attribute represents a target (a.k.a. a selection). */
bool IsTargetAttribute(char attribute) const {
return (attribute == ATTR_TARGET_CONVERTED ||
attribute == ATTR_TARGET_NOTCONVERTED);
}
/* Retrieve the target area. */
void GetCaret(HIMC imm_context, LPARAM lparam,
ImeComposition* composition);
/* Update the position of the IME windows. */
void MoveImeWindow(HWND window_handle, HIMC imm_context);
/* Complete the ongoing composition if it exists. */
void CompleteComposition(HWND window_handle, HIMC imm_context);
/* Retrieve a string from the IMM. */
bool GetString(HIMC imm_context, WPARAM lparam, int type,
ImeComposition* composition);
private:
/**
* Represents whether or not there is an ongoing composition in a browser
* process, i.e. whether or not a browser process is composing a text.
*/
bool is_composing_;
/**
* This value represents whether or not the current input context has IMEs.
* The following table shows the list of IME status:
* Value Description
* false The current input language does not have IMEs.
* true The current input language has IMEs.
*/
bool ime_status_;
/**
* The current input Language ID retrieved from Windows, which consists of:
* * Primary Language ID (bit 0 to bit 9), which shows a natunal language
* (English, Korean, Chinese, Japanese, etc.) and;
* * Sub-Language ID (bit 10 to bit 15), which shows a geometrical region
* the language is spoken (For English, United States, United Kingdom,
* Australia, Canada, etc.)
* The following list enumerates some examples for the Language ID:
* * "en-US" (0x0409)
* MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
* * "ko-KR" (0x0412)
* MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN);
* * "zh-TW" (0x0404)
* MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL);
* * "zh-CN" (0x0804)
* MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
* * "ja-JP" (0x0411)
* MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), etc.
* (See <winnt.h> for other available values.)
* This Language ID is used for processing language-specific operations in
* IME functions.
*/
LANGID input_language_id_;
/**
* Represents whether or not the current input context has created a system
* caret to set the position of its IME candidate window.
* * true: it creates a system caret.
* * false: it does not create a system caret.
*/
bool system_caret_;
/* The rectangle of the input caret retrieved from a renderer process. */
GHOST_Rect caret_rect_;
/* used for disable ime when start up */
bool is_first, is_enable;
};
#endif * __GHOST_IME_H__

View File

@@ -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;

View File

@@ -915,3 +915,23 @@ 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 */

View File

@@ -0,0 +1,517 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved.
* All rights reserved.
*
* The Original Code is: some of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file ghost/intern/GHOST_ImeWin32.cpp
* \ingroup GHOST
*/
#include "GHOST_C-api.h"
#include "GHOST_ImeWin32.h"
#include "GHOST_WindowWin32.h"
#include "utfconv.h"
GHOST_ImeWin32::GHOST_ImeWin32()
: ime_status_(false),
input_language_id_(LANG_USER_DEFAULT),
is_composing_(false),
system_caret_(false),
caret_rect_(-1, -1, 0, 0),
is_first(true),
is_enable(true)
{
}
GHOST_ImeWin32::~GHOST_ImeWin32()
{
}
bool GHOST_ImeWin32::SetInputLanguage()
{
/**
* Retrieve the current keyboard layout from Windows and determine whether
* or not the current input context has IMEs.
* Also save its input language for language-specific operations required
* while composing a text.
*/
HKL keyboard_layout = ::GetKeyboardLayout(0);
input_language_id_ = reinterpret_cast<LANGID>(keyboard_layout);
ime_status_ = ::ImmIsIME(keyboard_layout);
return ime_status_;
}
void GHOST_ImeWin32::CreateImeWindow(HWND window_handle)
{
/**
* When a user disables TSF (Text Service Framework) and CUAS (Cicero
* Unaware Application Support), Chinese IMEs somehow ignore function calls
* to ::ImmSetCandidateWindow(), i.e. they do not move their candidate
* window to the position given as its parameters, and use the position
* of the current system caret instead, i.e. it uses ::GetCaretPos() to
* retrieve the position of their IME candidate window.
* Therefore, we create a temporary system caret for Chinese IMEs and use
* it during this input context.
* Since some third-party Japanese IME also uses ::GetCaretPos() to determine
* their window position, we also create a caret for Japanese IMEs.
*/
if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE ||
PRIMARYLANGID(input_language_id_) == LANG_JAPANESE) {
if (!system_caret_) {
if (::CreateCaret(window_handle, NULL, 1, 1)) {
system_caret_ = true;
}
}
}
/* Restore the positions of the IME windows. */
UpdateImeWindow(window_handle);
}
void GHOST_ImeWin32::SetImeWindowStyle(HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam, BOOL *handled)
{
/**
* To prevent the IMM (Input Method Manager) from displaying the IME
* composition window, Update the styles of the IME windows and EXPLICITLY
* call ::DefWindowProc() here.
* NOTE(hbono): We can NEVER let WTL call ::DefWindowProc() when we update
* the styles of IME windows because the 'lparam' variable is a local one
* and all its updates disappear in returning from this function, i.e. WTL
* does not call ::DefWindowProc() with our updated 'lparam' value but call
* the function with its original value and over-writes our window styles.
*/
*handled = TRUE;
lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
::DefWindowProc(window_handle, message, wparam, lparam);
}
void GHOST_ImeWin32::DestroyImeWindow(HWND window_handle)
{
/* Destroy the system caret if we have created for this IME input context. */
if (system_caret_) {
::DestroyCaret();
system_caret_ = false;
}
}
void GHOST_ImeWin32::MoveImeWindow(HWND window_handle, HIMC imm_context)
{
int x = caret_rect_.m_l;
int y = caret_rect_.m_t;
const int kCaretMargin = 1;
/**
* As written in a comment in GHOST_ImeWin32::CreateImeWindow(),
* Chinese IMEs ignore function calls to ::ImmSetCandidateWindow()
* when a user disables TSF (Text Service Framework) and CUAS (Cicero
* Unaware Application Support).
* On the other hand, when a user enables TSF and CUAS, Chinese IMEs
* ignore the position of the current system caret and uses the
* parameters given to ::ImmSetCandidateWindow() with its 'dwStyle'
* parameter CFS_CANDIDATEPOS.
* Therefore, we do not only call ::ImmSetCandidateWindow() but also
* set the positions of the temporary system caret if it exists.
*/
CANDIDATEFORM candidate_position = { 0, CFS_CANDIDATEPOS, { x, y },
{ 0, 0, 0, 0 } };
::ImmSetCandidateWindow(imm_context, &candidate_position);
if (system_caret_) {
switch (PRIMARYLANGID(input_language_id_)) {
case LANG_JAPANESE:
::SetCaretPos(x, y + caret_rect_.getHeight());
break;
default:
::SetCaretPos(x, y);
break;
}
}
if (PRIMARYLANGID(input_language_id_) == LANG_KOREAN) {
/**
* Chinese IMEs and Japanese IMEs require the upper-left corner of
* the caret to move the position of their candidate windows.
* On the other hand, Korean IMEs require the lower-left corner of the
* caret to move their candidate windows.
*/
y += kCaretMargin;
}
/**
* Japanese IMEs and Korean IMEs also use the rectangle given to
* ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE
* to move their candidate windows when a user disables TSF and CUAS.
* Therefore, we also set this parameter here.
*/
CANDIDATEFORM exclude_rectangle = { 0, CFS_EXCLUDE, { x, y },
{ x, y, x + caret_rect_.getWidth(), y + caret_rect_.getHeight() } };
::ImmSetCandidateWindow(imm_context, &exclude_rectangle);
}
void GHOST_ImeWin32::UpdateImeWindow(HWND window_handle)
{
/* Just move the IME window attached to the given window. */
if (caret_rect_.m_l >= 0 && caret_rect_.m_t >= 0) {
HIMC imm_context = ::ImmGetContext(window_handle);
if (imm_context) {
MoveImeWindow(window_handle, imm_context);
::ImmReleaseContext(window_handle, imm_context);
}
}
}
void GHOST_ImeWin32::CleanupComposition(HWND window_handle)
{
/**
* Notify the IMM attached to the given window to complete the ongoing
* composition, (this case happens when the given window is de-activated
* while composing a text and re-activated), and reset the omposition status.
*/
if (is_composing_) {
HIMC imm_context = ::ImmGetContext(window_handle);
if (imm_context) {
::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
::ImmReleaseContext(window_handle, imm_context);
}
ResetComposition(window_handle);
}
}
void GHOST_ImeWin32::CheckFirst(HWND window_handle)
{
if (is_first) {
this->EndIME(window_handle);
is_first = false;
}
}
void GHOST_ImeWin32::ResetComposition(HWND window_handle)
{
/* Currently, just reset the composition status. */
is_composing_ = false;
}
void GHOST_ImeWin32::CompleteComposition(HWND window_handle, HIMC imm_context)
{
/**
* We have to confirm there is an ongoing composition before completing it.
* This is for preventing some IMEs from getting confused while completing an
* ongoing composition even if they do not have any ongoing compositions.)
*/
if (is_composing_) {
::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
ResetComposition(window_handle);
}
}
void GHOST_ImeWin32::GetCaret(HIMC imm_context, LPARAM lparam, ImeComposition *composition)
{
/**
* This operation is optional and language-dependent because the caret
* style is depended on the language, e.g.:
* * Korean IMEs: the caret is a blinking block,
* (It contains only one hangul character);
* * Chinese IMEs: the caret is a blinking line,
* (i.e. they do not need to retrieve the target selection);
* * Japanese IMEs: the caret is a selection (or underlined) block,
* (which can contain one or more Japanese characters).
*/
int target_start = -1;
int target_end = -1;
switch (PRIMARYLANGID(input_language_id_)) {
case LANG_KOREAN:
if (lparam & CS_NOMOVECARET) {
target_start = 0;
target_end = 1;
}
break;
case LANG_CHINESE:
{
int clause_size = ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, NULL, 0);
if (clause_size) {
static std::vector<unsigned long> clauses;
clause_size = clause_size / sizeof(clauses[0]);
clauses.resize(clause_size);
ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, &clauses[0],
sizeof(clauses[0]) *clause_size);
if (composition->cursor_position == composition->ime_string.size()) {
target_start = clauses[clause_size - 2];
target_end = clauses[clause_size - 1];
}
else {
for (int i = 0; i < clause_size - 1; i++) {
if (clauses[i] == composition->cursor_position) {
target_start = clauses[i];
target_end = clauses[i + 1];
break;
}
}
}
}
else {
if (composition->cursor_position != -1) {
target_start = composition->cursor_position;
target_end = composition->ime_string.size();
}
}
break;
}
case LANG_JAPANESE:
/**
* For Japanese IMEs, the robustest way to retrieve the caret
* is scanning the attribute of the latest composition string and
* retrieving the begining and the end of the target clause, i.e.
* a clause being converted.
*/
if (lparam & GCS_COMPATTR) {
int attribute_size = ::ImmGetCompositionStringW(imm_context,
GCS_COMPATTR,
NULL, 0);
if (attribute_size > 0) {
char *attribute_data = new char[attribute_size];
if (attribute_data) {
::ImmGetCompositionStringW(imm_context, GCS_COMPATTR,
attribute_data, attribute_size);
for (target_start = 0; target_start < attribute_size;
++target_start) {
if (IsTargetAttribute(attribute_data[target_start]))
break;
}
for (target_end = target_start; target_end < attribute_size;
++target_end) {
if (!IsTargetAttribute(attribute_data[target_end]))
break;
}
if (target_start == attribute_size) {
/**
* This composition clause does not contain any target clauses,
* i.e. this clauses is an input clause.
* We treat whole this clause as a target clause.
*/
target_end = target_start;
target_start = 0;
}
if (target_start != -1 && target_start < attribute_size &&
attribute_data[target_start] == ATTR_TARGET_NOTCONVERTED)
{
composition->cursor_position = target_start;
}
}
delete[] attribute_data;
}
}
break;
}
composition->target_start = target_start;
composition->target_end = target_end;
}
bool GHOST_ImeWin32::GetString(HIMC imm_context, WPARAM lparam, int type, ImeComposition *composition)
{
bool result = false;
if (lparam & type) {
int string_size = ::ImmGetCompositionStringW(imm_context, type, NULL, 0);
if (string_size > 0) {
int string_length = string_size / sizeof(wchar_t);
wchar_t *string_data = new wchar_t[string_length + 1];
string_data[string_length] = '\0';
if (string_data) {
/* Fill the given ImeComposition object. */
::ImmGetCompositionStringW(imm_context, type,
string_data, string_size);
composition->string_type = type;
composition->ime_string = string_data;
result = true;
}
delete[] string_data;
}
}
return result;
}
bool GHOST_ImeWin32::GetResult(HWND window_handle, LPARAM lparam, ImeComposition *composition)
{
bool result = false;
HIMC imm_context = ::ImmGetContext(window_handle);
if (imm_context) {
/* Copy the result string to the ImeComposition object. */
result = GetString(imm_context, lparam, GCS_RESULTSTR, composition);
/**
* Reset all the other parameters because a result string does not
* have composition attributes.
*/
composition->cursor_position = -1;
composition->target_start = -1;
composition->target_end = -1;
::ImmReleaseContext(window_handle, imm_context);
}
return result;
}
bool GHOST_ImeWin32::GetComposition(HWND window_handle, LPARAM lparam, ImeComposition *composition)
{
bool result = false;
HIMC imm_context = ::ImmGetContext(window_handle);
if (imm_context) {
/* Copy the composition string to the ImeComposition object. */
result = GetString(imm_context, lparam, GCS_COMPSTR, composition);
/* Retrieve the cursor position in the IME composition. */
int cursor_position = ::ImmGetCompositionStringW(imm_context, GCS_CURSORPOS, NULL, 0);
composition->cursor_position = cursor_position;
composition->target_start = -1;
composition->target_end = -1;
/* Retrieve the target selection and Update the ImeComposition object. */
GetCaret(imm_context, lparam, composition);
/* Mark that there is an ongoing composition. */
is_composing_ = true;
::ImmReleaseContext(window_handle, imm_context);
}
return result;
}
void GHOST_ImeWin32::EndIME(HWND window_handle)
{
/**
* A renderer process have moved its input focus to a password input
* when there is an ongoing composition, e.g. a user has clicked a
* mouse button and selected a password input while composing a text.
* For this case, we have to complete the ongoing composition and
* clean up the resources attached to this object BEFORE DISABLING THE IME.
*/
if (!is_enable) return;
is_enable = false;
CleanupComposition(window_handle);
::ImmAssociateContextEx(window_handle, NULL, 0);
eventImeData.composite_len = 0;
}
void GHOST_ImeWin32::BeginIME(HWND window_handle, const GHOST_Rect &caret_rect, bool complete)
{
if (is_enable && complete) return;
is_enable = true;
/**
* Load the default IME context.
* NOTE(hbono)
* IMM ignores this call if the IME context is loaded. Therefore, we do
* not have to check whether or not the IME context is loaded.
*/
::ImmAssociateContextEx(window_handle, NULL, IACE_DEFAULT);
/* Complete the ongoing composition and move the IME windows. */
HIMC imm_context = ::ImmGetContext(window_handle);
if (imm_context) {
if (complete) {
/**
* A renderer process have moved its input focus to another edit
* control when there is an ongoing composition, e.g. a user has
* clicked a mouse button and selected another edit control while
* composing a text.
* For this case, we have to complete the ongoing composition and
* hide the IME windows BEFORE MOVING THEM.
*/
CompleteComposition(window_handle, imm_context);
}
/**
* Save the caret position, and Update the position of the IME window.
* This update is used for moving an IME window when a renderer process
* resize/moves the input caret.
*/
if (caret_rect.m_l >= 0 && caret_rect.m_t >= 0) {
caret_rect_ = caret_rect;
MoveImeWindow(window_handle, imm_context);
}
::ImmReleaseContext(window_handle, imm_context);
}
}
static void convert_utf16_to_utf8_len(std::wstring s, int &len)
{
if (len >= 0 && len <= s.size())
len = count_utf_8_from_16(s.substr(0, len).c_str()) - 1;
else
len = -1;
}
static size_t updateUtf8Buf(ImeComposition &info)
{
size_t len = count_utf_8_from_16(info.ime_string.c_str());
info.utf8_buf.resize(len);
conv_utf_16_to_8(info.ime_string.c_str(), &info.utf8_buf[0], len);
convert_utf16_to_utf8_len(info.ime_string, info.cursor_position);
convert_utf16_to_utf8_len(info.ime_string, info.target_start);
convert_utf16_to_utf8_len(info.ime_string, info.target_end);
return len - 1;
}
void GHOST_ImeWin32::UpdateInfo(HWND window_handle)
{
int res = this->GetResult(window_handle, GCS_RESULTSTR, &resultInfo);
int comp = this->GetComposition(window_handle, GCS_COMPSTR | GCS_COMPATTR, &compInfo);
/* convert wchar to utf8 */
if (res) {
eventImeData.result_len = (GHOST_TUserDataPtr)updateUtf8Buf(resultInfo);
eventImeData.result = &resultInfo.utf8_buf[0];
}
else {
eventImeData.result = 0;
eventImeData.result_len = 0;
}
if (comp) {
eventImeData.composite_len = (GHOST_TUserDataPtr)updateUtf8Buf(compInfo);
eventImeData.composite = &compInfo.utf8_buf[0];
eventImeData.cursor_position = compInfo.cursor_position;
eventImeData.target_start = compInfo.target_start;
eventImeData.target_end = compInfo.target_end;
}
else {
eventImeData.composite = 0;
eventImeData.composite_len = 0;
eventImeData.cursor_position = -1;
eventImeData.target_start = -1;
eventImeData.target_end = -1;
}
}

View File

@@ -0,0 +1,401 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (c) 2010 The Chromium Authors. All rights reserved.
* All rights reserved.
*
* The Original Code is: some of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file ghost/intern/GHOST_ImeWin32.h
* \ingroup GHOST
*/
#ifndef __GHOST_IME_H__
#define __GHOST_IME_H__
#include <windows.h>
#include <string>
#include "GHOST_Event.h"
#include "GHOST_Rect.h"
#include <vector>
class GHOST_EventIME : public GHOST_Event
{
public:
/**
* Constructor.
* \param msec The time this event was generated.
* \param type The type of key event.
* \param key The key code of the key.
*/
GHOST_EventIME(GHOST_TUns64 msec,
GHOST_TEventType type,
GHOST_IWindow *window, void *customdata)
: GHOST_Event(msec, type, window)
{
this->m_data = customdata;
}
};
/**
* This header file defines a struct and a class used for encapsulating IMM32
* APIs, controls IMEs attached to a window, and enables the 'on-the-spot'
* input without deep knowledge about the APIs, i.e. knowledge about the
* language-specific and IME-specific behaviors.
* The following items enumerates the simplest steps for an (window)
* application to control its IMEs with the struct and the class defined
* this file.
* 1. Add an instance of the GHOST_ImeWin32 class to its window class.
* (The GHOST_ImeWin32 class needs a window handle.)
* 2. Add messages handlers listed in the following subsections, follow the
* instructions written in each subsection, and use the GHOST_ImeWin32 class.
* 2.1. WM_IME_SETCONTEXT (0x0281)
* Call the functions listed below:
* - GHOST_ImeWin32::CreateImeWindow();
* - GHOST_ImeWin32::CleanupComposition(), and;
* - GHOST_ImeWin32::SetImeWindowStyle().
* An application MUST prevent from calling ::DefWindowProc().
* 2.2. WM_IME_STARTCOMPOSITION (0x010D)
* Call the functions listed below:
* - GHOST_ImeWin32::CreateImeWindow(), and;
* - GHOST_ImeWin32::ResetComposition().
* An application MUST prevent from calling ::DefWindowProc().
* 2.3. WM_IME_COMPOSITION (0x010F)
* Call the functions listed below:
* - GHOST_ImeWin32::UpdateImeWindow();
* - GHOST_ImeWin32::GetResult();
* - GHOST_ImeWin32::GetComposition(), and;
* - GHOST_ImeWin32::ResetComposition() (optional).
* An application MUST prevent from calling ::DefWindowProc().
* 2.4. WM_IME_ENDCOMPOSITION (0x010E)
* Call the functions listed below:
* - GHOST_ImeWin32::ResetComposition(), and;
* - GHOST_ImeWin32::DestroyImeWindow().
* An application CAN call ::DefWindowProc().
* 2.5. WM_INPUTLANGCHANGE (0x0051)
* Call the functions listed below:
* - GHOST_ImeWin32::SetInputLanguage().
* An application CAN call ::DefWindowProc().
*/
/* This struct represents the status of an ongoing composition. */
struct ImeComposition {
/* Represents the 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;
/**
* Represents the type of the string in the 'ime_string' parameter.
* Its possible values and description are listed bwlow:
* Value Description
* 0 The parameter is not used.
* GCS_RESULTSTR The parameter represents a result string.
* GCS_COMPSTR The parameter represents a composition string.
*/
int string_type;
/* Represents the string retrieved from IME (Input Method Editor) */
std::wstring ime_string;
std::vector<char> utf8_buf;
std::vector<unsigned char> format;
};
/**
* This class controls the IMM (Input Method Manager) through IMM32 APIs and
* enables it to retrieve the string being controled by the IMM. (I wrote
* a note to describe the reason why I do not use 'IME' but 'IMM' below.)
* NOTE(hbono):
* Fortunately or unfortunately, TSF (Text Service Framework) and
* CUAS (Cicero Unaware Application Support) allows IMM32 APIs for
* retrieving not only the inputs from IMEs (Input Method Editors), used
* only for inputting East-Asian language texts, but also the ones from
* tablets (on Windows XP Tablet PC Edition and Windows Vista), voice
* recognizers (e.g. ViaVoice and Microsoft Office), etc.
* We can disable TSF and CUAS in Windows XP Tablet PC Edition. On the other
* hand, we can NEVER disable either TSF or CUAS in Windows Vista, i.e.
* THIS CLASS IS NOT ONLY USED ON THE INPUT CONTEXTS OF EAST-ASIAN
* LANGUAGES BUT ALSO USED ON THE INPUT CONTEXTS OF ALL LANGUAGES.
*/
class GHOST_ImeWin32 {
public:
GHOST_ImeWin32();
~GHOST_ImeWin32();
/* Retrieves whether or not there is an ongoing composition. */
bool is_composing() const {return is_composing_;}
/**
* Retrieves the input language from Windows and update it.
* Return values
* * true
* The given input language has IMEs.
* * false
* The given input language does not have IMEs.
*/
bool SetInputLanguage();
/**
* Create the IME windows, and allocate required resources for them.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void CreateImeWindow(HWND window_handle);
/**
* Update the style of the IME windows.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
* * message [in] (UINT)
* * wparam [in] (WPARAM)
* * lparam [in] (LPARAM)
* Represent the windows message of the caller.
* These parameters are used for verifying if this function is called
* in a handler function for WM_IME_SETCONTEXT messages because this
* function uses ::DefWindowProc() to update the style.
* A caller just has to pass the input parameters for the handler
* function without modifications.
* * handled [out] (BOOL*)
* Returns ::DefWindowProc() is really called in this function.
* PLEASE DO NOT CALL ::DefWindowProc() IF THIS VALUE IS TRUE!
* All the window styles set in this function are over-written when
* calling ::DefWindowProc() after returning this function.
*/
void SetImeWindowStyle(HWND window_handle, UINT message,
WPARAM wparam, LPARAM lparam, BOOL* handled);
/**
* Destroy the IME windows and all the resources attached to them.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void DestroyImeWindow(HWND window_handle);
/**
* Update the position of the IME windows.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void UpdateImeWindow(HWND window_handle);
/**
* Clean up the all resources attached to the given GHOST_ImeWin32 object, and
* reset its composition status.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void CleanupComposition(HWND window_handle);
/**
* Reset the composition status.
* Cancel the ongoing composition if it exists.
* NOTE(hbono): This method does not release the allocated resources.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void ResetComposition(HWND window_handle);
/**
* Retrieve a composition result of the ongoing composition if it exists.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
* * lparam [in] (LPARAM)
* Specifies the updated members of the ongoing composition, and must be
* the same parameter of a WM_IME_COMPOSITION message handler.
* This parameter is used for checking if the ongoing composition has
* its result string,
* * composition [out] (ImeComposition)
* Represents the struct contains the composition result.
* Return values
* * true
* The ongoing composition has a composition result.
* * false
* The ongoing composition does not have composition results.
* Remarks
* This function is designed for being called from WM_IME_COMPOSITION
* message handlers.
*/
bool GetResult(HWND window_handle, LPARAM lparam,
ImeComposition* composition);
/**
* Retrieve the current composition status of the ongoing composition.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
* * lparam [in] (LPARAM)
* Specifies the updated members of the ongoing composition, and must be
* the same parameter of a WM_IME_COMPOSITION message handler.
* This parameter is used for checking if the ongoing composition has
* its result string,
* * composition [out] (ImeComposition)
* Represents the struct contains the composition status.
* Return values
* * true
* The status of the ongoing composition is updated.
* * false
* The status of the ongoing composition is not updated.
* Remarks
* This function is designed for being called from WM_IME_COMPOSITION
* message handlers.
*/
bool GetComposition(HWND window_handle, LPARAM lparam,
ImeComposition* composition);
/**
* Enable the IME attached to the given window, i.e. allows user-input
* events to be dispatched to the IME.
* In Chrome, this function is used when:
* * a renderer process moves its input focus to another edit control, or;
* * a renrerer process moves the position of the focused edit control.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
* * caret_rect [in] (const gfx::Rect&)
* Represent the rectangle of the input caret.
* This rectangle is used for controlling the positions of IME windows.
* * complete [in] (bool)
* Represents whether or not to complete the ongoing composition.
* + true
* After finishing the ongoing composition and close its IME windows,
* start another composition and display its IME windows to the given
* position.
* + false
* Just move the IME windows of the ongoing composition to the given
* position without finishing it.
*/
void BeginIME(HWND window_handle,
const GHOST_Rect& caret_rect,
bool complete);
/**
* Disable the IME attached to the given window, i.e. prohibits any user-input
* events from being dispatched to the IME.
* In Chrome, this function is used when:
* * a renreder process sets its input focus to a password input.
* Parameters
* * window_handle [in] (HWND)
* Represents the window handle of the caller.
*/
void EndIME(HWND window_handle);
/* Updatg resultInfo and compInfo */
void UpdateInfo(HWND window_handle);
/* disable ime when start up */
void CheckFirst(HWND window_handle);
ImeComposition resultInfo, compInfo;
GHOST_TEventImeData eventImeData;
protected:
/* Determines whether or not the given attribute represents a target (a.k.a. a selection). */
bool IsTargetAttribute(char attribute) const {
return (attribute == ATTR_TARGET_CONVERTED ||
attribute == ATTR_TARGET_NOTCONVERTED);
}
/* Retrieve the target area. */
void GetCaret(HIMC imm_context, LPARAM lparam,
ImeComposition* composition);
/* Update the position of the IME windows. */
void MoveImeWindow(HWND window_handle, HIMC imm_context);
/* Complete the ongoing composition if it exists. */
void CompleteComposition(HWND window_handle, HIMC imm_context);
/* Retrieve a string from the IMM. */
bool GetString(HIMC imm_context, WPARAM lparam, int type,
ImeComposition* composition);
private:
/**
* Represents whether or not there is an ongoing composition in a browser
* process, i.e. whether or not a browser process is composing a text.
*/
bool is_composing_;
/**
* This value represents whether or not the current input context has IMEs.
* The following table shows the list of IME status:
* Value Description
* false The current input language does not have IMEs.
* true The current input language has IMEs.
*/
bool ime_status_;
/**
* The current input Language ID retrieved from Windows, which consists of:
* * Primary Language ID (bit 0 to bit 9), which shows a natunal language
* (English, Korean, Chinese, Japanese, etc.) and;
* * Sub-Language ID (bit 10 to bit 15), which shows a geometrical region
* the language is spoken (For English, United States, United Kingdom,
* Australia, Canada, etc.)
* The following list enumerates some examples for the Language ID:
* * "en-US" (0x0409)
* MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
* * "ko-KR" (0x0412)
* MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN);
* * "zh-TW" (0x0404)
* MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL);
* * "zh-CN" (0x0804)
* MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);
* * "ja-JP" (0x0411)
* MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), etc.
* (See <winnt.h> for other available values.)
* This Language ID is used for processing language-specific operations in
* IME functions.
*/
LANGID input_language_id_;
/**
* Represents whether or not the current input context has created a system
* caret to set the position of its IME candidate window.
* * true: it creates a system caret.
* * false: it does not create a system caret.
*/
bool system_caret_;
/* The rectangle of the input caret retrieved from a renderer process. */
GHOST_Rect caret_rect_;
/* used for disable ime when start up */
bool is_first, is_enable;
};
#endif * __GHOST_IME_H__

View File

@@ -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
////////////////////////////////////////////////////////////////////////

View File

@@ -306,6 +306,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.

View File

@@ -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.

View File

@@ -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 */

View File

@@ -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,18 @@ 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 +354,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__

View File

@@ -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_text_draw_underline(int pos_x, int pos_y, int len, int height);
/* state for scrolldrawing */
#define UI_SCROLL_PRESSED (1 << 0)
@@ -1009,4 +1010,6 @@ void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p);
int UI_calc_float_precision(int prec, double value);
/* Utility */
void ui_region_to_window(const struct ARegion *ar, int *x, int *y);
#endif /* __UI_INTERFACE_H__ */

View File

@@ -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}")

View File

@@ -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)

View File

@@ -395,6 +395,11 @@ 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_text_draw_underline(int pos_x, int pos_y, int len, int height) {
short 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)

View File

@@ -86,6 +86,7 @@
#include "WM_api.h"
#include "WM_types.h"
#include "wm_window.h"
/* place the mouse at the scaled down location when un-grabbing */
#define USE_CONT_MOUSE_CORRECT
@@ -2425,8 +2426,48 @@ 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, int 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) {
@@ -2483,11 +2524,17 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
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 +2565,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 +2634,10 @@ 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;
wmWindow *win = CTX_wm_window(C);
wmImeData *ime_data = win->ime_data;
bool is_ime_composing = ime_data && ime_data->is_ime_composite;
switch (event->type) {
case MOUSEMOVE:
case MOUSEPAN:
@@ -2603,6 +2658,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 +2721,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 +2837,14 @@ 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 +2879,22 @@ 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->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) {

View File

@@ -627,6 +627,9 @@ 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);
void ui_but_ime_reposition(uiBut *but, int x, int y, int complete);
struct wmImeData *ui_but_get_ime_data(uiBut *but);
/* 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);

View File

@@ -33,6 +33,8 @@
#include <string.h>
#include <assert.h>
#include "MEM_guardedalloc.h"
#include "DNA_brush_types.h"
#include "DNA_screen_types.h"
#include "DNA_userdef_types.h"
@@ -59,6 +61,8 @@
#include "interface_intern.h"
#include "WM_types.h"
/* 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,12 +1236,56 @@ 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, rcti *rect, wmImeData *ime_data, char *drawstr)
{
int ofs_x, width;
int rect_x = BLI_rcti_size_x(rect);
int target_start = ime_data->target_start, target_end = ime_data->target_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_text_draw_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 1);
/* draw the thick line */
if (target_start != -1 && target_end != -1) {
target_end -= target_start;
target_start += but->pos;
if (target_start >= but->ofs) {
ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, target_start - but->ofs);
}
else {
ofs_x = 0;
}
width = BLF_width(fstyle->uifont_id, drawstr + but->ofs,
target_end + target_start - but->ofs);
UI_text_draw_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 2);
}
}
}
#endif
static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect)
{
int drawstr_left_len = UI_MAX_DRAW_STR;
const char *drawstr = but->drawstr;
char *drawstr = but->drawstr;
const char *drawstr_right = NULL;
char *drawstr_edit = NULL;
bool use_right_only = false;
wmImeData *ime_data;
UI_fontstyle_set(fstyle);
@@ -1266,13 +1314,26 @@ 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;
#ifdef WITH_INPUT_IME
ime_data = ui_but_get_ime_data(but);
if (ime_data && ime_data->composite_len) {
/* insert composite string into cursor pos */
BLI_snprintf(drawstr, UI_MAX_DRAW_STR, "%s%s%s",
but->editstr, ime_data->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 vpos;
int tx, ty;
/* text button selection */
if ((but->selend - but->selsta) > 0) {
@@ -1297,19 +1358,48 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b
}
}
/* text cursor */
vpos = but->pos;
#ifdef WITH_INPUT_IME
/* if is ime compositing, move the cursor */
if (ime_data && ime_data->composite_len && ime_data->cursor_position != -1) {
vpos += ime_data->cursor_position;
}
#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, vpos - 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);
}
#else
(void)ime_data;
#endif
}
if (fstyle->kerning == 1)

View File

@@ -52,6 +52,12 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
if(WIN32)
if(WITH_INPUT_IME)
add_definitions(-DWITH_INPUT_IME)
endif()
endif()
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_editor_screen "${SRC}" "${INC}" "${INC_SYS}")

View File

@@ -54,6 +54,7 @@
#include "WM_api.h"
#include "WM_types.h"
#include "wm_window.h"
#include "ED_object.h"
#include "ED_screen.h"
@@ -1434,6 +1435,11 @@ void ED_screen_set_subwinactive(bContext *C, wmEvent *event)
else {
/* notifier invokes freeing the buttons... causing a bit too much redraws */
if (oldswin != scr->subwinactive) {
#ifdef WITH_INPUT_IME
/* when cursor leaves a region, disable IME */
wm_window_IME_end(win);
#endif
region_cursor_set(win, scr->subwinactive, true);
/* this used to be a notifier, but needs to be done immediate

View File

@@ -47,6 +47,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_space_console "${SRC}" "${INC}" "${INC_SYS}")

View File

@@ -47,6 +47,7 @@
#include "console_intern.h"
#include "WM_types.h"
#include "../space_info/textview.h"
@@ -161,7 +162,7 @@ static void console_cursor_wrap_offset(const char *str, int width, int *row, int
return;
}
static int console_textview_line_color(struct TextViewContext *tvc, unsigned char fg[3], unsigned char UNUSED(bg[3]))
static int console_textview_line_color(struct TextViewContext *tvc, wmImeData *ime_data, unsigned char fg[3], unsigned char UNUSED(bg[3]))
{
ConsoleLine *cl_iter = (ConsoleLine *)tvc->iter;
@@ -176,9 +177,28 @@ static int console_textview_line_color(struct TextViewContext *tvc, unsigned cha
console_cursor_wrap_offset(sc->prompt, tvc->console_width, &offl, &offc, NULL);
console_cursor_wrap_offset(cl->line, tvc->console_width, &offl, &offc, cl->line + cl->cursor);
pen[0] = tvc->cwidth * offc;
pen[1] = -2 - tvc->lheight * offl;
#ifdef WITH_INPUT_IME
/* consider the effect of composition string */
if (ime_data && ime_data->composite_len) {
char *end = NULL;
if (ime_data->cursor_position != -1)
end = ime_data->composite + ime_data->cursor_position;
console_cursor_wrap_offset(ime_data->composite, tvc->console_width, &offl, &offc, end);
/* cursor inside the composition string */
pen[0] = tvc->cwidth * offc;
pen[1] = -2 - tvc->lheight * offl;
if (end != NULL)
console_cursor_wrap_offset(end, tvc->console_width, &offl, &offc, NULL);
}
#endif /* WITH_INPUT_IME */
console_cursor_wrap_offset(cl->line + cl->cursor, tvc->console_width, &offl, &offc, NULL);
pen[1] += tvc->lheight * offl;
@@ -191,6 +211,15 @@ static int console_textview_line_color(struct TextViewContext *tvc, unsigned cha
(xy[0] + pen[0]) + 1,
(xy[1] + pen[1] + tvc->lheight)
);
#ifdef WITH_INPUT_IME
/* cursor following */
if (ime_data && ime_data->composite_len) {
ime_data->cursor_xy[0] = (xy[0] + pen[0]) + 1;
ime_data->cursor_xy[1] = (xy[1] + pen[1]) - 1;
}
#endif
}
console_line_color(fg, cl_iter->type);
@@ -203,8 +232,8 @@ static void console_textview_const_colors(TextViewContext *UNUSED(tvc), unsigned
UI_GetThemeColor4ubv(TH_CONSOLE_SELECT, bg_sel);
}
static int console_textview_main__internal(struct SpaceConsole *sc, ARegion *ar, int draw,
int mval[2], void **mouse_pick, int *pos_pick)
static int console_textview_main__internal(struct SpaceConsole *sc, ARegion *ar, wmImeData *ime_data,
int draw, int mval[2], void **mouse_pick, int *pos_pick)
{
ConsoleLine cl_dummy = {NULL};
int ret = 0;
@@ -232,27 +261,33 @@ static int console_textview_main__internal(struct SpaceConsole *sc, ARegion *ar,
tvc.ymax = v2d->cur.ymax;
tvc.winx = ar->winx - V2D_SCROLL_WIDTH;
#ifdef WITH_INPUT_IME
if (!draw) {
ime_data = NULL;
}
#endif
console_scrollback_prompt_begin(sc, &cl_dummy);
ret = textview_draw(&tvc, draw, mval, mouse_pick, pos_pick);
ret = textview_draw(&tvc, ime_data, draw, mval, mouse_pick, pos_pick);
console_scrollback_prompt_end(sc, &cl_dummy);
return ret;
}
void console_textview_main(struct SpaceConsole *sc, ARegion *ar)
void console_textview_main(struct SpaceConsole *sc, ARegion *ar, wmImeData *ime_data)
{
int mval[2] = {INT_MAX, INT_MAX};
console_textview_main__internal(sc, ar, 1, mval, NULL, NULL);
console_textview_main__internal(sc, ar, ime_data, 1, mval, NULL, NULL);
}
int console_textview_height(struct SpaceConsole *sc, ARegion *ar)
{
int mval[2] = {INT_MAX, INT_MAX};
return console_textview_main__internal(sc, ar, 0, mval, NULL, NULL);
return console_textview_main__internal(sc, ar, NULL, 0, mval, NULL, NULL);
}
int console_char_pick(struct SpaceConsole *sc, ARegion *ar, const int mval[2])
int console_char_pick(struct SpaceConsole *sc, ARegion *ar, wmImeData *ime_data, const int mval[2])
{
int pos_pick = 0;
void *mouse_pick = NULL;
@@ -261,6 +296,6 @@ int console_char_pick(struct SpaceConsole *sc, ARegion *ar, const int mval[2])
mval_clamp[0] = CLAMPIS(mval[0], CONSOLE_DRAW_MARGIN, ar->winx - CONSOLE_DRAW_MARGIN);
mval_clamp[1] = CLAMPIS(mval[1], CONSOLE_DRAW_MARGIN, ar->winy - CONSOLE_DRAW_MARGIN);
console_textview_main__internal(sc, ar, 0, mval_clamp, &mouse_pick, &pos_pick);
console_textview_main__internal(sc, ar, ime_data, 0, mval_clamp, &mouse_pick, &pos_pick);
return pos_pick;
}

View File

@@ -35,9 +35,9 @@ struct ReportList;
struct bContext;
/* console_draw.c */
void console_textview_main(struct SpaceConsole *sc, struct ARegion *ar);
void console_textview_main(struct SpaceConsole *sc, struct ARegion *ar, struct wmImeData *ime_data);
int console_textview_height(struct SpaceConsole *sc, struct ARegion *ar); /* needed to calculate the scrollbar */
int console_char_pick(struct SpaceConsole *sc, struct ARegion *ar, const int mval[2]);
int console_char_pick(struct SpaceConsole *sc, struct ARegion *ar, struct wmImeData *ime_data, const int mval[2]);
void console_scrollback_prompt_begin(struct SpaceConsole *sc, ConsoleLine *cl_dummy);
void console_scrollback_prompt_end(struct SpaceConsole *sc, ConsoleLine *cl_dummy);

View File

@@ -394,6 +394,37 @@ static int console_insert_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static int console_insert_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
#ifdef WITH_INPUT_IME
wmWindow *win = CTX_wm_window(C);
wmImeData *ime = win->ime_data;
ARegion *ar = CTX_wm_region(C);
if (event->type == WM_IME_COMPOSITE_EVENT) {
ED_area_tag_redraw(CTX_wm_area(C));
console_scroll_bottom(ar);
}
/* composition complete
* some Japanese IMEs return result without WM_IME_COMPOSITE_END event
*/
if (event->type == WM_IME_COMPOSITE_EVENT && ime->result_len) {
char *str = "";
if (ime->result) str = ime->result;
RNA_string_set(op->ptr, "text", str);
console_insert_exec(C, op);
}
if (event->type == WM_IME_COMPOSITE_END)
return OPERATOR_FINISHED;
#else
(void)C; (void)op; (void)event;
#endif /* WITH_INPUT_IME */
return OPERATOR_RUNNING_MODAL;
}
static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
// if (!RNA_struct_property_is_set(op->ptr, "text")) { /* always set from keymap XXX */
@@ -405,6 +436,19 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve
if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) {
return OPERATOR_PASS_THROUGH;
}
/* most IME shortcut for switch IME, fullwidth/halfwidth and so on */
if (WM_event_is_ime_switch(event))
{
return OPERATOR_PASS_THROUGH;
}
/* composition begin and enter modal */
else if (event->type == WM_IME_COMPOSITE_START) {
SpaceConsole *sc = CTX_wm_space_console(C);
WM_event_add_modal_handler(C, op);
/* clear seletion */
sc->sel_end = sc->sel_start;
return console_insert_modal(C, op, event);
}
else {
char str[BLI_UTF8_MAX + 1];
size_t len;
@@ -436,6 +480,7 @@ void CONSOLE_OT_insert(wmOperatorType *ot)
/* api callbacks */
ot->exec = console_insert_exec;
ot->invoke = console_insert_invoke;
ot->modal = console_insert_modal;
ot->poll = ED_operator_console_active;
/* properties */
@@ -1017,10 +1062,10 @@ typedef struct SetConsoleCursor {
} SetConsoleCursor;
// TODO, cursor placement without selection
static void console_cursor_set_to_pos(SpaceConsole *sc, ARegion *ar, SetConsoleCursor *scu, int mval[2], int UNUSED(sel))
static void console_cursor_set_to_pos(SpaceConsole *sc, ARegion *ar, SetConsoleCursor *scu, wmImeData *ime_data, int mval[2], int UNUSED(sel))
{
int pos;
pos = console_char_pick(sc, ar, mval);
pos = console_char_pick(sc, ar, ime_data, mval);
if (scu->sel_init == INT_MAX) {
scu->sel_init = pos;
@@ -1043,6 +1088,7 @@ static void console_cursor_set_to_pos(SpaceConsole *sc, ARegion *ar, SetConsoleC
static void console_modal_select_apply(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
SpaceConsole *sc = CTX_wm_space_console(C);
ARegion *ar = CTX_wm_region(C);
SetConsoleCursor *scu = op->customdata;
@@ -1055,7 +1101,7 @@ static void console_modal_select_apply(bContext *C, wmOperator *op, const wmEven
sel_prev[0] = sc->sel_start;
sel_prev[1] = sc->sel_end;
console_cursor_set_to_pos(sc, ar, scu, mval, true);
console_cursor_set_to_pos(sc, ar, scu, win->ime_data, mval, true);
/* only redraw if the selection changed */
if (sel_prev[0] != sc->sel_start || sel_prev[1] != sc->sel_end) {

View File

@@ -48,7 +48,9 @@
#include "WM_api.h"
#include "WM_types.h"
#include "wm_window.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "UI_view2d.h"
@@ -167,6 +169,12 @@ static void console_cursor(wmWindow *win, ScrArea *sa, ARegion *ar)
if (st->text && BLI_rcti_isect_pt(&st->txtbar, win->eventstate->x - ar->winrct.xmin, st->txtbar.ymin)) {
wmcursor = CURSOR_STD;
}
#ifdef WITH_INPUT_IME
/* XXX find a better place */
else {
wm_window_IME_begin(win, ar->winrct.xmin, ar->winrct.ymin, 0, 0, true);
}
#endif
WM_cursor_set(win, wmcursor);
}
@@ -225,6 +233,13 @@ static void console_main_area_draw(const bContext *C, ARegion *ar)
SpaceConsole *sc = CTX_wm_space_console(C);
View2D *v2d = &ar->v2d;
View2DScrollers *scrollers;
#ifdef WITH_INPUT_IME
wmWindow *win = CTX_wm_window(C);
wmImeData *ime_data = win->ime_data;
bool is_ime_active = ime_data &&
ime_data->composite_len &&
BLI_rcti_isect_pt_v(&ar->winrct, &win->eventstate->x);
#endif
if (BLI_listbase_is_empty(&sc->scrollback))
WM_operator_name_call((bContext *)C, "CONSOLE_OT_banner", WM_OP_EXEC_DEFAULT, NULL);
@@ -239,7 +254,33 @@ static void console_main_area_draw(const bContext *C, ARegion *ar)
/* data... */
console_history_verify(C); /* make sure we have some command line */
console_textview_main(sc, ar);
#ifdef WITH_INPUT_IME
if (is_ime_active) {
/* get cursor position from console_textview_main and repositon ime window */
ConsoleLine *line = (ConsoleLine *)sc->history.last;
ime_data->cursor_pos_text = line->cursor + strlen(sc->prompt);
}
else {
/* delete ImeData if it didn't exist previously */
ime_data = NULL;
}
console_textview_main(sc, ar, ime_data);
if (ime_data && is_ime_active) {
int x = ime_data->cursor_xy[0];
int y = ime_data->cursor_xy[1];
ui_region_to_window(ar, &x, &y);
wm_window_IME_begin(win, x + 5, y, 0, 0, false);
ime_data->cursor_xy[0] = ime_data->cursor_xy[1] = 0;
}
#else
console_textview_main(sc, ar, NULL);
#endif /* WITH_INPUT_IME */
/* reset view matrix */
UI_view2d_view_restore(C);
@@ -354,6 +395,8 @@ static void console_keymap(struct wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "CONSOLE_OT_indent", TABKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "CONSOLE_OT_unindent", TABKEY, KM_PRESS, KM_SHIFT, 0);
WM_keymap_add_item(keymap, "CONSOLE_OT_insert", WM_IME_COMPOSITE_START, KM_ANY, KM_ANY, 0);
WM_keymap_add_item(keymap, "CONSOLE_OT_insert", KM_TEXTINPUT, KM_ANY, KM_ANY, 0); // last!
}

View File

@@ -54,6 +54,12 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
if(WIN32)
if(WITH_INPUT_IME)
add_definitions(-DWITH_INPUT_IME)
endif()
endif()
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_editor_space_info "${SRC}" "${INC}" "${INC_SYS}")

View File

@@ -191,7 +191,7 @@ static int report_textview_line_get(struct TextViewContext *tvc, const char **li
return 1;
}
static int report_textview_line_color(struct TextViewContext *tvc, unsigned char fg[3], unsigned char bg[3])
static int report_textview_line_color(struct TextViewContext *tvc, struct wmImeData *ime_data, unsigned char fg[3], unsigned char bg[3])
{
Report *report = (Report *)tvc->iter;
info_report_color(fg, bg, report, tvc->iter_tmp % 2);
@@ -232,7 +232,7 @@ static int report_textview_line_color(struct TextViewContext *tvc, unsigned char
#undef USE_INFO_NEWLINE
static int info_textview_main__internal(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports,
static int info_textview_main__internal(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports, struct wmImeData *ime_data,
int draw, int mval[2], void **mouse_pick, int *pos_pick)
{
int ret = 0;
@@ -259,12 +259,12 @@ static int info_textview_main__internal(struct SpaceInfo *sinfo, ARegion *ar, Re
tvc.ymax = v2d->cur.ymax;
tvc.winx = ar->winx - V2D_SCROLL_WIDTH;
ret = textview_draw(&tvc, draw, mval, mouse_pick, pos_pick);
ret = textview_draw(&tvc, ime_data, draw, mval, mouse_pick, pos_pick);
return ret;
}
void *info_text_pick(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports, int mouse_y)
void *info_text_pick(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports, struct wmImeData *ime_data, int mouse_y)
{
void *mouse_pick = NULL;
int mval[2];
@@ -272,19 +272,19 @@ void *info_text_pick(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports,
mval[0] = 0;
mval[1] = mouse_y;
info_textview_main__internal(sinfo, ar, reports, 0, mval, &mouse_pick, NULL);
info_textview_main__internal(sinfo, ar, reports, ime_data, 0, mval, &mouse_pick, NULL);
return (void *)mouse_pick;
}
int info_textview_height(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports)
int info_textview_height(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports, struct wmImeData *ime_data)
{
int mval[2] = {INT_MAX, INT_MAX};
return info_textview_main__internal(sinfo, ar, reports, 0, mval, NULL, NULL);
return info_textview_main__internal(sinfo, ar, reports, ime_data, 0, mval, NULL, NULL);
}
void info_textview_main(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports)
void info_textview_main(struct SpaceInfo *sinfo, ARegion *ar, ReportList *reports, struct wmImeData *ime_data)
{
int mval[2] = {INT_MAX, INT_MAX};
info_textview_main__internal(sinfo, ar, reports, 1, mval, NULL, NULL);
info_textview_main__internal(sinfo, ar, reports, ime_data, 1, mval, NULL, NULL);
}

View File

@@ -53,9 +53,9 @@ void FILE_OT_find_missing_files(struct wmOperatorType *ot);
void INFO_OT_reports_display_update(struct wmOperatorType *ot);
/* info_draw.c */
void *info_text_pick(struct SpaceInfo *sinfo, struct ARegion *ar, ReportList *reports, int mouse_y);
int info_textview_height(struct SpaceInfo *sinfo, struct ARegion *ar, struct ReportList *reports);
void info_textview_main(struct SpaceInfo *sinfo, struct ARegion *ar, struct ReportList *reports);
void *info_text_pick(struct SpaceInfo *sinfo, struct ARegion *ar, ReportList *reports, struct wmImeData *ime_data, int mouse_y);
int info_textview_height(struct SpaceInfo *sinfo, struct ARegion *ar, struct ReportList *reports, struct wmImeData *ime_data);
void info_textview_main(struct SpaceInfo *sinfo, struct ARegion *ar, struct ReportList *reports, struct wmImeData *ime_data);
/* info_report.c */
int info_report_mask(struct SpaceInfo *sinfo);

View File

@@ -128,15 +128,16 @@ static int select_report_pick_exec(bContext *C, wmOperator *op)
static int select_report_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
SpaceInfo *sinfo = CTX_wm_space_info(C);
ARegion *ar = CTX_wm_region(C);
ReportList *reports = CTX_wm_reports(C);
Report *report;
/* uses opengl */
wmSubWindowSet(CTX_wm_window(C), ar->swinid);
wmSubWindowSet(win, ar->swinid);
report = info_text_pick(sinfo, ar, reports, event->mval[1]);
report = info_text_pick(sinfo, ar, reports, win->ime_data, event->mval[1]);
RNA_int_set(op->ptr, "report_index", BLI_findindex(&reports->list, report));
@@ -218,6 +219,7 @@ void INFO_OT_select_all_toggle(wmOperatorType *ot)
/* borderselect operator */
static int borderselect_exec(bContext *C, wmOperator *op)
{
wmWindow *win = CTX_wm_window(C);
SpaceInfo *sinfo = CTX_wm_space_info(C);
ARegion *ar = CTX_wm_region(C);
ReportList *reports = CTX_wm_reports(C);
@@ -245,8 +247,8 @@ static int borderselect_exec(bContext *C, wmOperator *op)
}
}
report_min = info_text_pick(sinfo, ar, reports, rect.ymax);
report_max = info_text_pick(sinfo, ar, reports, rect.ymin);
report_min = info_text_pick(sinfo, ar, reports, win->ime_data, rect.ymax);
report_max = info_text_pick(sinfo, ar, reports, win->ime_data, rect.ymin);
/* get the first report if none found */
if (report_min == NULL) {

View File

@@ -139,10 +139,11 @@ static void info_main_area_init(wmWindowManager *wm, ARegion *ar)
static void info_textview_update_rect(const bContext *C, ARegion *ar)
{
wmWindow *win = CTX_wm_window(C);
SpaceInfo *sinfo = CTX_wm_space_info(C);
View2D *v2d = &ar->v2d;
UI_view2d_totRect_set(v2d, ar->winx - 1, info_textview_height(sinfo, ar, CTX_wm_reports(C)));
UI_view2d_totRect_set(v2d, ar->winx - 1, info_textview_height(sinfo, ar, CTX_wm_reports(C), win->ime_data));
}
static void info_main_area_draw(const bContext *C, ARegion *ar)
@@ -151,6 +152,7 @@ static void info_main_area_draw(const bContext *C, ARegion *ar)
SpaceInfo *sinfo = CTX_wm_space_info(C);
View2D *v2d = &ar->v2d;
View2DScrollers *scrollers;
wmWindow *win = CTX_wm_window(C);
/* clear and setup matrix */
UI_ThemeClearColor(TH_BACK);
@@ -165,7 +167,7 @@ static void info_main_area_draw(const bContext *C, ARegion *ar)
/* worlks best with no view2d matrix set */
UI_view2d_view_ortho(v2d);
info_textview_main(sinfo, ar, CTX_wm_reports(C));
info_textview_main(sinfo, ar, CTX_wm_reports(C), win->ime_data);
/* reset view matrix */
UI_view2d_view_restore(C);

View File

@@ -37,6 +37,7 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BIF_gl.h"
@@ -45,6 +46,10 @@
#include "textview.h"
#include "UI_interface.h"
#include "WM_types.h"
static void console_font_begin(TextViewContext *sc)
{
/* 0.875 is based on: 16 pixels lines get 14 pixel text */
@@ -88,6 +93,25 @@ static void console_draw_sel(const char *str, const int sel[2], const int xy[2],
}
}
#ifdef WITH_INPUT_IME
static void console_draw_underline(const char *str, const int sel[2], const int xy[2], const int str_len_draw,
const int cwidth, const int height, const unsigned char bg_sel[4])
{
if (sel[0] <= str_len_draw && sel[1] >= 0) {
const int col_xmin = txt_utf8_offset_to_column(str, max_ii(sel[0], 0));
const int col_xmax = txt_utf8_offset_to_column(str, min_ii(sel[1], str_len_draw));
const int x = xy[0] + (cwidth * col_xmin);
const int y = xy[1];
const int width = (xy[0] + (cwidth * col_xmax)) - x;
glColor3ubv(bg_sel);
UI_text_draw_underline(x, y, width, height);
}
}
#endif /* WITH_INPUT_IME */
/* warning: allocated memory for 'offsets' must be freed by caller */
static int console_wrap_offsets(const char *str, int len, int width, int *lines, int **offsets)
{
@@ -117,13 +141,33 @@ static int console_wrap_offsets(const char *str, int len, int width, int *lines,
/* return 0 if the last line is off the screen
* should be able to use this for any string type */
static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str_len,
static int console_draw_string(ConsoleDrawContext *cdc, wmImeData *ime_data, const char *str, int str_len,
const unsigned char fg[3], const unsigned char bg[3], const unsigned char bg_sel[4])
{
int tot_lines; /* total number of lines for wrapping */
int *offsets; /* offsets of line beginnings for wrapping */
int y_next;
const int mono = blf_mono_font;
const int mono = blf_mono_font;;
int cursor;
#ifdef WITH_INPUT_IME
if (ime_data && !(ime_data->cursor_pos_text || ime_data->cursor_xy)) {
ime_data = NULL;
}
if (ime_data && ime_data->cursor_xy && ime_data->composite_len) {
size_t slen;
char buf[1024]; /* should be enough */
cursor = ime_data->cursor_pos_text;
/* insert composite str */
slen = ime_data->composite_len;
BLI_snprintf(buf, slen + str_len + 1, "%s%s%s", str, ime_data->composite, str + cursor);
str = buf;
str_len += slen;
}
#endif
str_len = console_wrap_offsets(str, str_len, cdc->console_width, &tot_lines, &offsets);
y_next = cdc->xy[1] + cdc->lheight * tot_lines;
@@ -173,6 +217,7 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str
size_t len = str_len - initial_offset;
const char *s = str + initial_offset;
int i;
int cursors[2];
int sel_orig[2];
copy_v2_v2_int(sel_orig, cdc->sel);
@@ -199,6 +244,25 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str
glColor3ubv(fg);
}
#ifdef WITH_INPUT_IME
cursors[0] = cursor;
/* draw IME composite underline */
if (ime_data && ime_data->composite_len) {
cursors[0] -= initial_offset;
cursors[1] = cursors[0] + ime_data->composite_len;
console_draw_underline(s, cursors, cdc->xy, len, cdc->cwidth, 1, fg);
/* draw the thicker line */
if (ime_data->target_start != -1 && ime_data->target_end != -1) {
int isel[2];
isel[0] = cursors[0] + ime_data->target_start;
isel[1] = cursors[0] + ime_data->target_end;
console_draw_underline(s, isel, cdc->xy, len, cdc->cwidth, 2, fg);
}
}
#endif
cdc->xy[1] += cdc->lheight;
for (i = tot_lines - 1; i > 0; i--) {
@@ -215,6 +279,25 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str
glColor3ubv(fg);
}
#ifdef WITH_INPUT_IME
/* draw IME composite underline */
if (ime_data &&ime_data->composite_len) {
cursors[0] += len;
cursors[1] = cursors[0] + ime_data->composite_len;
console_draw_underline(s, cursors, cdc->xy, len, cdc->cwidth, 1, fg);
/* draw the thicker line */
if (ime_data->target_start != -1 && ime_data->target_end != -1) {
int isel[2];
isel[0] = cursors[0] + ime_data->target_start;
isel[1] = cursors[0] + ime_data->target_end;
console_draw_underline(s, isel, cdc->xy, len, cdc->cwidth, 2, fg);
}
}
#else
(void)cursor; (void)cursors;
#endif
cdc->xy[1] += cdc->lheight;
/* check if were out of view bounds */
@@ -250,6 +333,24 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str
console_step_sel(cdc, -(str_len + 1));
}
#ifdef WITH_INPUT_IME
/* draw IME composite underline */
if (ime_data &&ime_data->composite_len) {
int isel[2];
isel[0] = cursor;
isel[1] = cursor + ime_data->composite_len;
console_draw_underline(str, isel, cdc->xy, str_len, cdc->cwidth, 1, fg);
/* draw the thicker line */
if (ime_data->target_start != -1 && ime_data->target_end != -1) {
isel[0] = cursor + ime_data->target_start;
isel[1] = cursor + ime_data->target_end;
console_draw_underline(str, isel, cdc->xy, str_len, cdc->cwidth, 2, fg);
}
}
#endif
cdc->xy[1] += cdc->lheight;
if (cdc->xy[1] > cdc->ymax) {
@@ -264,7 +365,7 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str
#define CONSOLE_DRAW_MARGIN 4
int textview_draw(TextViewContext *tvc, const int draw, int mval[2], void **mouse_pick, int *pos_pick)
int textview_draw(TextViewContext *tvc, wmImeData *ime_data, const int draw, int mval[2], void **mouse_pick, int *pos_pick)
{
ConsoleDrawContext cdc = {0};
@@ -308,6 +409,7 @@ int textview_draw(TextViewContext *tvc, const int draw, int mval[2], void **mous
tvc->console_width = cdc.console_width;
tvc->iter_index = 0;
if (tvc->sel_start != tvc->sel_end) {
sel[0] = tvc->sel_start;
sel[1] = tvc->sel_end;
@@ -328,11 +430,17 @@ int textview_draw(TextViewContext *tvc, const int draw, int mval[2], void **mous
y_prev = xy[1];
if (draw)
color_flag = tvc->line_color(tvc, fg, bg);
color_flag = tvc->line_color(tvc, ime_data, fg, bg);
tvc->line_get(tvc, &ext_line, &ext_len);
if (!console_draw_string(&cdc, ext_line, ext_len,
#ifdef WITH_INPUT_IME
if (ime_data && tvc->iter_index) {
ime_data = NULL;
}
#endif
if (!console_draw_string(&cdc, ime_data, ext_line, ext_len,
(color_flag & TVC_LINE_FG) ? fg : NULL,
(color_flag & TVC_LINE_BG) ? bg : NULL,
bg_sel))

View File

@@ -47,17 +47,16 @@ typedef struct TextViewContext {
/* iterator */
int (*step)(struct TextViewContext *tvc);
int (*line_get)(struct TextViewContext *tvc, const char **, int *);
int (*line_color)(struct TextViewContext *tvc, unsigned char fg[3], unsigned char bg[3]);
int (*line_color)(struct TextViewContext *tvc, struct wmImeData *ime_data, unsigned char fg[3], unsigned char bg[3]);
void (*const_colors)(struct TextViewContext *tvc, unsigned char bg_sel[4]); /* constant theme colors */
void *iter;
int iter_index;
int iter_char; /* char intex, used for multi-line report display */
int iter_char_next; /* same as above, next \n */
int iter_tmp; /* internal iterator use */
} TextViewContext;
int textview_draw(struct TextViewContext *tvc, const int draw, int mval[2], void **mouse_pick, int *pos_pick);
int textview_draw(struct TextViewContext *tvc, struct wmImeData *ime_data, const int draw, int mval[2], void **mouse_pick, int *pos_pick);
#define TVC_LINE_FG (1<<0)
#define TVC_LINE_BG (1<<1)

View File

@@ -63,4 +63,10 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
if(WIN32)
if(WITH_INPUT_IME)
add_definitions(-DWITH_INPUT_IME)
endif()
endif()
blender_add_lib(bf_editor_space_text "${SRC}" "${INC}" "${INC_SYS}")

View File

@@ -48,6 +48,7 @@
#include "WM_api.h"
#include "WM_types.h"
#include "wm_window.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -380,6 +381,8 @@ static void text_keymap(struct wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "TEXT_OT_autocomplete", SPACEKEY, KM_PRESS, KM_CTRL, 0);
WM_keymap_add_item(keymap, "TEXT_OT_line_number", KM_TEXTINPUT, KM_ANY, KM_ANY, 0);
WM_keymap_add_item(keymap, "TEXT_OT_insert", WM_IME_COMPOSITE_START, KM_ANY, KM_ANY, 0);
WM_keymap_add_item(keymap, "TEXT_OT_insert", KM_TEXTINPUT, KM_ANY, KM_ANY, 0); // last!
}
@@ -427,6 +430,13 @@ static void text_main_area_draw(const bContext *C, ARegion *ar)
{
/* draw entirely, view changes should be handled here */
SpaceText *st = CTX_wm_space_text(C);
#ifdef WITH_INPUT_IME
wmWindow *win = CTX_wm_window(C);
wmImeData *ime_data = win->ime_data;
bool is_ime_active = ime_data &&
ime_data->composite_len &&
BLI_rcti_isect_pt_v(&ar->winrct, &win->eventstate->x);
#endif
//View2D *v2d = &ar->v2d;
/* clear and setup matrix */
@@ -436,7 +446,27 @@ static void text_main_area_draw(const bContext *C, ARegion *ar)
// UI_view2d_view_ortho(v2d);
/* data... */
draw_text_main(st, ar);
/* get cursor position from draw_text_main and repositon ime window */
#ifdef WITH_INPUT_IME
if (!is_ime_active) {
ime_data = NULL;
}
draw_text_main(win, st, ar);
if (ime_data && is_ime_active) {
int x = ime_data->cursor_xy[0];
int y = ime_data->cursor_xy[1];
ui_region_to_window(ar, &x, &y);
wm_window_IME_begin(win, x + 5, y, 0, 0, false);
ime_data->cursor_xy[0] = ime_data->cursor_xy[1] = 0;
}
#else
draw_text_main(win, st, ar);
#endif
/* reset view matrix */
// UI_view2d_view_restore(C);
@@ -453,6 +483,13 @@ static void text_cursor(wmWindow *win, ScrArea *sa, ARegion *ar)
wmcursor = CURSOR_STD;
}
#ifdef WITH_INPUT_IME
/* enable IME if the text region has text which can be edited */
if (st->text && !st->text->id.lib) {
wm_window_IME_begin(win, ar->winrct.xmin, ar->winrct.ymin, 0, 0, true);
}
#endif
WM_cursor_set(win, wmcursor);
}

View File

@@ -34,10 +34,12 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_string_utf8.h"
#include "DNA_text_types.h"
#include "DNA_space_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
#include "BKE_context.h"
#include "BKE_suggestions.h"
@@ -53,6 +55,8 @@
#include "text_intern.h"
#include "text_format.h"
#include "WM_types.h"
/******************** text font drawing ******************/
// XXX, fixme
#define mono blf_mono_font
@@ -412,10 +416,21 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w
/* Draw the visible portion of text on the overshot line */
for (a = fstart, ma = mstart; ma < mend; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
int len;
if (use_syntax) {
if (fmt_prev != format[a]) format_draw_color(fmt_prev = format[a]);
}
x += text_font_draw_character_utf8(st, x, y, str + ma);
len = text_font_draw_character_utf8(st, x, y, str + ma);
#ifdef WITH_INPUT_IME
/* draw underline */
if (format && format[a] == FMT_TYPE_ULINE)
UI_text_draw_underline(x, y, len, 1);
else
if (format && format[a] == FMT_TYPE_TULINE)
UI_text_draw_underline(x, y, len, 2);
x += len;
#endif
fpos++;
}
y -= st->lheight_dpi + TXT_LINE_SPACING;
@@ -435,11 +450,22 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w
/* Draw the remaining text */
for (a = fstart, ma = mstart; str[ma] && y > clip_min_y; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
int len;
if (use_syntax) {
if (fmt_prev != format[a]) format_draw_color(fmt_prev = format[a]);
}
x += text_font_draw_character_utf8(st, x, y, str + ma);
len = text_font_draw_character_utf8(st, x, y, str + ma);
#ifdef WITH_INPUT_IME
/* draw underline */
if (format && format[a] == FMT_TYPE_ULINE)
UI_text_draw_underline(x, y, len, 1);
else
if (format && format[a] == FMT_TYPE_TULINE)
UI_text_draw_underline(x, y, len, 2);
x += len;
#endif
}
flatten_string_free(&fs);
@@ -447,7 +473,7 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w
return lines;
}
static void text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int x, int y, const char *format)
static void text_draw(SpaceText *st, wmImeData *ime_data, char *str, int cshift, int maxwidth, int x, int y, const char *format)
{
const bool use_syntax = (st->showsyntax && format);
FlattenString fs;
@@ -482,13 +508,20 @@ static void text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int x,
x += st->cwidth * padding;
if (use_syntax) {
int a, str_shift = 0;
if ((use_syntax || (ime_data && ime_data->composite_len)) && format) {
int a, str_shift = 0, len;
char fmt_prev = 0xff;
for (a = 0; a < amount; a++) {
if (format[a] != fmt_prev) format_draw_color(fmt_prev = format[a]);
x += text_font_draw_character_utf8(st, x, y, in + str_shift);
len = text_font_draw_character_utf8(st, x, y, in + str_shift);
/* draw underline */
if (fmt_prev == FMT_TYPE_ULINE)
UI_text_draw_underline(x, y, len, 1);
else
if (fmt_prev == FMT_TYPE_TULINE)
UI_text_draw_underline(x, y, len, 2);
x += len;
str_shift += BLI_str_utf8_size_safe(in + str_shift);
}
}
@@ -908,7 +941,7 @@ static void draw_textscroll(SpaceText *st, rcti *scroll, rcti *back)
/*********************** draw documentation *******************************/
static void draw_documentation(SpaceText *st, ARegion *ar)
static void draw_documentation(SpaceText *st, ARegion *ar, wmImeData *ime_data)
{
TextLine *tmp;
char *docs, buf[DOC_WIDTH + 1], *p;
@@ -971,7 +1004,7 @@ static void draw_documentation(SpaceText *st, ARegion *ar)
buf[i] = '\0';
if (lines >= 0) {
y -= st->lheight_dpi;
text_draw(st, buf, 0, 0, x + 4, y - 3, NULL);
text_draw(st, ime_data, buf, 0, 0, x + 4, y - 3, NULL);
}
i = 0; br = DOC_WIDTH; lines++;
}
@@ -980,7 +1013,7 @@ static void draw_documentation(SpaceText *st, ARegion *ar)
buf[br] = '\0';
if (lines >= 0) {
y -= st->lheight_dpi;
text_draw(st, buf, 0, 0, x + 4, y - 3, NULL);
text_draw(st, ime_data, buf, 0, 0, x + 4, y - 3, NULL);
}
p -= i - br - 1; /* Rewind pointer to last break */
i = 0; br = DOC_WIDTH; lines++;
@@ -990,13 +1023,13 @@ static void draw_documentation(SpaceText *st, ARegion *ar)
if (0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) {
// XXX doc_scroll--;
draw_documentation(st, ar);
draw_documentation(st, ar, ime_data);
}
}
/*********************** draw suggestion list *******************************/
static void draw_suggestion_list(SpaceText *st, ARegion *ar)
static void draw_suggestion_list(SpaceText *st, ARegion *ar, wmImeData *ime_data)
{
SuggItem *item, *first, *last, *sel;
char str[SUGG_LIST_WIDTH * BLI_UTF8_MAX + 1];
@@ -1061,7 +1094,7 @@ static void draw_suggestion_list(SpaceText *st, ARegion *ar)
}
format_draw_color(item->type);
text_draw(st, str, 0, 0, x + margin_x, y - 1, NULL);
text_draw(st, ime_data, str, 0, 0, x + margin_x, y - 1, NULL);
if (item == last) break;
}
@@ -1069,7 +1102,7 @@ static void draw_suggestion_list(SpaceText *st, ARegion *ar)
/*********************** draw cursor ************************/
static void draw_cursor(SpaceText *st, ARegion *ar)
static void draw_cursor(SpaceText *st, ARegion *ar, wmImeData *ime_data)
{
Text *text = st->text;
int vcurl, vcurc, vsell, vselc, hidden = 0;
@@ -1182,6 +1215,13 @@ static void draw_cursor(SpaceText *st, ARegion *ar)
else {
UI_ThemeColor(TH_HILITE);
glRecti(x - 1, y, x + 1, y - lheight);
#ifdef WITH_INPUT_IME
if (ime_data && ime_data->composite_len && text->curl) {
ime_data->cursor_xy[0] = x + 1;
ime_data->cursor_xy[1] = y - lheight - 3;
}
#endif
}
}
}
@@ -1315,7 +1355,7 @@ static void draw_brackets(SpaceText *st, ARegion *ar)
/*********************** main area drawing *************************/
void draw_text_main(SpaceText *st, ARegion *ar)
void draw_text_main(wmWindow *win, SpaceText *st, ARegion *ar)
{
Text *text = st->text;
TextFormatType *tft;
@@ -1327,6 +1367,14 @@ void draw_text_main(SpaceText *st, ARegion *ar)
int margin_column_x;
/* don't draw lines below this */
const int clip_min_y = -(int)(st->lheight_dpi - 1);
#ifdef WITH_INPUT_IME
TextLine bak = {0};
wmImeData *ime_data = win->ime_data;
bool is_ime_active = ime_data &&
ime_data->cursor_xy &&
ime_data->composite_len &&
BLI_rcti_isect_pt_v(&ar->winrct, &win->eventstate->x);
#endif
/* if no text, nothing to do */
if (!text)
@@ -1336,6 +1384,57 @@ void draw_text_main(SpaceText *st, ARegion *ar)
st->lheight_dpi = (U.widget_unit * st->lheight) / 20;
st->viewlines = (st->lheight_dpi) ? (int)(ar->winy - clip_min_y) / (st->lheight_dpi + TXT_LINE_SPACING) : 0;
#ifdef WITH_INPUT_IME
/* if is composing, backup and insert composition string */
if (is_ime_active && text->curl) {
int clen = ime_data->composite_len;
int tlen = text->curl->len;
int clen_utf8 = BLI_strnlen_utf8(ime_data->composite, clen);
int tlen_utf8 = BLI_strnlen_utf8(text->curl->line, tlen);
int max_size = tlen + clen + 1;
int max_size_utf8 = tlen_utf8 + clen_utf8 + 1;
int i = text->curc;
bak = *text->curl;
tmp = text->curl;
/* XXX - would be good if we could avoid this as it mallocs every redraw
* keep in mind we need to ensure virtually endless lines, so a limited array isn't a solution */
tmp->line = MEM_mallocN(max_size, "text ime backup");
tmp->format = MEM_mallocN(max_size_utf8, "text ime backup");
BLI_snprintf(tmp->line, max_size, "%s%s%s", bak.line, ime_data->composite, bak.line + i);
tmp->len += clen;
i = BLI_strnlen_utf8(bak.line, text->curc);
if (bak.format) {
memset(tmp->format + i, FMT_TYPE_ULINE, clen_utf8);
BLI_snprintf(tmp->format, tlen_utf8 - i, "%s%s%s", bak.format, tmp->format + i, bak.format + i);
}
else {
memset(tmp->format, FMT_TYPE_DEFAULT, clen_utf8 + tlen_utf8);
memset(tmp->format + i, FMT_TYPE_ULINE, clen_utf8);
}
/* set thicker line format */
if (ime_data->target_start != -1 && ime_data->target_end != -1) {
int target_start = BLI_strnlen_utf8(ime_data->composite, ime_data->target_start);
int target_end = BLI_strnlen_utf8(ime_data->composite, ime_data->target_end);
target_end -= target_start;
memset(tmp->format + i + target_start, FMT_TYPE_TULINE, target_end);
}
tmp->format[clen_utf8 + tlen_utf8] = '\0';
/* move the cursor */
text->curc += ime_data->cursor_position;
text->selc += ime_data->cursor_position;
}
#endif
text_update_drawcache(st, ar);
/* make sure all the positional pointers exist */
@@ -1393,7 +1492,7 @@ void draw_text_main(SpaceText *st, ARegion *ar)
winx = ar->winx - TXT_SCROLL_WIDTH;
/* draw cursor */
draw_cursor(st, ar);
draw_cursor(st, ar, ime_data);
/* draw the text */
UI_ThemeColor(TH_TEXT);
@@ -1423,7 +1522,7 @@ void draw_text_main(SpaceText *st, ARegion *ar)
}
else {
/* draw unwrapped text */
text_draw(st, tmp->line, st->left, ar->winx / st->cwidth, x, y, tmp->format);
text_draw(st, ime_data, tmp->line, st->left, ar->winx / st->cwidth, x, y, tmp->format);
y -= st->lheight_dpi + TXT_LINE_SPACING;
}
@@ -1447,10 +1546,21 @@ void draw_text_main(SpaceText *st, ARegion *ar)
draw_brackets(st, ar);
glTranslatef(GLA_PIXEL_OFS, GLA_PIXEL_OFS, 0.0f); /* XXX scroll requires exact pixel space */
draw_textscroll(st, &scroll, &back);
draw_documentation(st, ar);
draw_suggestion_list(st, ar);
draw_documentation(st, ar, ime_data);
draw_suggestion_list(st, ar, ime_data);
text_font_end(st);
#ifdef WITH_INPUT_IME
if (bak.line) {
MEM_freeN(text->curl->line);
MEM_freeN(text->curl->format);
*text->curl = bak;
/* recover the cursor */
text->curc -= ime_data->cursor_position;
text->selc -= ime_data->cursor_position;
}
#endif
}
/************************** update ***************************/

View File

@@ -93,6 +93,8 @@ enum {
FMT_TYPE_RESERVED = 'r', /* Reserved keywords currently not in use, but still prohibited (OSL -> switch e.g.) */
FMT_TYPE_KEYWORD = 'b', /* Built-in names (return, for, etc.) */
FMT_TYPE_DEFAULT = 'q', /* Regular text (identifiers, etc.) */
FMT_TYPE_ULINE = 'u', /* Regular text with underline, be used for composition string */
FMT_TYPE_TULINE = 't', /* Regular text with thicker underline, be used for target string */
};
TextFormatType *ED_text_format_get(Text *text);

View File

@@ -45,7 +45,7 @@ struct wmOperatorType;
struct wmWindowManager;
/* text_draw.c */
void draw_text_main(struct SpaceText *st, struct ARegion *ar);
void draw_text_main(struct wmWindow *win, struct SpaceText *st, struct ARegion *ar);
void text_update_line_edited(struct TextLine *line);
void text_update_edited(struct Text *text);

View File

@@ -2914,6 +2914,41 @@ static int text_insert_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static int text_insert_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
#ifdef WITH_INPUT_IME
wmWindow *win = CTX_wm_window(C);
wmImeData *ime_data = win->ime_data;
Text *text = CTX_data_edit_text(C);
/* composition complete
* some Japanese IMEs return result without WM_IME_COMPOSITE_END event
*/
if (event->type == WM_IME_COMPOSITE_EVENT && ime_data->result_len) {
const char *str = "";
int ret;
if (ime_data->result) str = ime_data->result;
RNA_string_set(op->ptr, "text", str);
ret = text_insert_exec(C, op);
if (ret == OPERATOR_CANCELLED)
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_TEXT, text);
}
if (event->type == WM_IME_COMPOSITE_END)
return OPERATOR_FINISHED;
if (event->type == WM_IME_COMPOSITE_EVENT) {
/* only redraw, don't clean away format */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_TEXT, text);
}
#else
(void)C; (void)op; (void)event;
#endif /* WITH_INPUT_IME */
return OPERATOR_RUNNING_MODAL;
}
static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int ret;
@@ -2927,6 +2962,20 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) {
return OPERATOR_PASS_THROUGH;
}
#ifdef WITH_INPUT_IME
/* most IME shortcut for switch IME, fullwidth/halfwidth and so on */
if (WM_event_is_ime_switch(event))
{
return OPERATOR_PASS_THROUGH;
}
/* composition begin and enter modal */
else if (event->type == WM_IME_COMPOSITE_START) {
Text *text = CTX_data_edit_text(C);
WM_event_add_modal_handler(C, op);
txt_delete_selected(text);
return text_insert_modal(C, op, event);
}
#endif /* WITH_INPUT_IME */
else {
char str[BLI_UTF8_MAX + 1];
size_t len;
@@ -2965,6 +3014,7 @@ void TEXT_OT_insert(wmOperatorType *ot)
/* api callbacks */
ot->exec = text_insert_exec;
ot->invoke = text_insert_invoke;
ot->modal = text_insert_modal;
ot->poll = text_edit_poll;
/* properties */

View File

@@ -191,6 +191,8 @@ typedef struct wmWindow {
short last_pie_event; /* exception to the above rule for nested pies, store last pie event for operators
* that spawn a new pie right after destruction of last pie */
struct wmImeData *ime_data; /* Input Method Editor data - complex character input (esp. for asian character input) */
struct wmEvent *eventstate; /* storage for event system */
struct wmSubWindow *curswin; /* internal for wm_subwindow.c only */

View File

@@ -130,10 +130,14 @@ if(WITH_BUILDINFO)
add_definitions(-DWITH_BUILDINFO)
endif()
if(WIN322)
if(WIN32)
list(APPEND INC
../../../intern/utfconv
)
if(WITH_INPUT_IME)
add_definitions(-DWITH_INPUT_IME)
endif()
endif()
if(WITH_COMPOSITOR)

View File

@@ -462,6 +462,8 @@ 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);
bool WM_event_is_ime_switch(const struct wmEvent *event);
#ifdef __cplusplus
}
#endif

View File

@@ -470,6 +470,23 @@ typedef struct wmTabletData {
float Ytilt; /* as above */
} wmTabletData;
/* similar to GHOST_TEventImeData */
/* XXX - names, comments */
typedef struct wmImeData {
size_t result_len, composite_len;
char *result; /* utf8 encoding */
char *composite; /* utf8 encoding */
int cursor_position; /* cursor position in the IME composition. */
int target_start; /* position of the beginning of the selection */
int target_end; /* position of the end of the selection */
int cursor_xy[2]; /* text cursor position */
int cursor_pos_text; /* cursor pos in text (console space only) */
bool is_ime_composite; /* for uiBut only */
} wmImeData;
typedef enum { /* motion progress, for modal handlers */
P_NOT_STARTED,
P_STARTING, /* <-- */

View File

@@ -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_composite = 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_composite = 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
/** \} */

View File

@@ -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);
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 */

View File

@@ -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.

View File

@@ -69,6 +69,10 @@ wmWindow *wm_window_copy (bContext *C, wmWindow *winorig);
void wm_window_testbreak (void);
/* *************** window IME api ************** */
void wm_window_IME_begin (wmWindow *win, int x, int y, int w, int h, bool complete);
void wm_window_IME_end (wmWindow *win);
/* *************** window operators ************** */
int wm_window_duplicate_exec(bContext *C, struct wmOperator *op);
int wm_window_fullscreen_toggle_exec(bContext *C, struct wmOperator *op);