| 1 |
/******************************************************************************* |
|---|
| 2 |
* Copyright (c) 2007, 2008 IBM Corporation and others. |
|---|
| 3 |
* All rights reserved. This program and the accompanying materials |
|---|
| 4 |
* are made available under the terms of the Eclipse Public License v1.0 |
|---|
| 5 |
* which accompanies this distribution, and is available at |
|---|
| 6 |
* http://www.eclipse.org/legal/epl-v10.html |
|---|
| 7 |
* |
|---|
| 8 |
* Contributors: |
|---|
| 9 |
* IBM Corporation - initial API and implementation |
|---|
| 10 |
*******************************************************************************/ |
|---|
| 11 |
module dwt.widgets.IME; |
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
import dwt.DWT; |
|---|
| 15 |
import dwt.DWTException; |
|---|
| 16 |
import dwt.graphics.Color; |
|---|
| 17 |
import dwt.graphics.TextStyle; |
|---|
| 18 |
import dwt.internal.win32.OS; |
|---|
| 19 |
import dwt.internal.win32.WINTYPES; |
|---|
| 20 |
|
|---|
| 21 |
import dwt.widgets.Widget; |
|---|
| 22 |
import dwt.widgets.Canvas; |
|---|
| 23 |
import dwt.widgets.Event; |
|---|
| 24 |
import dwt.widgets.Display; |
|---|
| 25 |
|
|---|
| 26 |
import dwt.dwthelper.utils; |
|---|
| 27 |
|
|---|
| 28 |
/** |
|---|
| 29 |
* Instances of this class represent input method editors. |
|---|
| 30 |
* These are typically in-line pre-edit text areas that allow |
|---|
| 31 |
* the user to compose characters from Far Eastern languages |
|---|
| 32 |
* such as Japanese, Chinese or Korean. |
|---|
| 33 |
* |
|---|
| 34 |
* <dl> |
|---|
| 35 |
* <dt><b>Styles:</b></dt> |
|---|
| 36 |
* <dd>(none)</dd> |
|---|
| 37 |
* <dt><b>Events:</b></dt> |
|---|
| 38 |
* <dd>ImeComposition</dd> |
|---|
| 39 |
* </dl> |
|---|
| 40 |
* <p> |
|---|
| 41 |
* IMPORTANT: This class is <em>not</em> intended to be subclassed. |
|---|
| 42 |
* </p> |
|---|
| 43 |
* |
|---|
| 44 |
* @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> |
|---|
| 45 |
* |
|---|
| 46 |
* @since 3.4 |
|---|
| 47 |
*/ |
|---|
| 48 |
public class IME : Widget { |
|---|
| 49 |
Canvas parent; |
|---|
| 50 |
int caretOffset; |
|---|
| 51 |
int startOffset; |
|---|
| 52 |
int commitCount; |
|---|
| 53 |
String text; |
|---|
| 54 |
int [] ranges; |
|---|
| 55 |
TextStyle [] styles; |
|---|
| 56 |
|
|---|
| 57 |
static const int WM_MSIME_MOUSE; |
|---|
| 58 |
|
|---|
| 59 |
static byte [16] IID_ITfInputProcessorProfiles; |
|---|
| 60 |
static byte [16] IID_ITfDisplayAttributeProvider; |
|---|
| 61 |
static byte [16] CLSID_TF_InputProcessorProfiles; |
|---|
| 62 |
static byte [16] GUID_TFCAT_TIP_KEYBOARD; |
|---|
| 63 |
static this() { |
|---|
| 64 |
WM_MSIME_MOUSE = OS.RegisterWindowMessage (StrToTCHARz ("MSIMEMouseOperation")); |
|---|
| 65 |
|
|---|
| 66 |
OS.IIDFromString ("{1F02B6C5-7842-4EE6-8A0B-9A24183A95CA}\0"w.ptr, IID_ITfInputProcessorProfiles.ptr); |
|---|
| 67 |
OS.IIDFromString ("{fee47777-163c-4769-996a-6e9c50ad8f54}\0"w.ptr, IID_ITfDisplayAttributeProvider.ptr); |
|---|
| 68 |
OS.IIDFromString ("{33C53A50-F456-4884-B049-85FD643ECFED}\0"w.ptr, CLSID_TF_InputProcessorProfiles.ptr); |
|---|
| 69 |
OS.IIDFromString ("{34745C63-B2F0-4784-8B67-5E12C8701A31}\0"w.ptr, GUID_TFCAT_TIP_KEYBOARD.ptr); |
|---|
| 70 |
} |
|---|
| 71 |
|
|---|
| 72 |
/* TextLayout has a copy of these constants */ |
|---|
| 73 |
static const int UNDERLINE_IME_DOT = 1 << 16; |
|---|
| 74 |
static const int UNDERLINE_IME_DASH = 2 << 16; |
|---|
| 75 |
static const int UNDERLINE_IME_THICK = 3 << 16; |
|---|
| 76 |
|
|---|
| 77 |
/** |
|---|
| 78 |
* Prevents uninitialized instances from being created outside the package. |
|---|
| 79 |
*/ |
|---|
| 80 |
this () { |
|---|
| 81 |
} |
|---|
| 82 |
|
|---|
| 83 |
/** |
|---|
| 84 |
* Constructs a new instance of this class given its parent |
|---|
| 85 |
* and a style value describing its behavior and appearance. |
|---|
| 86 |
* <p> |
|---|
| 87 |
* The style value is either one of the style constants defined in |
|---|
| 88 |
* class <code>DWT</code> which is applicable to instances of this |
|---|
| 89 |
* class, or must be built by <em>bitwise OR</em>'ing together |
|---|
| 90 |
* (that is, using the <code>int</code> "|" operator) two or more |
|---|
| 91 |
* of those <code>DWT</code> style constants. The class description |
|---|
| 92 |
* lists the style constants that are applicable to the class. |
|---|
| 93 |
* Style bits are also inherited from superclasses. |
|---|
| 94 |
* </p> |
|---|
| 95 |
* |
|---|
| 96 |
* @param parent a canvas control which will be the parent of the new instance (cannot be null) |
|---|
| 97 |
* @param style the style of control to construct |
|---|
| 98 |
* |
|---|
| 99 |
* @exception IllegalArgumentException <ul> |
|---|
| 100 |
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
|---|
| 101 |
* </ul> |
|---|
| 102 |
* @exception DWTException <ul> |
|---|
| 103 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> |
|---|
| 104 |
* <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> |
|---|
| 105 |
* </ul> |
|---|
| 106 |
* |
|---|
| 107 |
* @see Widget#checkSubclass |
|---|
| 108 |
* @see Widget#getStyle |
|---|
| 109 |
*/ |
|---|
| 110 |
public this (Canvas parent, int style) { |
|---|
| 111 |
super (parent, style); |
|---|
| 112 |
this.parent = parent; |
|---|
| 113 |
createWidget (); |
|---|
| 114 |
} |
|---|
| 115 |
|
|---|
| 116 |
void createWidget () { |
|---|
| 117 |
text = ""; //$NON-NLS-1$ |
|---|
| 118 |
startOffset = -1; |
|---|
| 119 |
if (parent.getIME () is null) { |
|---|
| 120 |
parent.setIME (this); |
|---|
| 121 |
} |
|---|
| 122 |
} |
|---|
| 123 |
|
|---|
| 124 |
/** |
|---|
| 125 |
* Returns the offset of the caret from the start of the document. |
|---|
| 126 |
* The caret is within the current composition. |
|---|
| 127 |
* |
|---|
| 128 |
* @return the caret offset |
|---|
| 129 |
* |
|---|
| 130 |
* @exception DWTException <ul> |
|---|
| 131 |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
|---|
| 132 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
|---|
| 133 |
* </ul> |
|---|
| 134 |
*/ |
|---|
| 135 |
public int getCaretOffset () { |
|---|
| 136 |
checkWidget (); |
|---|
| 137 |
return startOffset + caretOffset; |
|---|
| 138 |
} |
|---|
| 139 |
|
|---|
| 140 |
/** |
|---|
| 141 |
* Returns the commit count of the composition. This is the |
|---|
| 142 |
* number of characters that have been composed. When the |
|---|
| 143 |
* commit count is equal to the length of the composition |
|---|
| 144 |
* text, then the in-line edit operation is complete. |
|---|
| 145 |
* |
|---|
| 146 |
* @return the commit count |
|---|
| 147 |
* |
|---|
| 148 |
* @exception DWTException <ul> |
|---|
| 149 |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
|---|
| 150 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
|---|
| 151 |
* </ul> |
|---|
| 152 |
* |
|---|
| 153 |
* @see IME#getText |
|---|
| 154 |
*/ |
|---|
| 155 |
public int getCommitCount () { |
|---|
| 156 |
checkWidget (); |
|---|
| 157 |
return commitCount; |
|---|
| 158 |
} |
|---|
| 159 |
|
|---|
| 160 |
/** |
|---|
| 161 |
* Returns the offset of the composition from the start of the document. |
|---|
| 162 |
* This is the start offset of the composition within the document and |
|---|
| 163 |
* in not changed by the input method editor itself during the in-line edit |
|---|
| 164 |
* session. |
|---|
| 165 |
* |
|---|
| 166 |
* @return the offset of the composition |
|---|
| 167 |
* |
|---|
| 168 |
* @exception DWTException <ul> |
|---|
| 169 |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
|---|
| 170 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
|---|
| 171 |
* </ul> |
|---|
| 172 |
*/ |
|---|
| 173 |
public int getCompositionOffset () { |
|---|
| 174 |
checkWidget (); |
|---|
| 175 |
return startOffset; |
|---|
| 176 |
} |
|---|
| 177 |
|
|---|
| 178 |
TF_DISPLAYATTRIBUTE* getDisplayAttribute (short langid, int attInfo) { |
|---|
| 179 |
void* pProfiles; |
|---|
| 180 |
int hr = OS.CoCreateInstance (CLSID_TF_InputProcessorProfiles.ptr, null, OS.CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles.ptr, &pProfiles); |
|---|
| 181 |
TF_DISPLAYATTRIBUTE* pda; |
|---|
| 182 |
if (hr is OS.S_OK) { |
|---|
| 183 |
byte [16] pclsid; |
|---|
| 184 |
byte [16] pguidProfile; |
|---|
| 185 |
/* pProfiles.GetDefaultLanguageProfile () */ |
|---|
| 186 |
hr = OS.VtblCall (8, pProfiles, cast(int)langid, cast(int)GUID_TFCAT_TIP_KEYBOARD.ptr, cast(int)pclsid.ptr, cast(int)pguidProfile.ptr); |
|---|
| 187 |
if (hr is OS.S_OK) { |
|---|
| 188 |
void* pProvider; |
|---|
| 189 |
hr = OS.CoCreateInstance (pclsid.ptr, null, OS.CLSCTX_INPROC_SERVER, IID_ITfDisplayAttributeProvider.ptr, &pProvider); |
|---|
| 190 |
if (hr is OS.S_OK) { |
|---|
| 191 |
void* pEnum; |
|---|
| 192 |
/* pProvider.EnumDisplayAttributeInfo () */ |
|---|
| 193 |
hr = OS.VtblCall (3, pProvider, cast(int)&pEnum); |
|---|
| 194 |
if (hr is OS.S_OK) { |
|---|
| 195 |
void* pDispInfo; |
|---|
| 196 |
TF_DISPLAYATTRIBUTE* tempPda = new TF_DISPLAYATTRIBUTE(); |
|---|
| 197 |
/* pEnum.Next () */ |
|---|
| 198 |
while ((hr = OS.VtblCall (4, pEnum, 1, cast(int) &pDispInfo, 0 )) is OS.S_OK) { |
|---|
| 199 |
/* pDispInfo.GetAttributeInfo(); */ |
|---|
| 200 |
OS.VtblCall (5, pDispInfo, cast(int)cast(void*)tempPda); |
|---|
| 201 |
/* pDispInfo.Release () */ |
|---|
| 202 |
OS.VtblCall (2, pDispInfo); |
|---|
| 203 |
if (tempPda.bAttr is attInfo) { |
|---|
| 204 |
pda = tempPda; |
|---|
| 205 |
break; |
|---|
| 206 |
} |
|---|
| 207 |
} |
|---|
| 208 |
/* pEnum.Release () */ |
|---|
| 209 |
hr = OS.VtblCall (2, pEnum); |
|---|
| 210 |
} |
|---|
| 211 |
/* pProvider.Release () */ |
|---|
| 212 |
hr = OS.VtblCall (2, pProvider); |
|---|
| 213 |
} |
|---|
| 214 |
} |
|---|
| 215 |
/* pProfiles.Release () */ |
|---|
| 216 |
hr = OS.VtblCall (2, pProfiles); |
|---|
| 217 |
} |
|---|
| 218 |
if (pda is null) { |
|---|
| 219 |
pda = new TF_DISPLAYATTRIBUTE (); |
|---|
| 220 |
switch (attInfo) { |
|---|
| 221 |
case OS.TF_ATTR_INPUT: |
|---|
| 222 |
pda.lsStyle = OS.TF_LS_SQUIGGLE; |
|---|
| 223 |
break; |
|---|
| 224 |
case OS.TF_ATTR_CONVERTED: |
|---|
| 225 |
case OS.TF_ATTR_TARGET_CONVERTED: |
|---|
| 226 |
pda.lsStyle = OS.TF_LS_SOLID; |
|---|
| 227 |
pda.fBoldLine = attInfo is OS.TF_ATTR_TARGET_CONVERTED; |
|---|
| 228 |
break; |
|---|
| 229 |
default: |
|---|
| 230 |
} |
|---|
| 231 |
} |
|---|
| 232 |
return pda; |
|---|
| 233 |
} |
|---|
| 234 |
|
|---|
| 235 |
/** |
|---|
| 236 |
* Returns the ranges for the style that should be applied during the |
|---|
| 237 |
* in-line edit session. |
|---|
| 238 |
* <p> |
|---|
| 239 |
* The ranges array contains start and end pairs. Each pair refers to |
|---|
| 240 |
* the corresponding style in the styles array. For example, the pair |
|---|
| 241 |
* that starts at ranges[n] and ends at ranges[n+1] uses the style |
|---|
| 242 |
* at styles[n/2] returned by <code>getStyles()</code>. |
|---|
| 243 |
* </p> |
|---|
| 244 |
* @return the ranges for the styles |
|---|
| 245 |
* |
|---|
| 246 |
* @exception DWTException <ul> |
|---|
| 247 |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
|---|
| 248 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
|---|
| 249 |
* </ul> |
|---|
| 250 |
* |
|---|
| 251 |
* @see IME#getStyles |
|---|
| 252 |
*/ |
|---|
| 253 |
public int [] getRanges () { |
|---|
| 254 |
checkWidget (); |
|---|
| 255 |
if (ranges is null) return new int [0]; |
|---|
| 256 |
int [] result = new int [ranges.length]; |
|---|
| 257 |
for (int i = 0; i < result.length; i++) { |
|---|
| 258 |
result [i] = ranges [i] + startOffset; |
|---|
| 259 |
} |
|---|
| 260 |
return result; |
|---|
| 261 |
} |
|---|
| 262 |
|
|---|
| 263 |
/** |
|---|
| 264 |
* Returns the styles for the ranges. |
|---|
| 265 |
* <p> |
|---|
| 266 |
* The ranges array contains start and end pairs. Each pair refers to |
|---|
| 267 |
* the corresponding style in the styles array. For example, the pair |
|---|
| 268 |
* that starts at ranges[n] and ends at ranges[n+1] uses the style |
|---|
| 269 |
* at styles[n/2]. |
|---|
| 270 |
* </p> |
|---|
| 271 |
* |
|---|
| 272 |
* @return the ranges for the styles |
|---|
| 273 |
* |
|---|
| 274 |
* @exception DWTException <ul> |
|---|
| 275 |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
|---|
| 276 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
|---|
| 277 |
* </ul> |
|---|
| 278 |
* |
|---|
| 279 |
* @see IME#getRanges |
|---|
| 280 |
*/ |
|---|
| 281 |
public TextStyle [] getStyles () { |
|---|
| 282 |
checkWidget (); |
|---|
| 283 |
if (styles is null) return new TextStyle [0]; |
|---|
| 284 |
TextStyle [] result = new TextStyle [styles.length]; |
|---|
| 285 |
System.arraycopy (styles, 0, result, 0, styles.length); |
|---|
| 286 |
return result; |
|---|
| 287 |
} |
|---|
| 288 |
|
|---|
| 289 |
/** |
|---|
| 290 |
* Returns the composition text. |
|---|
| 291 |
* <p> |
|---|
| 292 |
* The text for an IME is the characters in the widget that |
|---|
| 293 |
* are in the current composition. When the commit count is |
|---|
| 294 |
* equal to the length of the composition text, then the |
|---|
| 295 |
* in-line edit operation is complete. |
|---|
| 296 |
* </p> |
|---|
| 297 |
* |
|---|
| 298 |
* @return the widget text |
|---|
| 299 |
* |
|---|
| 300 |
* @exception DWTException <ul> |
|---|
| 301 |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
|---|
| 302 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
|---|
| 303 |
* </ul> |
|---|
| 304 |
*/ |
|---|
| 305 |
public String getText () { |
|---|
| 306 |
checkWidget (); |
|---|
| 307 |
return text; |
|---|
| 308 |
} |
|---|
| 309 |
|
|---|
| 310 |
/** |
|---|
| 311 |
* Returns <code>true</code> if the caret should be wide, and |
|---|
| 312 |
* <code>false</code> otherwise. In some languages, for example |
|---|
| 313 |
* Korean, the caret is typically widened to the width of the |
|---|
| 314 |
* current character in the in-line edit session. |
|---|
| 315 |
* |
|---|
| 316 |
* @return the wide caret state |
|---|
| 317 |
* |
|---|
| 318 |
* @exception DWTException <ul> |
|---|
| 319 |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
|---|
| 320 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
|---|
| 321 |
* </ul> |
|---|
| 322 |
*/ |
|---|
| 323 |
public bool getWideCaret() { |
|---|
| 324 |
checkWidget (); |
|---|
| 325 |
auto layout = OS.GetKeyboardLayout (0); |
|---|
| 326 |
short langID = cast(short)OS.LOWORD ( cast(int) layout); |
|---|
| 327 |
return OS.PRIMARYLANGID (langID) is OS.LANG_KOREAN; |
|---|
| 328 |
} |
|---|
| 329 |
|
|---|
| 330 |
bool isInlineEnabled () { |
|---|
| 331 |
if (OS.IsWinCE || OS.WIN32_VERSION < OS.VERSION (5, 1)) return false; |
|---|
| 332 |
return OS.IsDBLocale && hooks (DWT.ImeComposition); |
|---|
| 333 |
} |
|---|
| 334 |
|
|---|
| 335 |
void releaseParent () { |
|---|
| 336 |
super.releaseParent (); |
|---|
| 337 |
if (this is parent.getIME ()) parent.setIME (null); |
|---|
| 338 |
} |
|---|
| 339 |
|
|---|
| 340 |
void releaseWidget () { |
|---|
| 341 |
super.releaseWidget (); |
|---|
| 342 |
parent = null; |
|---|
| 343 |
text = null; |
|---|
| 344 |
styles = null; |
|---|
| 345 |
ranges = null; |
|---|
| 346 |
} |
|---|
| 347 |
|
|---|
| 348 |
/** |
|---|
| 349 |
* Sets the offset of the composition from the start of the document. |
|---|
| 350 |
* This is the start offset of the composition within the document and |
|---|
| 351 |
* in not changed by the input method editor itself during the in-line edit |
|---|
| 352 |
* session but may need to be changed by clients of the IME. For example, |
|---|
| 353 |
* if during an in-line edit operation, a text editor inserts characters |
|---|
| 354 |
* above the IME, then the IME must be informed that the composition |
|---|
| 355 |
* offset has changed. |
|---|
| 356 |
* |
|---|
| 357 |
* @return the offset of the composition |
|---|
| 358 |
* |
|---|
| 359 |
* @exception DWTException <ul> |
|---|
| 360 |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
|---|
| 361 |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
|---|
| 362 |
* </ul> |
|---|
| 363 |
*/ |
|---|
| 364 |
public void setCompositionOffset (int offset) { |
|---|
| 365 |
checkWidget (); |
|---|
| 366 |
if (offset < 0) return; |
|---|
| 367 |
if (startOffset !is -1) { |
|---|
| 368 |
startOffset = offset; |
|---|
| 369 |
} |
|---|
| 370 |
} |
|---|
| 371 |
|
|---|
| 372 |
LRESULT WM_IME_COMPOSITION (int /*long*/ wParam, int /*long*/ lParam) { |
|---|
| 373 |
if (!isInlineEnabled ()) return null; |
|---|
| 374 |
ranges = null; |
|---|
| 375 |
styles = null; |
|---|
| 376 |
caretOffset = commitCount = 0; |
|---|
| 377 |
auto hwnd = parent.handle; |
|---|
| 378 |
auto hIMC = OS.ImmGetContext (hwnd); |
|---|
| 379 |
int codePage = parent.getCodePage (); |
|---|
| 380 |
if (hIMC !is null) { |
|---|
| 381 |
TCHAR[] buffer = null; |
|---|
| 382 |
if ((lParam & OS.GCS_RESULTSTR) !is 0) { |
|---|
| 383 |
int length_ = OS.ImmGetCompositionString (hIMC, OS.GCS_RESULTSTR, null, 0); |
|---|
| 384 |
if (length_ > 0) { |
|---|
| 385 |
buffer = NewTCHARs (codePage, length_ / TCHAR.sizeof); |
|---|
| 386 |
OS.ImmGetCompositionString (hIMC, OS.GCS_RESULTSTR, buffer.ptr, length_); |
|---|
| 387 |
if (startOffset is -1) { |
|---|
| 388 |
Event event = new Event (); |
|---|
| 389 |
event.detail = DWT.COMPOSITION_SELECTION; |
|---|
| 390 |
sendEvent (DWT.ImeComposition, event); |
|---|
| 391 |
startOffset = event.start; |
|---|
| 392 |
} |
|---|
| 393 |
Event event = new Event (); |
|---|
| 394 |
event.detail = DWT.COMPOSITION_CHANGED; |
|---|
| 395 |
event.start = startOffset; |
|---|
| 396 |
event.end = startOffset + text.length; |
|---|
| 397 |
event.text = text = buffer !is null ? TCHARsToStr(buffer) : ""; |
|---|
| 398 |
commitCount = text.length ; |
|---|
| 399 |
sendEvent (DWT.ImeComposition, event); |
|---|
| 400 |
String chars = text; |
|---|
| 401 |
text = ""; //$NON-NLS-1$ |
|---|
| 402 |
startOffset = -1; |
|---|
| 403 |
commitCount = 0; |
|---|
| 404 |
if (event.doit) { |
|---|
| 405 |
Display display = this.display; |
|---|
| 406 |
display.lastKey = 0; |
|---|
| 407 |
display.lastVirtual = display.lastNull = display.lastDead = false; |
|---|
| 408 |
length_ = chars.length; |
|---|
| 409 |
for (int i = 0; i < length_; i+=chars.getRelativeCodePointOffset(i,1)) { |
|---|
| 410 |
dchar c = chars[ i .. $ ].firstCodePoint(); |
|---|
| 411 |
display.lastAscii = c; |
|---|
| 412 |
event = new Event (); |
|---|
| 413 |
event.character = c; |
|---|
| 414 |
parent.sendEvent (DWT.KeyDown, event); |
|---|
| 415 |
} |
|---|
| 416 |
} |
|---|
| 417 |
} |
|---|
| 418 |
if ((lParam & OS.GCS_COMPSTR) is 0) return LRESULT.ONE; |
|---|
| 419 |
} |
|---|
| 420 |
buffer = null; |
|---|
| 421 |
if ((lParam & OS.GCS_COMPSTR) !is 0) { |
|---|
| 422 |
int length_ = OS.ImmGetCompositionString (hIMC, OS.GCS_COMPSTR, null, 0); |
|---|
| 423 |
if (length_ > 0) { |
|---|
| 424 |
buffer = NewTCHARs (codePage, length_ / TCHAR.sizeof); |
|---|
| 425 |
OS.ImmGetCompositionString (hIMC, OS.GCS_COMPSTR, buffer.ptr, length_); |
|---|
| 426 |
if ((lParam & OS.GCS_CURSORPOS) !is 0) { |
|---|
| 427 |
caretOffset = OS.ImmGetCompositionString (hIMC, OS.GCS_CURSORPOS, null, 0); |
|---|
| 428 |
} |
|---|
| 429 |
int [] clauses = null; |
|---|
| 430 |
if ((lParam & OS.GCS_COMPCLAUSE) !is 0) { |
|---|
| 431 |
length_ = OS.ImmGetCompositionStringW (hIMC, OS.GCS_COMPCLAUSE, /+cast(int [])+/null, 0); |
|---|
| 432 |
if (length_ > 0) { |
|---|
| 433 |
clauses = new int [length_ / 4]; |
|---|
| 434 |
OS.ImmGetCompositionStringW (hIMC, OS.GCS_COMPCLAUSE, clauses.ptr, length_); |
|---|
| 435 |
} |
|---|
| 436 |
} |
|---|
| 437 |
if ((lParam & OS.GCS_COMPATTR) !is 0 && clauses !is null) { |
|---|
| 438 |
length_ = OS.ImmGetCompositionStringA (hIMC, OS.GCS_COMPATTR, /+cast(byte [])+/null, 0); |
|---|
| 439 |
if (length_ > 0) { |
|---|
| 440 |
byte [] attrs = new byte [length_]; |
|---|
| 441 |
OS.ImmGetCompositionStringA (hIMC, OS.GCS_COMPATTR, attrs.ptr, length_); |
|---|
| 442 |
length_ = clauses.length - 1; |
|---|
| 443 |
ranges = new int [length_ * 2]; |
|---|
| 444 |
styles = new TextStyle [length_]; |
|---|
| 445 |
auto layout = OS.GetKeyboardLayout (0); |
|---|
| 446 |
short langID = cast(short)OS.LOWORD ( cast(int) layout); |
|---|
| 447 |
TF_DISPLAYATTRIBUTE* attr = null; |
|---|
| 448 |
TextStyle style = null; |
|---|
| 449 |
for (int i = 0; i < length_; i++) { |
|---|
| 450 |
ranges [i * 2] = clauses [i]; |
|---|
| 451 |
ranges [i * 2 + 1] = clauses [i + 1] - 1; |
|---|
| 452 |
styles [i] = style = new TextStyle (); |
|---|
| 453 |
attr = getDisplayAttribute (langID, attrs [clauses [i]]); |
|---|
| 454 |
if (attr !is null) { |
|---|
| 455 |
switch (attr.crText.type) { |
|---|
| 456 |
case OS.TF_CT_COLORREF: |
|---|
| 457 |
style.foreground = Color.win32_new (display, attr.crText.cr); |
|---|
| 458 |
break; |
|---|
| 459 |
case OS.TF_CT_SYSCOLOR: |
|---|
| 460 |
int colorRef = OS.GetSysColor (attr.crText.cr); |
|---|
| 461 |
style.foreground = Color.win32_new (display, colorRef); |
|---|
| 462 |
break; |
|---|
| 463 |
default: |
|---|
| 464 |
} |
|---|
| 465 |
switch (attr.crBk.type) { |
|---|
| 466 |
case OS.TF_CT_COLORREF: |
|---|
| 467 |
style.background = Color.win32_new (display, attr.crBk.cr); |
|---|
| 468 |
break; |
|---|
| 469 |
case OS.TF_CT_SYSCOLOR: |
|---|
| 470 |
int colorRef = OS.GetSysColor (attr.crBk.cr); |
|---|
| 471 |
style.background = Color.win32_new (display, colorRef); |
|---|
| 472 |
break; |
|---|
| 473 |
default: |
|---|
| 474 |
} |
|---|
| 475 |
switch (attr.crLine.type) { |
|---|
| 476 |
case OS.TF_CT_COLORREF: |
|---|
| 477 |
style.underlineColor = Color.win32_new (display, attr.crLine.cr); |
|---|
| 478 |
break; |
|---|
| 479 |
case OS.TF_CT_SYSCOLOR: |
|---|
| 480 |
int colorRef = OS.GetSysColor (attr.crLine.cr); |
|---|
| 481 |
style.underlineColor = Color.win32_new (display, colorRef); |
|---|
| 482 |
break; |
|---|
| 483 |
default: |
|---|
| 484 |
} |
|---|
| 485 |
style.underline = attr.lsStyle !is OS.TF_LS_NONE; |
|---|
| 486 |
switch (attr.lsStyle) { |
|---|
| 487 |
case OS.TF_LS_SQUIGGLE: |
|---|
| 488 |
style.underlineStyle = DWT.UNDERLINE_SQUIGGLE; |
|---|
| 489 |
break; |
|---|
| 490 |
case OS.TF_LS_DASH: |
|---|
| 491 |
style.underlineStyle = UNDERLINE_IME_DASH; |
|---|
| 492 |
break; |
|---|
| 493 |
case OS.TF_LS_DOT: |
|---|
| 494 |
style.underlineStyle = UNDERLINE_IME_DOT; |
|---|
| 495 |
break; |
|---|
| 496 |
case OS.TF_LS_SOLID: |
|---|
| 497 |
style.underlineStyle = attr.fBoldLine ? UNDERLINE_IME_THICK : DWT.UNDERLINE_SINGLE; |
|---|
| 498 |
break; |
|---|
| 499 |
default: |
|---|
| 500 |
} |
|---|
| 501 |
} |
|---|
| 502 |
delete attr; |
|---|
| 503 |
} |
|---|
| 504 |
} |
|---|
| 505 |
} |
|---|
| 506 |
} |
|---|
| 507 |
OS.ImmReleaseContext (hwnd, hIMC); |
|---|
| 508 |
} |
|---|
| 509 |
int end = startOffset + text.length; |
|---|
| 510 |
if (startOffset is -1) { |
|---|
| 511 |
Event event = new Event (); |
|---|
| 512 |
event.detail = DWT.COMPOSITION_SELECTION; |
|---|
| 513 |
sendEvent (DWT.ImeComposition, event); |
|---|
| 514 |
startOffset = event.start; |
|---|
| 515 |
end = event.end; |
|---|
| 516 |
} |
|---|
| 517 |
Event event = new Event (); |
|---|
| 518 |
event.detail = DWT.COMPOSITION_CHANGED; |
|---|
| 519 |
event.start = startOffset; |
|---|
| 520 |
event.end = end; |
|---|
| 521 |
event.text = text = buffer !is null ? TCHARsToStr(buffer) : ""; |
|---|
| 522 |
sendEvent (DWT.ImeComposition, event); |
|---|
| 523 |
if (text.length is 0) { |
|---|
| 524 |
startOffset = -1; |
|---|
| 525 |
ranges = null; |
|---|
| 526 |
styles = null; |
|---|
| 527 |
} |
|---|
| 528 |
} |
|---|
| 529 |
return LRESULT.ONE; |
|---|
| 530 |
} |
|---|
| 531 |
|
|---|
| 532 |
LRESULT WM_IME_COMPOSITION_START (int /*long*/ wParam, int /*long*/ lParam) { |
|---|
| 533 |
return isInlineEnabled () ? LRESULT.ONE : null; |
|---|
| 534 |
} |
|---|
| 535 |
|
|---|
| 536 |
LRESULT WM_IME_ENDCOMPOSITION (int /*long*/ wParam, int /*long*/ lParam) { |
|---|
| 537 |
return isInlineEnabled () ? LRESULT.ONE : null; |
|---|
| 538 |
} |
|---|
| 539 |
|
|---|
| 540 |
LRESULT WM_KILLFOCUS (int /*long*/ wParam, int /*long*/ lParam) { |
|---|
| 541 |
if (!isInlineEnabled ()) return null; |
|---|
| 542 |
auto hwnd = parent.handle; |
|---|
| 543 |
auto hIMC = OS.ImmGetContext (hwnd); |
|---|
| 544 |
if (hIMC !is null) { |
|---|
| 545 |
if (OS.ImmGetOpenStatus (hIMC)) { |
|---|
| 546 |
OS.ImmNotifyIME (hIMC, OS.NI_COMPOSITIONSTR, OS.CPS_COMPLETE, 0); |
|---|
| 547 |
} |
|---|
| 548 |
OS.ImmReleaseContext (hwnd, hIMC); |
|---|
| 549 |
} |
|---|
|