aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llwindow/llwindowwin32.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:45:27 -0500
committerJacek Antonelli2008-08-15 23:45:27 -0500
commita8a62201ba762e98dff92cf49033e577fc34d8d4 (patch)
tree11f8513c5cdc222f2fac0c93eb724c089803c200 /linden/indra/llwindow/llwindowwin32.cpp
parentSecond Life viewer sources 1.18.6.4-RC (diff)
downloadmeta-impy-a8a62201ba762e98dff92cf49033e577fc34d8d4.zip
meta-impy-a8a62201ba762e98dff92cf49033e577fc34d8d4.tar.gz
meta-impy-a8a62201ba762e98dff92cf49033e577fc34d8d4.tar.bz2
meta-impy-a8a62201ba762e98dff92cf49033e577fc34d8d4.tar.xz
Second Life viewer sources 1.19.0.0
Diffstat (limited to 'linden/indra/llwindow/llwindowwin32.cpp')
-rw-r--r--linden/indra/llwindow/llwindowwin32.cpp663
1 files changed, 638 insertions, 25 deletions
diff --git a/linden/indra/llwindow/llwindowwin32.cpp b/linden/indra/llwindow/llwindowwin32.cpp
index 2fcb36c..171cc09 100644
--- a/linden/indra/llwindow/llwindowwin32.cpp
+++ b/linden/indra/llwindow/llwindowwin32.cpp
@@ -12,12 +12,12 @@
12 * ("GPL"), unless you have obtained a separate licensing agreement 12 * ("GPL"), unless you have obtained a separate licensing agreement
13 * ("Other License"), formally executed by you and Linden Lab. Terms of 13 * ("Other License"), formally executed by you and Linden Lab. Terms of
14 * the GPL can be found in doc/GPL-license.txt in this distribution, or 14 * the GPL can be found in doc/GPL-license.txt in this distribution, or
15 * online at http://secondlife.com/developers/opensource/gplv2 15 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
16 * 16 *
17 * There are special exceptions to the terms and conditions of the GPL as 17 * There are special exceptions to the terms and conditions of the GPL as
18 * it is applied to this Source Code. View the full text of the exception 18 * it is applied to this Source Code. View the full text of the exception
19 * in the file doc/FLOSS-exception.txt in this software distribution, or 19 * in the file doc/FLOSS-exception.txt in this software distribution, or
20 * online at http://secondlife.com/developers/opensource/flossexception 20 * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
21 * 21 *
22 * By copying, modifying or distributing this software, you acknowledge 22 * By copying, modifying or distributing this software, you acknowledge
23 * that you have read and understood your obligations described above, 23 * that you have read and understood your obligations described above,
@@ -57,6 +57,8 @@
57 57
58#include "indra_constants.h" 58#include "indra_constants.h"
59 59
60#include "llpreeditor.h"
61
60// culled from winuser.h 62// culled from winuser.h
61#ifndef WM_MOUSEWHEEL /* Added to be compatible with later SDK's */ 63#ifndef WM_MOUSEWHEEL /* Added to be compatible with later SDK's */
62const S32 WM_MOUSEWHEEL = 0x020A; 64const S32 WM_MOUSEWHEEL = 0x020A;
@@ -82,7 +84,6 @@ LLW32MsgCallback gAsyncMsgCallback = NULL;
82void show_window_creation_error(const char* title) 84void show_window_creation_error(const char* title)
83{ 85{
84 llwarns << title << llendl; 86 llwarns << title << llendl;
85 shell_open( "help/window_creation_error.html");
86} 87}
87 88
88//static 89//static
@@ -112,6 +113,7 @@ public:
112public: 113public:
113 // Wrappers for IMM API. 114 // Wrappers for IMM API.
114 static BOOL isIME(HKL hkl); 115 static BOOL isIME(HKL hkl);
116 static HWND getDefaultIMEWnd(HWND hwnd);
115 static HIMC getContext(HWND hwnd); 117 static HIMC getContext(HWND hwnd);
116 static BOOL releaseContext(HWND hwnd, HIMC himc); 118 static BOOL releaseContext(HWND hwnd, HIMC himc);
117 static BOOL getOpenStatus(HIMC himc); 119 static BOOL getOpenStatus(HIMC himc);
@@ -120,6 +122,11 @@ public:
120 static BOOL setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence); 122 static BOOL setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence);
121 static BOOL getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form); 123 static BOOL getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form);
122 static BOOL setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form); 124 static BOOL setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form);
125 static LONG getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length);
126 static BOOL setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength);
127 static BOOL setCompositionFont(HIMC himc, LPLOGFONTW logfont);
128 static BOOL setCandidateWindow(HIMC himc, LPCANDIDATEFORM candidate_form);
129 static BOOL notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value);
123 130
124private: 131private:
125 LLWinImm(); 132 LLWinImm();
@@ -128,6 +135,7 @@ private:
128private: 135private:
129 // Pointers to IMM API. 136 // Pointers to IMM API.
130 BOOL (WINAPI *mImmIsIME)(HKL); 137 BOOL (WINAPI *mImmIsIME)(HKL);
138 HWND (WINAPI *mImmGetDefaultIMEWnd)(HWND);
131 HIMC (WINAPI *mImmGetContext)(HWND); 139 HIMC (WINAPI *mImmGetContext)(HWND);
132 BOOL (WINAPI *mImmReleaseContext)(HWND, HIMC); 140 BOOL (WINAPI *mImmReleaseContext)(HWND, HIMC);
133 BOOL (WINAPI *mImmGetOpenStatus)(HIMC); 141 BOOL (WINAPI *mImmGetOpenStatus)(HIMC);
@@ -136,6 +144,11 @@ private:
136 BOOL (WINAPI *mImmSetConversionStatus)(HIMC, DWORD, DWORD); 144 BOOL (WINAPI *mImmSetConversionStatus)(HIMC, DWORD, DWORD);
137 BOOL (WINAPI *mImmGetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM); 145 BOOL (WINAPI *mImmGetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM);
138 BOOL (WINAPI *mImmSetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM); 146 BOOL (WINAPI *mImmSetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM);
147 LONG (WINAPI *mImmGetCompositionString)(HIMC, DWORD, LPVOID, DWORD);
148 BOOL (WINAPI *mImmSetCompositionString)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD);
149 BOOL (WINAPI *mImmSetCompositionFont)(HIMC, LPLOGFONTW);
150 BOOL (WINAPI *mImmSetCandidateWindow)(HIMC, LPCANDIDATEFORM);
151 BOOL (WINAPI *mImmNotifyIME)(HIMC, DWORD, DWORD, DWORD);
139 152
140private: 153private:
141 HMODULE mHImmDll; 154 HMODULE mHImmDll;
@@ -155,6 +168,7 @@ LLWinImm::LLWinImm() : mHImmDll(NULL)
155 if (mHImmDll != NULL) 168 if (mHImmDll != NULL)
156 { 169 {
157 mImmIsIME = (BOOL (WINAPI *)(HKL)) GetProcAddress(mHImmDll, "ImmIsIME"); 170 mImmIsIME = (BOOL (WINAPI *)(HKL)) GetProcAddress(mHImmDll, "ImmIsIME");
171 mImmGetDefaultIMEWnd = (HWND (WINAPI *)(HWND)) GetProcAddress(mHImmDll, "ImmGetDefaultIMEWnd");
158 mImmGetContext = (HIMC (WINAPI *)(HWND)) GetProcAddress(mHImmDll, "ImmGetContext"); 172 mImmGetContext = (HIMC (WINAPI *)(HWND)) GetProcAddress(mHImmDll, "ImmGetContext");
159 mImmReleaseContext = (BOOL (WINAPI *)(HWND, HIMC)) GetProcAddress(mHImmDll, "ImmReleaseContext"); 173 mImmReleaseContext = (BOOL (WINAPI *)(HWND, HIMC)) GetProcAddress(mHImmDll, "ImmReleaseContext");
160 mImmGetOpenStatus = (BOOL (WINAPI *)(HIMC)) GetProcAddress(mHImmDll, "ImmGetOpenStatus"); 174 mImmGetOpenStatus = (BOOL (WINAPI *)(HIMC)) GetProcAddress(mHImmDll, "ImmGetOpenStatus");
@@ -163,8 +177,14 @@ LLWinImm::LLWinImm() : mHImmDll(NULL)
163 mImmSetConversionStatus = (BOOL (WINAPI *)(HIMC, DWORD, DWORD)) GetProcAddress(mHImmDll, "ImmSetConversionStatus"); 177 mImmSetConversionStatus = (BOOL (WINAPI *)(HIMC, DWORD, DWORD)) GetProcAddress(mHImmDll, "ImmSetConversionStatus");
164 mImmGetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM)) GetProcAddress(mHImmDll, "ImmGetCompositionWindow"); 178 mImmGetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM)) GetProcAddress(mHImmDll, "ImmGetCompositionWindow");
165 mImmSetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM)) GetProcAddress(mHImmDll, "ImmSetCompositionWindow"); 179 mImmSetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM)) GetProcAddress(mHImmDll, "ImmSetCompositionWindow");
180 mImmGetCompositionString= (LONG (WINAPI *)(HIMC, DWORD, LPVOID, DWORD)) GetProcAddress(mHImmDll, "ImmGetCompositionStringW");
181 mImmSetCompositionString= (BOOL (WINAPI *)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD)) GetProcAddress(mHImmDll, "ImmSetCompositionStringW");
182 mImmSetCompositionFont = (BOOL (WINAPI *)(HIMC, LPLOGFONTW)) GetProcAddress(mHImmDll, "ImmSetCompositionFontW");
183 mImmSetCandidateWindow = (BOOL (WINAPI *)(HIMC, LPCANDIDATEFORM)) GetProcAddress(mHImmDll, "ImmSetCandidateWindow");
184 mImmNotifyIME = (BOOL (WINAPI *)(HIMC, DWORD, DWORD, DWORD)) GetProcAddress(mHImmDll, "ImmNotifyIME");
166 185
167 if (mImmIsIME == NULL || 186 if (mImmIsIME == NULL ||
187 mImmGetDefaultIMEWnd == NULL ||
168 mImmGetContext == NULL || 188 mImmGetContext == NULL ||
169 mImmReleaseContext == NULL || 189 mImmReleaseContext == NULL ||
170 mImmGetOpenStatus == NULL || 190 mImmGetOpenStatus == NULL ||
@@ -172,7 +192,12 @@ LLWinImm::LLWinImm() : mHImmDll(NULL)
172 mImmGetConversionStatus == NULL || 192 mImmGetConversionStatus == NULL ||
173 mImmSetConversionStatus == NULL || 193 mImmSetConversionStatus == NULL ||
174 mImmGetCompostitionWindow == NULL || 194 mImmGetCompostitionWindow == NULL ||
175 mImmSetCompostitionWindow == NULL) 195 mImmSetCompostitionWindow == NULL ||
196 mImmGetCompositionString == NULL ||
197 mImmSetCompositionString == NULL ||
198 mImmSetCompositionFont == NULL ||
199 mImmSetCandidateWindow == NULL ||
200 mImmNotifyIME == NULL)
176 { 201 {
177 // If any of the above API entires are not found, we can't use IMM API. 202 // If any of the above API entires are not found, we can't use IMM API.
178 // So, turn off the IMM support. We should log some warning message in 203 // So, turn off the IMM support. We should log some warning message in
@@ -186,6 +211,7 @@ LLWinImm::LLWinImm() : mHImmDll(NULL)
186 211
187 // If we unload the library, make sure all the function pointers are cleared 212 // If we unload the library, make sure all the function pointers are cleared
188 mImmIsIME = NULL; 213 mImmIsIME = NULL;
214 mImmGetDefaultIMEWnd = NULL;
189 mImmGetContext = NULL; 215 mImmGetContext = NULL;
190 mImmReleaseContext = NULL; 216 mImmReleaseContext = NULL;
191 mImmGetOpenStatus = NULL; 217 mImmGetOpenStatus = NULL;
@@ -194,6 +220,11 @@ LLWinImm::LLWinImm() : mHImmDll(NULL)
194 mImmSetConversionStatus = NULL; 220 mImmSetConversionStatus = NULL;
195 mImmGetCompostitionWindow = NULL; 221 mImmGetCompostitionWindow = NULL;
196 mImmSetCompostitionWindow = NULL; 222 mImmSetCompostitionWindow = NULL;
223 mImmGetCompositionString = NULL;
224 mImmSetCompositionString = NULL;
225 mImmSetCompositionFont = NULL;
226 mImmSetCandidateWindow = NULL;
227 mImmNotifyIME = NULL;
197 } 228 }
198 } 229 }
199} 230}
@@ -272,6 +303,50 @@ BOOL LLWinImm::setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)
272} 303}
273 304
274 305
306// static
307LONG LLWinImm::getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length)
308{
309 if ( sTheInstance.mImmGetCompositionString )
310 return sTheInstance.mImmGetCompositionString(himc, index, data, length);
311 return FALSE;
312}
313
314
315// static
316BOOL LLWinImm::setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength)
317{
318 if ( sTheInstance.mImmSetCompositionString )
319 return sTheInstance.mImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength);
320 return FALSE;
321}
322
323// static
324BOOL LLWinImm::setCompositionFont(HIMC himc, LPLOGFONTW pFont)
325{
326 if ( sTheInstance.mImmSetCompositionFont )
327 return sTheInstance.mImmSetCompositionFont(himc, pFont);
328 return FALSE;
329}
330
331// static
332BOOL LLWinImm::setCandidateWindow(HIMC himc, LPCANDIDATEFORM form)
333{
334 if ( sTheInstance.mImmSetCandidateWindow )
335 return sTheInstance.mImmSetCandidateWindow(himc, form);
336 return FALSE;
337}
338
339// static
340BOOL LLWinImm::notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value)
341{
342 if ( sTheInstance.mImmNotifyIME )
343 return sTheInstance.mImmNotifyIME(himc, action, index, value);
344 return FALSE;
345}
346
347
348
349
275// ---------------------------------------------------------------------------------------- 350// ----------------------------------------------------------------------------------------
276LLWinImm::~LLWinImm() 351LLWinImm::~LLWinImm()
277{ 352{
@@ -304,13 +379,14 @@ LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width,
304 mNativeAspectRatio = 0.f; 379 mNativeAspectRatio = 0.f;
305 mMousePositionModified = FALSE; 380 mMousePositionModified = FALSE;
306 mInputProcessingPaused = FALSE; 381 mInputProcessingPaused = FALSE;
382 mPreeditor = NULL;
307 383
308 // Initialize the keyboard 384 // Initialize the keyboard
309 gKeyboard = new LLKeyboardWin32(); 385 gKeyboard = new LLKeyboardWin32();
310 386
311 // Initialize (boot strap) the Language text input management, 387 // Initialize (boot strap) the Language text input management,
312 // based on the system's (user's) default settings. 388 // based on the system's (user's) default settings.
313 allowLanguageTextInput(FALSE); 389 allowLanguageTextInput(mPreeditor, FALSE);
314 390
315 GLuint pixel_format; 391 GLuint pixel_format;
316 WNDCLASS wc; 392 WNDCLASS wc;
@@ -938,6 +1014,10 @@ LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width,
938 initCursors(); 1014 initCursors();
939 setCursor( UI_CURSOR_ARROW ); 1015 setCursor( UI_CURSOR_ARROW );
940 1016
1017 // Initialize (boot strap) the Language text input management,
1018 // based on the system's (or user's) default settings.
1019 allowLanguageTextInput(NULL, FALSE);
1020
941 // Direct Input 1021 // Direct Input
942 HRESULT hr; 1022 HRESULT hr;
943 1023
@@ -2035,6 +2115,11 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
2035 2115
2036 BOOL minimized = BOOL(HIWORD(w_param)); 2116 BOOL minimized = BOOL(HIWORD(w_param));
2037 2117
2118 if (!activating && LLWinImm::isAvailable() && window_imp->mPreeditor)
2119 {
2120 window_imp->interruptLanguageTextInput();
2121 }
2122
2038 // JC - I'm not sure why, but if we don't report that we handled the 2123 // JC - I'm not sure why, but if we don't report that we handled the
2039 // WM_ACTIVATE message, the WM_ACTIVATEAPP messages don't work 2124 // WM_ACTIVATE message, the WM_ACTIVATEAPP messages don't work
2040 // properly when we run fullscreen. 2125 // properly when we run fullscreen.
@@ -2127,6 +2212,47 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
2127 // pass on to windows 2212 // pass on to windows
2128 break; 2213 break;
2129 2214
2215 case WM_IME_SETCONTEXT:
2216 if (LLWinImm::isAvailable() && window_imp->mPreeditor)
2217 {
2218 l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW;
2219 // Invoke DefWinProc with the modified LPARAM.
2220 }
2221 break;
2222
2223 case WM_IME_STARTCOMPOSITION:
2224 if (LLWinImm::isAvailable() && window_imp->mPreeditor)
2225 {
2226 window_imp->handleStartCompositionMessage();
2227 return 0;
2228 }
2229 break;
2230
2231 case WM_IME_ENDCOMPOSITION:
2232 if (LLWinImm::isAvailable() && window_imp->mPreeditor)
2233 {
2234 return 0;
2235 }
2236 break;
2237
2238 case WM_IME_COMPOSITION:
2239 if (LLWinImm::isAvailable() && window_imp->mPreeditor)
2240 {
2241 window_imp->handleCompositionMessage(l_param);
2242 return 0;
2243 }
2244 break;
2245
2246 case WM_IME_REQUEST:
2247 if (LLWinImm::isAvailable() && window_imp->mPreeditor)
2248 {
2249 LRESULT result = 0;
2250 if (window_imp->handleImeRequests(w_param, l_param, &result))
2251 {
2252 return result;
2253 }
2254 }
2255 break;
2130 2256
2131 case WM_CHAR: 2257 case WM_CHAR:
2132 // Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need 2258 // Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need
@@ -2154,6 +2280,11 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
2154 2280
2155 case WM_LBUTTONDOWN: 2281 case WM_LBUTTONDOWN:
2156 { 2282 {
2283 if (LLWinImm::isAvailable() && window_imp->mPreeditor)
2284 {
2285 window_imp->interruptLanguageTextInput();
2286 }
2287
2157 // Because we move the cursor position in the app, we need to query 2288 // Because we move the cursor position in the app, we need to query
2158 // to find out where the cursor at the time the event is handled. 2289 // to find out where the cursor at the time the event is handled.
2159 // If we don't do this, many clicks could get buffered up, and if the 2290 // If we don't do this, many clicks could get buffered up, and if the
@@ -2236,6 +2367,11 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
2236 case WM_RBUTTONDBLCLK: 2367 case WM_RBUTTONDBLCLK:
2237 case WM_RBUTTONDOWN: 2368 case WM_RBUTTONDOWN:
2238 { 2369 {
2370 if (LLWinImm::isAvailable() && window_imp->mPreeditor)
2371 {
2372 window_imp->interruptLanguageTextInput();
2373 }
2374
2239 // Because we move the cursor position in tllviewerhe app, we need to query 2375 // Because we move the cursor position in tllviewerhe app, we need to query
2240 // to find out where the cursor at the time the event is handled. 2376 // to find out where the cursor at the time the event is handled.
2241 // If we don't do this, many clicks could get buffered up, and if the 2377 // If we don't do this, many clicks could get buffered up, and if the
@@ -2287,6 +2423,11 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
2287 case WM_MBUTTONDOWN: 2423 case WM_MBUTTONDOWN:
2288// case WM_MBUTTONDBLCLK: 2424// case WM_MBUTTONDBLCLK:
2289 { 2425 {
2426 if (LLWinImm::isAvailable() && window_imp->mPreeditor)
2427 {
2428 window_imp->interruptLanguageTextInput();
2429 }
2430
2290 // Because we move the cursor position in tllviewerhe app, we need to query 2431 // Because we move the cursor position in tllviewerhe app, we need to query
2291 // to find out where the cursor at the time the event is handled. 2432 // to find out where the cursor at the time the event is handled.
2292 // If we don't do this, many clicks could get buffered up, and if the 2433 // If we don't do this, many clicks could get buffered up, and if the
@@ -3256,24 +3397,6 @@ void spawn_web_browser(const char* escaped_url )
3256 } 3397 }
3257} 3398}
3258 3399
3259void shell_open( const char* file_path )
3260{
3261 llinfos << "Opening " << file_path << llendl;
3262
3263 WCHAR wstr[1024];
3264 mbstowcs(wstr, file_path, 1024);
3265
3266 HWND our_window = NULL;
3267 int retval = (int) ShellExecute(our_window, L"open", wstr, NULL, NULL, SW_SHOWNORMAL); /* Flawfinder: ignore */
3268 if (retval > 32)
3269 {
3270 llinfos << "ShellExecute success with " << retval << llendl;
3271 }
3272 else
3273 {
3274 llinfos << "ShellExecute failure with " << retval << llendl;
3275 }
3276}
3277 3400
3278BOOL LLWindowWin32::dialog_color_picker ( F32 *r, F32 *g, F32 *b ) 3401BOOL LLWindowWin32::dialog_color_picker ( F32 *r, F32 *g, F32 *b )
3279{ 3402{
@@ -3324,15 +3447,37 @@ void LLWindowWin32::focusClient()
3324 SetFocus ( mWindowHandle ); 3447 SetFocus ( mWindowHandle );
3325} 3448}
3326 3449
3327void LLWindowWin32::allowLanguageTextInput(BOOL b) 3450void LLWindowWin32::allowLanguageTextInput(LLPreeditor *preeditor, BOOL b)
3328{ 3451{
3329 if (b == sLanguageTextInputAllowed || !LLWinImm::isAvailable()) 3452 if (b == sLanguageTextInputAllowed || !LLWinImm::isAvailable())
3330 { 3453 {
3331 return; 3454 return;
3332 } 3455 }
3456
3457 if (preeditor != mPreeditor && !b)
3458 {
3459 // This condition may occur with a call to
3460 // setEnabled(BOOL) from LLTextEditor or LLLineEditor
3461 // when the control is not focused.
3462 // We need to silently ignore the case so that
3463 // the language input status of the focused control
3464 // is not disturbed.
3465 return;
3466 }
3467
3468 // Take care of old and new preeditors.
3469 if (preeditor != mPreeditor || !b)
3470 {
3471 if (sLanguageTextInputAllowed)
3472 {
3473 interruptLanguageTextInput();
3474 }
3475 mPreeditor = (b ? preeditor : NULL);
3476 }
3477
3333 sLanguageTextInputAllowed = b; 3478 sLanguageTextInputAllowed = b;
3334 3479
3335 if (b) 3480 if ( sLanguageTextInputAllowed )
3336 { 3481 {
3337 // Allowing: Restore the previous IME status, so that the user has a feeling that the previous 3482 // Allowing: Restore the previous IME status, so that the user has a feeling that the previous
3338 // text input continues naturally. Be careful, however, the IME status is meaningful only during the user keeps 3483 // text input continues naturally. Be careful, however, the IME status is meaningful only during the user keeps
@@ -3368,7 +3513,24 @@ void LLWindowWin32::allowLanguageTextInput(BOOL b)
3368 LLWinImm::releaseContext(mWindowHandle, himc); 3513 LLWinImm::releaseContext(mWindowHandle, himc);
3369 } 3514 }
3370 } 3515 }
3516}
3517
3518void LLWindowWin32::fillCandidateForm(const LLCoordGL& caret, const LLRect& bounds,
3519 CANDIDATEFORM *form)
3520{
3521 LLCoordWindow caret_coord, top_left, bottom_right;
3522 convertCoords(caret, &caret_coord);
3523 convertCoords(LLCoordGL(bounds.mLeft, bounds.mTop), &top_left);
3524 convertCoords(LLCoordGL(bounds.mRight, bounds.mBottom), &bottom_right);
3371 3525
3526 memset(form, 0, sizeof(CANDIDATEFORM));
3527 form->dwStyle = CFS_EXCLUDE;
3528 form->ptCurrentPos.x = caret_coord.mX;
3529 form->ptCurrentPos.y = caret_coord.mY;
3530 form->rcArea.left = top_left.mX;
3531 form->rcArea.top = top_left.mY;
3532 form->rcArea.right = bottom_right.mX;
3533 form->rcArea.bottom = bottom_right.mY;
3372} 3534}
3373 3535
3374 3536
@@ -3400,4 +3562,455 @@ void LLWindowWin32::setLanguageTextInput( const LLCoordGL & position )
3400 } 3562 }
3401} 3563}
3402 3564
3565
3566void LLWindowWin32::fillCharPosition(const LLCoordGL& caret, const LLRect& bounds, const LLRect& control,
3567 IMECHARPOSITION *char_position)
3568{
3569 LLCoordScreen caret_coord, top_left, bottom_right;
3570 convertCoords(caret, &caret_coord);
3571 convertCoords(LLCoordGL(bounds.mLeft, bounds.mTop), &top_left);
3572 convertCoords(LLCoordGL(bounds.mRight, bounds.mBottom), &bottom_right);
3573
3574 char_position->pt.x = caret_coord.mX;
3575 char_position->pt.y = top_left.mY; // Windows wants the coordinate of upper left corner of a character...
3576 char_position->cLineHeight = bottom_right.mY - top_left.mY;
3577 char_position->rcDocument.left = top_left.mX;
3578 char_position->rcDocument.top = top_left.mY;
3579 char_position->rcDocument.right = bottom_right.mX;
3580 char_position->rcDocument.bottom = bottom_right.mY;
3581}
3582
3583void LLWindowWin32::fillCompositionLogfont(LOGFONT *logfont)
3584{
3585 // Our font is a list of FreeType recognized font files that may
3586 // not have a corresponding ones in Windows' fonts. Hence, we
3587 // can't simply tell Windows which font we are using. We will
3588 // notify a _standard_ font for a current input locale instead.
3589 // We use a hard-coded knowledge about the Windows' standard
3590 // configuration to do so...
3591
3592 memset(logfont, 0, sizeof(LOGFONT));
3593
3594 const WORD lang_id = LOWORD(GetKeyboardLayout(0));
3595 switch (PRIMARYLANGID(lang_id))
3596 {
3597 case LANG_CHINESE:
3598 // We need to identify one of two Chinese fonts.
3599 switch (SUBLANGID(lang_id))
3600 {
3601 case SUBLANG_CHINESE_SIMPLIFIED:
3602 case SUBLANG_CHINESE_SINGAPORE:
3603 logfont->lfCharSet = GB2312_CHARSET;
3604 lstrcpy(logfont->lfFaceName, TEXT("SimHei"));
3605 break;
3606 case SUBLANG_CHINESE_TRADITIONAL:
3607 case SUBLANG_CHINESE_HONGKONG:
3608 case SUBLANG_CHINESE_MACAU:
3609 default:
3610 logfont->lfCharSet = CHINESEBIG5_CHARSET;
3611 lstrcpy(logfont->lfFaceName, TEXT("MingLiU"));
3612 break;
3613 }
3614 break;
3615 case LANG_JAPANESE:
3616 logfont->lfCharSet = SHIFTJIS_CHARSET;
3617 lstrcpy(logfont->lfFaceName, TEXT("MS Gothic"));
3618 break;
3619 case LANG_KOREAN:
3620 logfont->lfCharSet = HANGUL_CHARSET;
3621 lstrcpy(logfont->lfFaceName, TEXT("Gulim"));
3622 break;
3623 default:
3624 logfont->lfCharSet = ANSI_CHARSET;
3625 lstrcpy(logfont->lfFaceName, TEXT("Tahoma"));
3626 break;
3627 }
3628
3629 logfont->lfHeight = mPreeditor->getPreeditFontSize();
3630 logfont->lfWeight = FW_NORMAL;
3631}
3632
3633U32 LLWindowWin32::fillReconvertString(const LLWString &text,
3634 S32 focus, S32 focus_length, RECONVERTSTRING *reconvert_string)
3635{
3636 const llutf16string text_utf16 = wstring_to_utf16str(text);
3637 const DWORD required_size = sizeof(RECONVERTSTRING) + (text_utf16.length() + 1) * sizeof(WCHAR);
3638 if (reconvert_string && reconvert_string->dwSize >= required_size)
3639 {
3640 const DWORD focus_utf16_at = wstring_utf16_length(text, 0, focus);
3641 const DWORD focus_utf16_length = wstring_utf16_length(text, focus, focus_length);
3642
3643 reconvert_string->dwVersion = 0;
3644 reconvert_string->dwStrLen = text_utf16.length();
3645 reconvert_string->dwStrOffset = sizeof(RECONVERTSTRING);
3646 reconvert_string->dwCompStrLen = focus_utf16_length;
3647 reconvert_string->dwCompStrOffset = focus_utf16_at * sizeof(WCHAR);
3648 reconvert_string->dwTargetStrLen = 0;
3649 reconvert_string->dwTargetStrOffset = focus_utf16_at * sizeof(WCHAR);
3650
3651 const LPWSTR text = (LPWSTR)((BYTE *)reconvert_string + sizeof(RECONVERTSTRING));
3652 memcpy(text, text_utf16.c_str(), (text_utf16.length() + 1) * sizeof(WCHAR));
3653 }
3654 return required_size;
3655}
3656
3657void LLWindowWin32::updateLanguageTextInputArea()
3658{
3659 if (!mPreeditor || !LLWinImm::isAvailable())
3660 {
3661 return;
3662 }
3663
3664 LLCoordGL caret_coord;
3665 LLRect preedit_bounds;
3666 if (mPreeditor->getPreeditLocation(-1, &caret_coord, &preedit_bounds, NULL))
3667 {
3668 mLanguageTextInputPointGL = caret_coord;
3669 mLanguageTextInputAreaGL = preedit_bounds;
3670
3671 CANDIDATEFORM candidate_form;
3672 fillCandidateForm(caret_coord, preedit_bounds, &candidate_form);
3673
3674 HIMC himc = LLWinImm::getContext(mWindowHandle);
3675 // Win32 document says there may be up to 4 candidate windows.
3676 // This magic number 4 appears only in the document, and
3677 // there are no constant/macro for the value...
3678 for (int i = 3; i >= 0; --i)
3679 {
3680 candidate_form.dwIndex = i;
3681 LLWinImm::setCandidateWindow(himc, &candidate_form);
3682 }
3683 LLWinImm::releaseContext(mWindowHandle, himc);
3684 }
3685}
3686
3687void LLWindowWin32::interruptLanguageTextInput()
3688{
3689 if (mPreeditor)
3690 {
3691 if (LLWinImm::isAvailable())
3692 {
3693 HIMC himc = LLWinImm::getContext(mWindowHandle);
3694 LLWinImm::notifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
3695 LLWinImm::releaseContext(mWindowHandle, himc);
3696 }
3697 mPreeditor->resetPreedit();
3698 }
3699}
3700
3701void LLWindowWin32::handleStartCompositionMessage()
3702{
3703 // Let IME know the font to use in feedback UI.
3704 LOGFONT logfont;
3705 fillCompositionLogfont(&logfont);
3706 HIMC himc = LLWinImm::getContext(mWindowHandle);
3707 LLWinImm::setCompositionFont(himc, &logfont);
3708 LLWinImm::releaseContext(mWindowHandle, himc);
3709}
3710
3711// Handle WM_IME_COMPOSITION message.
3712
3713void LLWindowWin32::handleCompositionMessage(const U32 indexes)
3714{
3715 BOOL needs_update = FALSE;
3716 LLWString result_string;
3717 LLWString preedit_string;
3718 S32 preedit_string_utf16_length = 0;
3719 LLPreeditor::segment_lengths_t preedit_segment_lengths;
3720 LLPreeditor::standouts_t preedit_standouts;
3721
3722 // Step I: Receive details of preedits from IME.
3723
3724 HIMC himc = LLWinImm::getContext(mWindowHandle);
3725
3726 if (indexes & GCS_RESULTSTR)
3727 {
3728 LONG size = LLWinImm::getCompositionString(himc, GCS_RESULTSTR, NULL, 0);
3729 if (size >= 0)
3730 {
3731 const LPWSTR data = new WCHAR[size / sizeof(WCHAR) + 1];
3732 size = LLWinImm::getCompositionString(himc, GCS_RESULTSTR, data, size);
3733 if (size > 0)
3734 {
3735 result_string = utf16str_to_wstring(llutf16string(data, size / sizeof(WCHAR)));
3736 }
3737 delete[] data;
3738 needs_update = TRUE;
3739 }
3740 }
3741
3742 if (indexes & GCS_COMPSTR)
3743 {
3744 LONG size = LLWinImm::getCompositionString(himc, GCS_COMPSTR, NULL, 0);
3745 if (size >= 0)
3746 {
3747 const LPWSTR data = new WCHAR[size / sizeof(WCHAR) + 1];
3748 size = LLWinImm::getCompositionString(himc, GCS_COMPSTR, data, size);
3749 if (size > 0)
3750 {
3751 preedit_string_utf16_length = size / sizeof(WCHAR);
3752 preedit_string = utf16str_to_wstring(llutf16string(data, size / sizeof(WCHAR)));
3753 }
3754 delete[] data;
3755 needs_update = TRUE;
3756 }
3757 }
3758
3759 if ((indexes & GCS_COMPCLAUSE) && preedit_string.length() > 0)
3760 {
3761 LONG size = LLWinImm::getCompositionString(himc, GCS_COMPCLAUSE, NULL, 0);
3762 if (size > 0)
3763 {
3764 const LPDWORD data = new DWORD[size / sizeof(DWORD)];
3765 size = LLWinImm::getCompositionString(himc, GCS_COMPCLAUSE, data, size);
3766 if (size >= sizeof(DWORD) * 2
3767 && data[0] == 0 && data[size / sizeof(DWORD) - 1] == preedit_string_utf16_length)
3768 {
3769 preedit_segment_lengths.resize(size / sizeof(DWORD) - 1);
3770 S32 offset = 0;
3771 for (U32 i = 0; i < preedit_segment_lengths.size(); i++)
3772 {
3773 const S32 length = wstring_wstring_length_from_utf16_length(preedit_string, offset, data[i + 1] - data[i]);
3774 preedit_segment_lengths[i] = length;
3775 offset += length;
3776 }
3777 }
3778 delete[] data;
3779 }
3780 }
3781
3782 if ((indexes & GCS_COMPATTR) && preedit_segment_lengths.size() > 1)
3783 {
3784 LONG size = LLWinImm::getCompositionString(himc, GCS_COMPATTR, NULL, 0);
3785 if (size > 0)
3786 {
3787 const LPBYTE data = new BYTE[size / sizeof(BYTE)];
3788 size = LLWinImm::getCompositionString(himc, GCS_COMPATTR, data, size);
3789 if (size == preedit_string_utf16_length)
3790 {
3791 preedit_standouts.assign(preedit_segment_lengths.size(), FALSE);
3792 S32 offset = 0;
3793 for (U32 i = 0; i < preedit_segment_lengths.size(); i++)
3794 {
3795 if (ATTR_TARGET_CONVERTED == data[offset] || ATTR_TARGET_NOTCONVERTED == data[offset])
3796 {
3797 preedit_standouts[i] = TRUE;
3798 }
3799 offset += wstring_utf16_length(preedit_string, offset, preedit_segment_lengths[i]);
3800 }
3801 }
3802 delete[] data;
3803 }
3804 }
3805
3806 S32 caret_position = preedit_string.length();
3807 if (indexes & GCS_CURSORPOS)
3808 {
3809 const S32 caret_position_utf16 = LLWinImm::getCompositionString(himc, GCS_CURSORPOS, NULL, 0);
3810 if (caret_position_utf16 >= 0 && caret_position <= preedit_string_utf16_length)
3811 {
3812 caret_position = wstring_wstring_length_from_utf16_length(preedit_string, 0, caret_position_utf16);
3813 }
3814 }
3815
3816 if (indexes == 0)
3817 {
3818 // I'm not sure this condition really happens, but
3819 // Windows SDK document says it is an indication
3820 // of "reset everything."
3821 needs_update = TRUE;
3822 }
3823
3824 LLWinImm::releaseContext(mWindowHandle, himc);
3825
3826 // Step II: Update the active preeditor.
3827
3828 if (needs_update)
3829 {
3830 mPreeditor->resetPreedit();
3831
3832 if (result_string.length() > 0)
3833 {
3834 for (LLWString::const_iterator i = result_string.begin(); i != result_string.end(); i++)
3835 {
3836 mPreeditor->handleUnicodeCharHere(*i, FALSE);
3837 }
3838 }
3839
3840 if (preedit_string.length() > 0)
3841 {
3842 if (preedit_segment_lengths.size() == 0)
3843 {
3844 preedit_segment_lengths.assign(1, preedit_string.length());
3845 }
3846 if (preedit_standouts.size() == 0)
3847 {
3848 preedit_standouts.assign(preedit_segment_lengths.size(), FALSE);
3849 }
3850 mPreeditor->updatePreedit(preedit_string, preedit_segment_lengths, preedit_standouts, caret_position);
3851 }
3852
3853 // Some IME doesn't query char position after WM_IME_COMPOSITION,
3854 // so we need to update them actively.
3855 updateLanguageTextInputArea();
3856 }
3857}
3858
3859// Given a text and a focus range, find_context finds and returns a
3860// surrounding context of the focused subtext. A variable pointed
3861// to by offset receives the offset in llwchars of the beginning of
3862// the returned context string in the given wtext.
3863
3864static LLWString find_context(const LLWString & wtext, S32 focus, S32 focus_length, S32 *offset)
3865{
3866 static const S32 CONTEXT_EXCESS = 30; // This value is by experiences.
3867
3868 const S32 e = llmin((S32) wtext.length(), focus + focus_length + CONTEXT_EXCESS);
3869 S32 end = focus + focus_length;
3870 while (end < e && '\n' != wtext[end])
3871 {
3872 end++;
3873 }
3874
3875 const S32 s = llmax(0, focus - CONTEXT_EXCESS);
3876 S32 start = focus;
3877 while (start > s && '\n' != wtext[start - 1])
3878 {
3879 --start;
3880 }
3881
3882 *offset = start;
3883 return wtext.substr(start, end - start);
3884}
3885
3886// Handle WM_IME_REQUEST message.
3887// If it handled the message, returns TRUE. Otherwise, FALSE.
3888// When it handled the message, the value to be returned from
3889// the Window Procedure is set to *result.
3890
3891BOOL LLWindowWin32::handleImeRequests(U32 request, U32 param, LRESULT *result)
3892{
3893 if ( mPreeditor )
3894 {
3895 switch (request)
3896 {
3897 case IMR_CANDIDATEWINDOW: // http://msdn2.microsoft.com/en-us/library/ms776080.aspx
3898 {
3899 LLCoordGL caret_coord;
3900 LLRect preedit_bounds;
3901 mPreeditor->getPreeditLocation(-1, &caret_coord, &preedit_bounds, NULL);
3902
3903 CANDIDATEFORM *const form = (CANDIDATEFORM *)param;
3904 DWORD const dwIndex = form->dwIndex;
3905 fillCandidateForm(caret_coord, preedit_bounds, form);
3906 form->dwIndex = dwIndex;
3907
3908 *result = 1;
3909 return TRUE;
3910 }
3911 case IMR_QUERYCHARPOSITION:
3912 {
3913 IMECHARPOSITION *const char_position = (IMECHARPOSITION *)param;
3914
3915 // char_position->dwCharPos counts in number of
3916 // WCHARs, i.e., UTF-16 encoding units, so we can't simply pass the
3917 // number to getPreeditLocation.
3918
3919 const LLWString & wtext = mPreeditor->getWText();
3920 S32 preedit, preedit_length;
3921 mPreeditor->getPreeditRange(&preedit, &preedit_length);
3922 LLCoordGL caret_coord;
3923 LLRect preedit_bounds, text_control;
3924 const S32 position = wstring_wstring_length_from_utf16_length(wtext, preedit, char_position->dwCharPos);
3925
3926 if (!mPreeditor->getPreeditLocation(position, &caret_coord, &preedit_bounds, &text_control))
3927 {
3928 llwarns << "*** IMR_QUERYCHARPOSITON called but getPreeditLocation failed." << llendl;
3929 return FALSE;
3930 }
3931 fillCharPosition(caret_coord, preedit_bounds, text_control, char_position);
3932
3933 *result = 1;
3934 return TRUE;
3935 }
3936 case IMR_COMPOSITIONFONT:
3937 {
3938 fillCompositionLogfont((LOGFONT *)param);
3939
3940 *result = 1;
3941 return TRUE;
3942 }
3943 case IMR_RECONVERTSTRING:
3944 {
3945 mPreeditor->resetPreedit();
3946 const LLWString & wtext = mPreeditor->getWText();
3947 S32 select, select_length;
3948 mPreeditor->getSelectionRange(&select, &select_length);
3949
3950 S32 context_offset;
3951 const LLWString context = find_context(wtext, select, select_length, &context_offset);
3952
3953 RECONVERTSTRING * const reconvert_string = (RECONVERTSTRING *)param;
3954 const U32 size = fillReconvertString(context, select - context_offset, select_length, reconvert_string);
3955 if (reconvert_string)
3956 {
3957 if (select_length == 0)
3958 {
3959 // Let the IME to decide the reconversion range, and
3960 // adjust the reconvert_string structure accordingly.
3961 HIMC himc = LLWinImm::getContext(mWindowHandle);
3962 const BOOL adjusted = LLWinImm::setCompositionString(himc,
3963 SCS_QUERYRECONVERTSTRING, reconvert_string, size, NULL, 0);
3964 LLWinImm::releaseContext(mWindowHandle, himc);
3965 if (adjusted)
3966 {
3967 const llutf16string & text_utf16 = wstring_to_utf16str(context);
3968 const S32 new_preedit_start = reconvert_string->dwCompStrOffset / sizeof(WCHAR);
3969 const S32 new_preedit_end = new_preedit_start + reconvert_string->dwCompStrLen;
3970 select = utf16str_wstring_length(text_utf16, new_preedit_start);
3971 select_length = utf16str_wstring_length(text_utf16, new_preedit_end) - select;
3972 select += context_offset;
3973 }
3974 }
3975 mPreeditor->markAsPreedit(select, select_length);
3976 }
3977
3978 *result = size;
3979 return TRUE;
3980 }
3981 case IMR_CONFIRMRECONVERTSTRING:
3982 {
3983 *result = FALSE;
3984 return TRUE;
3985 }
3986 case IMR_DOCUMENTFEED:
3987 {
3988 const LLWString & wtext = mPreeditor->getWText();
3989 S32 preedit, preedit_length;
3990 mPreeditor->getPreeditRange(&preedit, &preedit_length);
3991
3992 S32 context_offset;
3993 LLWString context = find_context(wtext, preedit, preedit_length, &context_offset);
3994 preedit -= context_offset;
3995 if (preedit_length)
3996 {
3997 // IMR_DOCUMENTFEED may be called when we have an active preedit.
3998 // We should pass the context string *excluding* the preedit string.
3999 // Otherwise, some IME are confused.
4000 context.erase(preedit, preedit_length);
4001 }
4002
4003 RECONVERTSTRING *reconvert_string = (RECONVERTSTRING *)param;
4004 *result = fillReconvertString(context, preedit, 0, reconvert_string);
4005 return TRUE;
4006 }
4007 default:
4008 return FALSE;
4009 }
4010 }
4011
4012 return FALSE;
4013}
4014
4015
3403#endif // LL_WINDOWS 4016#endif // LL_WINDOWS